我想在大型阵列上执行复杂的CPU密集型工作。理想情况下,我希望将其传递给子进程。
var spawn = require('child_process').spawn;
// dataAsNumbers is a large 2D array
var child = spawn(process.execPath, ['/child_process_scripts/getStatistics', dataAsNumbers]);
child.stdout.on('data', function(data){
console.log('from child: ', data.toString());
});
但是当我这样做时,node给出了错误:
衍生E2BIG
我偶然发现了this article
因此,通过管道将数据传送子进程似乎是可行的。我的代码现在是:
var spawn = require('child_process').spawn;
console.log('creating child........................');
var options = { stdio: [null, null, null, 'pipe'] };
var args = [ '/getStatistics' ];
var child = spawn(process.execPath, args, options);
var pipe = child.stdio[3];
pipe.write(Buffer('awesome'));
child.stdout.on('data', function(data){
console.log('from child: ', data.toString());
});
然后在getStatistics.js中:
console.log('im inside child');
process.stdin.on('data', function(data) {
console.log('data is ', data);
process.exit(0);
});
但是,process.stdin.on
中的回调并未到达。如何在我的子脚本中接收流?
编辑
我不得不放弃缓冲方法。现在我将数组作为一条消息发送:
var cp = require('child_process');
var child = cp.fork('/getStatistics.js');
child.send({
dataAsNumbers: dataAsNumbers
});
但这仅在dataAsNumbers长度小于20,000时有效,否则它会超时。
发布于 2017-05-21 06:01:16
有了如此大量的数据,我会考虑使用,而不是将数据复制到子进程中(这就是使用管道或传递消息时发生的情况)。这将节省内存,为父进程占用更少的CPU时间,并且不太可能遇到某些限制。
shm-typed-array
是一个非常简单的模块,似乎适合您的应用程序。示例:
parent.js
"use strict";
const shm = require('shm-typed-array');
const fork = require('child_process').fork;
// Create shared memory
const SIZE = 20000000;
const data = shm.create(SIZE, 'Float64Array');
// Fill with dummy data
Array.prototype.fill.call(data, 1);
// Spawn child, set up communication, and give shared memory
const child = fork("child.js");
child.on('message', sum => {
console.log(`Got answer: ${sum}`);
// Demo only; ideally you'd re-use the same child
child.kill();
});
child.send(data.key);
child.js
"use strict";
const shm = require('shm-typed-array');
process.on('message', key => {
// Get access to shared memory
const data = shm.get(key, 'Float64Array');
// Perform processing
const sum = Array.prototype.reduce.call(data, (a, b) => a + b, 0);
// Return processed data
process.send(sum);
});
请注意,我们只通过IPC从父进程向子进程发送一个小的“键”,而不是整个数据。因此,我们节省了大量的内存和时间。
当然,您可以将'Float64Array'
(例如double
)更改为您的应用程序需要的任何typed array。请注意,这个库只处理一维类型的数组;但这应该只是一个小障碍。
发布于 2017-05-21 03:25:12
我也能够重现你所经历的延迟,但也许没有你那么糟糕。我使用了以下代码
// main.js
const fork = require('child_process').fork
const child = fork('./getStats.js')
const dataAsNumbers = Array(100000).fill(0).map(() =>
Array(100).fill(0).map(() => Math.round(Math.random() * 100)))
child.send({
dataAsNumbers: dataAsNumbers,
})
和
// getStats.js
process.on('message', function (data) {
console.log('data is ', data)
process.exit(0)
})
节点main.js 2.72s用户0.45s系统103%cpu总计3.045
我正在生成由100个数字组成的100k元素来模拟您的数据,请确保您使用的是process
上的message
事件。但可能您的子项更复杂,可能是失败的原因,这也取决于您在查询中设置的超时。
如果你想得到更好的结果,你可以做的是将你的数据分成多个块,这些块将被发送给子进程,并重新构建以形成初始数组。
还有一种可能是使用第三方库或协议,即使这需要更多的工作。您可以查看一下messenger.js,或者甚至是类似于AMQP队列的东西,这些队列允许您使用池在两个进程之间进行通信,并且子进程已确认消息的保证。它有几个节点实现,比如amqp.node,但它仍然需要一些设置和配置工作。
发布于 2017-05-22 10:02:48
为什么要生成子流程?跨子进程发送数据在cpu和实时方面的成本可能比在同一进程内进行处理所节省的成本更高。
相反,我建议您考虑在与nodejs主进程相同的内存中运行的工作线程中进行统计计算,以获得超高效率的编码。
您可以使用NAN编写可以发布到工作线程的C++代码,然后让该工作线程在完成后将结果和事件发送回您的nodejs事件循环。
这样做的好处是您不需要额外的时间将数据发送到不同的进程,但缺点是您将为线程操作编写一些C++代码,但NAN扩展应该会为您处理大部分困难的任务。
https://stackoverflow.com/questions/44052913
复制相似问题