mirror of
https://github.com/rclone/rclone.git
synced 2025-12-11 22:14:05 +01:00
fs: allow setting a write buffer for multithread
when multi-thread downloading is enabled, rclone used to send a write to disk after every read, resulting in a lot of small writes to different locations of the file. depending on the underlying filesystem or device, it can be more efficient to send bigger writes.
This commit is contained in:
committed by
Nick Craig-Wood
parent
5f938fb9ed
commit
fcb912a664
@@ -1,6 +1,7 @@
|
||||
package operations
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -12,11 +13,32 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
multithreadChunkSize = 64 << 10
|
||||
multithreadChunkSizeMask = multithreadChunkSize - 1
|
||||
multithreadBufferSize = 32 * 1024
|
||||
multithreadChunkSize = 64 << 10
|
||||
multithreadChunkSizeMask = multithreadChunkSize - 1
|
||||
multithreadReadBufferSize = 32 * 1024
|
||||
)
|
||||
|
||||
// An offsetWriter maps writes at offset base to offset base+off in the underlying writer.
|
||||
//
|
||||
// Modified from the go source code. Can be replaced with
|
||||
// io.OffsetWriter when we no longer need to support go1.19
|
||||
type offsetWriter struct {
|
||||
w io.WriterAt
|
||||
off int64 // the current offset
|
||||
}
|
||||
|
||||
// newOffsetWriter returns an offsetWriter that writes to w
|
||||
// starting at offset off.
|
||||
func newOffsetWriter(w io.WriterAt, off int64) *offsetWriter {
|
||||
return &offsetWriter{w, off}
|
||||
}
|
||||
|
||||
func (o *offsetWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = o.w.WriteAt(p, o.off)
|
||||
o.off += int64(n)
|
||||
return
|
||||
}
|
||||
|
||||
// Return a boolean as to whether we should use multi thread copy for
|
||||
// this transfer
|
||||
func doMultiThreadCopy(ctx context.Context, f fs.Fs, src fs.Object) bool {
|
||||
@@ -62,6 +84,7 @@ type multiThreadCopyState struct {
|
||||
|
||||
// Copy a single stream into place
|
||||
func (mc *multiThreadCopyState) copyStream(ctx context.Context, stream int) (err error) {
|
||||
ci := fs.GetConfig(ctx)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
fs.Debugf(mc.src, "multi-thread copy: stream %d/%d failed: %v", stream+1, mc.streams, err)
|
||||
@@ -84,8 +107,13 @@ func (mc *multiThreadCopyState) copyStream(ctx context.Context, stream int) (err
|
||||
}
|
||||
defer fs.CheckClose(rc, &err)
|
||||
|
||||
var writer io.Writer = newOffsetWriter(mc.wc, start)
|
||||
if ci.MultiThreadWriteBufferSize > 0 {
|
||||
writer = bufio.NewWriterSize(writer, int(ci.MultiThreadWriteBufferSize))
|
||||
fs.Debugf(mc.src, "multi-thread copy: write buffer set to %v", ci.MultiThreadWriteBufferSize)
|
||||
}
|
||||
// Copy the data
|
||||
buf := make([]byte, multithreadBufferSize)
|
||||
buf := make([]byte, multithreadReadBufferSize)
|
||||
offset := start
|
||||
for {
|
||||
// Check if context cancelled and exit if so
|
||||
@@ -98,7 +126,7 @@ func (mc *multiThreadCopyState) copyStream(ctx context.Context, stream int) (err
|
||||
if err != nil {
|
||||
return fmt.Errorf("multipart copy: accounting failed: %w", err)
|
||||
}
|
||||
nw, ew := mc.wc.WriteAt(buf[0:nr], offset)
|
||||
nw, ew := writer.Write(buf[0:nr])
|
||||
if nw > 0 {
|
||||
offset += int64(nw)
|
||||
}
|
||||
@@ -113,6 +141,16 @@ func (mc *multiThreadCopyState) copyStream(ctx context.Context, stream int) (err
|
||||
if er != io.EOF {
|
||||
return fmt.Errorf("multipart copy: read failed: %w", er)
|
||||
}
|
||||
|
||||
// if we were buffering, flush do disk
|
||||
switch w := writer.(type) {
|
||||
case *bufio.Writer:
|
||||
er2 := w.Flush()
|
||||
if er2 != nil {
|
||||
return fmt.Errorf("multipart copy: flush failed: %w", er2)
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user