mirror of
https://github.com/restic/restic.git
synced 2025-12-04 01:11:56 +00:00
Write sparse files in restorer
This writes files by using (*os.File).Truncate, which resolves to the
truncate system call on Unix.
Compared to the naive loop,
for _, b := range p {
if b != 0 {
return false
}
}
the optimized allZero is about 10× faster:
name old time/op new time/op delta
AllZero-8 1.09ms ± 1% 0.09ms ± 1% -92.10% (p=0.000 n=10+10)
name old speed new speed delta
AllZero-8 3.84GB/s ± 1% 48.59GB/s ± 1% +1166.51% (p=0.000 n=10+10)
This commit is contained in:
committed by
Michael Eischer
parent
b48766d7b8
commit
5d4568d393
60
internal/restorer/sparsewrite.go
Normal file
60
internal/restorer/sparsewrite.go
Normal file
@@ -0,0 +1,60 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package restorer
|
||||
|
||||
import "bytes"
|
||||
|
||||
// WriteAt writes p to f.File at offset. It tries to do a sparse write
|
||||
// and updates f.size.
|
||||
func (f *partialFile) WriteAt(p []byte, offset int64) (n int, err error) {
|
||||
n = len(p)
|
||||
end := offset + int64(n)
|
||||
|
||||
// Skip the longest all-zero prefix of p.
|
||||
// If it's long enough, we can punch a hole in the file.
|
||||
skipped := zeroPrefixLen(p)
|
||||
p = p[skipped:]
|
||||
offset += int64(skipped)
|
||||
|
||||
switch {
|
||||
case len(p) == 0 && end > f.size:
|
||||
// We need to do a Truncate, as WriteAt with length-0 input
|
||||
// doesn't actually extend the file.
|
||||
err = f.Truncate(end)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
case len(p) == 0:
|
||||
// All zeros, file already big enough. A previous WriteAt or
|
||||
// Truncate will have produced the zeros in f.File.
|
||||
|
||||
default:
|
||||
n, err = f.File.WriteAt(p, offset)
|
||||
}
|
||||
|
||||
end = offset + int64(n)
|
||||
if end > f.size {
|
||||
f.size = end
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// zeroPrefixLen returns the length of the longest all-zero prefix of p.
|
||||
func zeroPrefixLen(p []byte) (n int) {
|
||||
// First skip 1kB-sized blocks, for speed.
|
||||
var zeros [1024]byte
|
||||
|
||||
for len(p) >= len(zeros) && bytes.Equal(p[:len(zeros)], zeros[:]) {
|
||||
p = p[len(zeros):]
|
||||
n += len(zeros)
|
||||
}
|
||||
|
||||
for len(p) > 0 && p[0] == 0 {
|
||||
p = p[1:]
|
||||
n++
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
Reference in New Issue
Block a user