Node.js - 我发现使用多线程或多进程比单个进程更慢。为什么?

5

我有一个需要占用大量CPU的任务(循环遍历一些数据并评估结果)。我想利用多个核心来完成这项任务,但我的表现始终比仅使用单个核心要差。

我尝试过:

  • 使用express在不同端口上创建多个进程,并将任务发送到这些进程中
  • 使用webworker-threads在线程池中运行不同线程中的任务

我通过计算完成的迭代总数并除以解决问题所花费的时间来衡量结果。当使用单个核心时,我的结果明显更好。

一些值得注意的点:

  • 我可以通过任务管理器识别出我是在使用一个核心还是多个核心。我正在使用预期数量的核心。
  • 我有很多RAM
  • 我已经尝试过只在2或3个核心上运行
  • 我添加了nextTicks,但在这种情况下似乎没有影响
  • 每个任务需要几秒钟,因此我不认为我会因开销而损失太多

对于线程的更新:我怀疑webworker-threads中存在一个错误。暂时跳过express,我认为问题可能与我的线程循环有关。我正在做的是创建线程,然后尝试不断运行它们,但在它们之间发送数据。即使两个线程都在使用CPU,也只有线程0返回值。我的假设是emit any通常会将消息发射到空闲时间最长的线程,但事实并非如此。我的设置看起来像这样

在threadtask.js中

thread.on('init', function() {

    thread.emit('ready');

    thread.on('start', function(data) {
        console.log("THREAD " + thread.id + ": execute task");
        //...
        console.log("THREAD " + thread.id + ": emit result");
        thread.emit('result', otherData));
    });
});

main.js

var tp = Threads.createPool(NUM_THREADS);
tp.load(threadtaskjsFilePath);
var readyCount = 0;
tp.on('ready', function() {
    readyCount++;

    if(readyCount == tp.totalThreads()) {
        console.log('MAIN: Sending first start event');
        tp.all.emit('start', JSON.stringify(data));
    }
});

tp.on('result', function(eresult) {
    var result = JSON.parse(eresult);
    console.log('MAIN: result from thread ' + result.threadId);
    //...
    console.log('MAIN: emit start' + result.threadId);
    tp.any.emit('start' + result.threadId, data);   
});

tp.all.emit("init", JSON.stringify(data2));

这场灾难的输出
MAIN: Sending first start event
THREAD 0: execute task
THREAD 1: execute task
THREAD 1: emit result
MAIN: result from thread 1
THREAD 0: emit result
THREAD 0: execute task
THREAD 0: emit result
MAIN: result from thread 0
MAIN: result from thread 0
THREAD 0: execute task
THREAD 0: emit result
THREAD 0: execute task
THREAD 0: emit result
MAIN: result from thread 0
MAIN: result from thread 0
THREAD 0: execute task
THREAD 0: emit result
THREAD 0: execute task
THREAD 0: emit result
MAIN: result from thread 0
MAIN: result from thread 0

我曾尝试另一种方法:发出所有的信息,然后让每个线程监听只有它能回答的信息。例如,线程.on('start' + thread.id, function() { ... })。 但是这种方法行不通,因为当我执行tp.all.emit('start' + result.threadId, ...)时,信息并没有被接收。

MAIN: Sending first start event
THREAD 0: execute task
THREAD 1: execute task
THREAD 1: emit result
THREAD 0: emit result

之后没有发生更多的事情。

针对多个express服务器的更新:我得到了改进,但不如预期的大

我重新审视了这个解决方案,并且有了更好的运气。我认为我的原始测量可能存在缺陷。新结果:

  • 单进程:每秒3.3个迭代
  • 主进程+2个服务器:每秒4.2个迭代
  • 主进程+3个服务器:每秒4.9个迭代

我发现有一件事有点奇怪,那就是我没有看到每秒约6个迭代的2个服务器和每秒9个迭代的3个服务器。我知道网络会有一些损失,但如果我将任务时间增加到足够高的话,网络损失应该很小,我想。


1
你确定你是 CPU 瓶颈而不是 I/O 瓶颈吗? - Frederick Cheung
相当确定。你知道我如何确认它不是io绑定的吗?线程初始化后,我没有从磁盘加载任何内容。我正在将一些json数组来回发送到线程,但它们并不是很大。 - i8abug
注意磁盘/网络使用情况,看看是否达到了最大值。 - Frederick Cheung
@i8abug 你确定那些工作线程里没有从磁盘或网络读取数据吗? - GuardianX
你如何使用 express 创建多个进程?通常情况下,你可以在同一个进程中的不同端口上创建多个服务器。 - Bergi
显示剩余4条评论
1个回答

1
您不应该将Node.js进程推到运行多个线程以提高性能。在四核处理器上,有1个express进程处理一般请求和3个express进程处理CPU密集型请求可能是最有效的设置,因此建议您尝试设计您的express进程避免使用Web工作者并简单地阻塞,直到它们产生结果。这将使您降至单个进程和单个线程运行,按设计最有可能产生最佳结果。
我不知道Web工作者包如何处理同步,如何影响发生在c空间中的Node.js I/O线程池等细节,但我认为您通常会想引入Web工作者以能够同时管理更多的阻塞任务而不严重影响其他不需要线程和系统I/O或可以迅速响应的请求。这并不一定意味着应用此方法会产生改进的性能,特定任务的执行情况可能不同。如果您运行4个执行I/O的进程和4个线程,您可能会将自己锁定在在应用程序空间之外持续切换线程上浪费时间。

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