NodeJS通过流复制文件非常慢

10

我正在使用Node在VMware下复制文件到SSD,但性能非常低。我运行的基准测试以度量实际速度为:

$ hdparm -tT /dev/sda

/dev/sda:
 Timing cached reads:   12004 MB in  1.99 seconds = 6025.64 MB/sec
 Timing buffered disk reads: 1370 MB in  3.00 seconds = 456.29 MB/sec

然而,下面这段复制文件的Node代码非常慢,即使连续运行也无法加快速度:

var fs  = require("fs");
fs.createReadStream("bigfile").pipe(fs.createWriteStream("tempbigfile"));

然后运行如下:

$ seq 1 10000000 > bigfile
$ ll bigfile -h
-rw-rw-r-- 1 mustafa mustafa 848M Jun  3 03:30 bigfile
$ time node test.js 

real    0m4.973s
user    0m2.621s
sys     0m7.236s
$ time node test.js 

real    0m5.370s
user    0m2.496s
sys     0m7.190s
这里的问题是什么,如何加快速度?我相信通过调整缓冲区大小可以在C中更快地编写代码。让我困惑的是,当我编写简单的几乎等同于pv的程序,将stdin导入stdout时,速度非常快。
process.stdin.pipe(process.stdout);

并且运行结果为:

$ dd if=/dev/zero bs=8M count=128 | pv | dd of=/dev/null
128+0 records in 174MB/s] [        <=>                                                                                ]
128+0 records out
1073741824 bytes (1.1 GB) copied, 5.78077 s, 186 MB/s
   1GB 0:00:05 [ 177MB/s] [          <=>                                                                              ]
2097152+0 records in
2097152+0 records out
1073741824 bytes (1.1 GB) copied, 5.78131 s, 186 MB/s
$ dd if=/dev/zero bs=8M count=128 |  dd of=/dev/null
128+0 records in
128+0 records out
1073741824 bytes (1.1 GB) copied, 5.57005 s, 193 MB/s
2097152+0 records in
2097152+0 records out
1073741824 bytes (1.1 GB) copied, 5.5704 s, 193 MB/s
$ dd if=/dev/zero bs=8M count=128 | node test.js | dd of=/dev/null
128+0 records in
128+0 records out
1073741824 bytes (1.1 GB) copied, 4.61734 s, 233 MB/s
2097152+0 records in
2097152+0 records out
1073741824 bytes (1.1 GB) copied, 4.62766 s, 232 MB/s
$ dd if=/dev/zero bs=8M count=128 | node test.js | dd of=/dev/null
128+0 records in
128+0 records out
1073741824 bytes (1.1 GB) copied, 4.22107 s, 254 MB/s
2097152+0 records in
2097152+0 records out
1073741824 bytes (1.1 GB) copied, 4.23231 s, 254 MB/s
$ dd if=/dev/zero bs=8M count=128 | dd of=/dev/null
128+0 records in
128+0 records out
1073741824 bytes (1.1 GB) copied, 5.70124 s, 188 MB/s
2097152+0 records in
2097152+0 records out
1073741824 bytes (1.1 GB) copied, 5.70144 s, 188 MB/s
$ dd if=/dev/zero bs=8M count=128 | node test.js | dd of=/dev/null
128+0 records in
128+0 records out
1073741824 bytes (1.1 GB) copied, 4.51055 s, 238 MB/s
2097152+0 records in
2097152+0 records out
1073741824 bytes (1.1 GB) copied, 4.52087 s, 238 MB/s

不要使用Node复制文件,这会导致太多的开销。 - vkurchatkin
2
正如您所看到的,将stdout导入stdin没有任何开销,我怀疑这是文件缓冲区大小的问题。 - Mustafa
1个回答

31
I don't know the answer to your question, but perhaps this helps in your investigation of the problem.
在 Node.js 的流缓冲 documentation 中,它说: WritableReadable流都会将数据存储在内部缓冲区中,可以通过writable.writableBufferreadable.readableBuffer来检索缓冲数据。

可能缓冲的数据量取决于传递给流构造函数的highWaterMark选项。对于普通流,highWaterMark选项指定了总字节数。对于以对象模式操作的流,highWaterMark指定了总对象数...

stream API的一个关键目标,特别是stream.pipe()方法,是限制数据缓冲到可接受的水平,以便速度不同的源和目的地不会超过可用内存。

因此,您可以调整缓冲区大小以提高速度:

var fs = require('fs');
var path = require('path');
var from = path.normalize(process.argv[2]);
var to = path.normalize(process.argv[3]);

var readOpts = {highWaterMark: Math.pow(2,16)};  // 65536
var writeOpts = {highWaterMark: Math.pow(2,16)}; // 65536  

var source = fs.createReadStream(from, readOpts);
var destiny = fs.createWriteStream(to, writeOpts)

source.pipe(destiny);

11
谁曾经踩过这个,应该重新考虑一下。将它调整到那个大小几乎可以使其快两倍,更接近本地速度。谢谢! - Mustafa
你知道为什么在这里 https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options, fs.createWriteStream(path[, options])选项列表中没有列出highWaterMark吗?另外,如果可能的话,是否可以调整从API请求返回的可读流的highWaterMark - user1063287
我不确定为什么 highWaterMark 没有列在 fs.createWriteStream 上,但你可以将其作为选项传递,它会起作用。 - Nate Bosscher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接