I am somewhat new to Rust and I am trying to speed up the performance on my multithreaded downloader. The program takes in a URL, number of chunks and filepath.
What is the best way to write to file shared across threads?
I have tried to use Arc<Mutex<File>>
and locking the file before writing. Tried to optimize program with cargo build --release
. I have also tried to open the file in each thread and write to it without locking (somehow that was the best).
A similar program in Go is sharing a file across goroutines and writing to it there. I am surprised that Go beats Rust, since I expected Rust to have C/C++ performance.
The Rust function is used in 10 threads, sharing a single file. Not sure what the fastest way is to write to a single file shared across threads.
One of the methods for writing in Rust:
let read_size: usize = 64 * 1024
let mut buf_reader = BufReader::with_capacity(read_size, r.body_mut());
let mut f = open_file("test.mp4", true)?;
let mut buf_writer = BufWriter::with_capacity(read_size, &mut f);
let mut buf = [0; 64*1024]; // buffer read size in KiB
let mut n: usize; // declare var for how much was read
buf_writer.seek(SeekFrom::Start(range.start)).unwrap();
loop {
n = buf_reader.read(&mut buf)?;
if n < 1 {
break;
}
let n1 = buf_writer.write(&buf[..n]).unwrap();
written += n1 as u64;
}
Writing in Go:
readSize := 32 * 1024
buf := make([]byte, readSize)
for {
nr, err := res.Body.Read(buf)
if err != nil {
if err == io.EOF {
t.File.WriteAt(buf[:nr], start)
break
}
fmt.Println(err)
return
}
nw, err := t.File.WriteAt(buf[:nr], start)
handleErr(&err)
start += int64(nw)
}
EDIT: According to my research, Go uses syscall.Pwrite
which is one call to the system to write at an offset (does not require seeking first). In Rust, you must seek before writing, which is 2 system calls. "It's usually an universal truth that the less system calls program issues the more efficient it is." I hope that there is a way to write at an offset using Rust with one system call.