【问题标题】:NodeJS Copying File over a stream is very slowNodeJS通过流复制文件非常慢
【发布时间】:2014-07-23 05:13:57
【问题描述】:

我在 VMWare 下的 SSD 上使用 Node 复制文件,但性能非常低。我为测量实际速度而运行的基准如下:

$ 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

但是,以下复制文件的节点代码非常慢,即使后续运行也不会使其更快:

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.js performance file-io stream pipe


【解决方案1】:

我不知道你的问题的答案,但也许这有助于你调查问题。

在关于流缓冲的 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);

【讨论】:

  • 谁不赞成这个应该再想一想,将它调整到那个大小可以使它快近 2 倍,这更接近原生速度。谢谢!
  • 你知道为什么highWaterMark 不是为fs.createWriteStream(path[, options]) 列出的选项吗:nodejs.org/api/fs.html#fs_fs_createwritestream_path_options,以及是否可以调整正在返回的可读流的highWaterMark来自 api 请求?
  • 我不知道为什么highWaterMark 没有在fs.createWriteStream 中列出,但您可以将其作为选项传递,它会起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-05
  • 2012-07-07
  • 2016-11-18
  • 2020-08-14
  • 2012-01-18
  • 1970-01-01
  • 2021-08-28
相关资源
最近更新 更多