内容简介:大家都知道 Node.js 性能很高,是以异步事件驱动、非阻塞 I/O 而被广泛使用。但缺点也很明显,由于 Node.js 是单线程程序,如果长时间运算,会导致 CPU 不能及时释放,所以并不适合 CPU 密集型应用。当然,也不是没有办法解决这个问题。虽然 Node.js 不支持多线程,但是可创建多子进程来执行任务。 Node.js 提供了下面我们就分别使用单线程和多进程来模拟查找大量斐波那契数进行 CPU 密集测试
大家都知道 Node.js 性能很高,是以异步事件驱动、非阻塞 I/O 而被广泛使用。但缺点也很明显,由于 Node.js 是单线程程序,如果长时间运算,会导致 CPU 不能及时释放,所以并不适合 CPU 密集型应用。
当然,也不是没有办法解决这个问题。虽然 Node.js 不支持多线程,但是可创建多子进程来执行任务。
Node.js 提供了 child_process
和 cluster
两个模块可用于创建多子进程
下面我们就分别使用单线程和多进程来模拟查找大量斐波那契数进行 CPU 密集测试
以下代码是查找 500 次位置为 35 的斐波那契数(方便测试,定了一个时间不需要太长也不会太短的位置)
单线程处理
代码: single.js
function fibonacci(n) { if (n == 0 || n == 1) { return n; } else { return fibonacci(n - 1) + fibonacci(n - 2); } } let startTime = Date.now(); let totalCount = 500; let completedCount = 0; let n = 35; for (let i = 0; i < totalCount; i++) { fibonacci(n); completedCount++; console.log(`process: ${completedCount}/${totalCount}`); } console.log(":clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap:"); console.info(`任务完成,用时: ${Date.now() - startTime}ms`); console.log(":clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap:"); 复制代码
执行 node single.js
查看结果
在我的电脑上显示结果为 44611ms
(电脑配置不同也会有差异)。
... process: 500/500 :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: 任务完成,用时: 44611ms :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: 复制代码
查找 500 次需要 44 秒,太慢了。可想而知如果位置更大,数量更多...
那我们来尝试用多进程试试 :arrow_down:
多进程
采用 cluster
模块, Master-Worker
模式来测试
共 3 个 js,分别为主线程代码: master.js
、子进程代码: worker.js
、入口代码: cluster.js
(入口可无需单独写一个 js、这里是为了看起来更清楚一些)
主线程代码: master.js
const cluster = require("cluster"); const numCPUs = require("os").cpus().length; // 设置子进程执行程序 cluster.setupMaster({ exec: "./worker.js", slient: true }); function run() { // 记录开始时间 const startTime = Date.now(); // 总数 const totalCount = 500; // 当前已处理任务数 let completedCount = 0; // 任务生成器 const fbGenerator = FbGenerator(totalCount); if (cluster.isMaster) { cluster.on("fork", function(worker) { console.log(`[master] : fork worker ${worker.id}`); }); cluster.on("exit", function(worker, code, signal) { console.log(`[master] : worker ${worker.id} died`); }); for (let i = 0; i < numCPUs; i++) { const worker = cluster.fork(); // 接收子进程数据 worker.on("message", function(msg) { // 完成一个,记录并打印进度 completedCount++; console.log(`process: ${completedCount}/${totalCount}`); nextTask(this); }); nextTask(worker); } } else { process.on("message", function(msg) { console.log(msg); }); } /** * 继续下一个任务 * * @param {ChildProcess} worker 子进程对象,将在此进程上执行本次任务 */ function nextTask(worker) { // 获取下一个参数 const data = fbGenerator.next(); // 判断是否已经完成,如果完成则调用完成函数,结束程序 if (data.done) { done(); return; } // 否则继续任务 // 向子进程发送数据 worker.send(data.value); } /** * 完成,当所有任务完成时调用该函数以结束程序 */ function done() { if (completedCount >= totalCount) { cluster.disconnect(); console.log(":clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap:"); console.info(`任务完成,用时: ${Date.now() - startTime}ms`); console.log(":clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap:"); } } } /** * 生成器 */ function* FbGenerator(count) { var n = 35; for (var i = 0; i < count; i++) { yield n; } return; } module.exports = { run }; 复制代码
1.这里是根据当前电脑的逻辑 CPU 核数来创建子进程的,不同电脑数量也会不一样,我的 CPU 是 6 个物理核数,由于支持超线程处理,所以逻辑核数是 12,故会创建出 12 个子进程
2.主线程与子进程之间通信是通过 send
方法来发送数据,监听 message
事件来接收数据
3.不知道大家有没有注意到我这里使用了 ES6 的 Generator 生成器来模拟生成每次需要查找的斐波那契数位置(虽然是写死的 :joy:,为了和上面的单线程保证统一)。这么做是为了不让所有任务一次性扔出去,因为就算扔出去也会被阻塞,还不如放在程序端就给控制住,完成一个,放一个。
子进程代码: worker.js
function fibonacci(n) { if (n == 0 || n == 1) { return n; } else { return fibonacci(n - 1) + fibonacci(n - 2); } } // 接收主线程发送过来的任务,并开始查找斐波那契数 process.on("message", n => { var res = fibonacci(n); // 查找结束后通知主线程,以便主线程再度进行任务分配 process.send(res); }); 复制代码
入口代码: cluster.js
// 引入主线程js,并执行暴露出来的run方法 const master = require("./master"); master.run(); 复制代码
执行 node cluster.js
查看结果
在我的电脑上显示结果为 10724ms
(电脑配置不同也会有差异)。
process: 500/500 :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: 任务完成,用时: 10724ms :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: :clap: 复制代码
结果
进过上面两种方式的对比,结果很明显,多进程处理速度是单线程处理速度的 4 倍多。而且有条件的情况下,如果电脑 CPU 足够,进程数更多,那么速度也会更快。
如果有更好的方案或别的语言能处理你的需求那就更好,谁让 Node.js 天生就不适合 CPU 密集型应用呢。。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Linux 后台运行任务 nohup 结合 & 用法以及如何精准查找进程并 kill 后台任务实践
- Windows 10出现新0day,任务计划进程可用于攻击
- Windows10 SDK 17763中进程外后台任务编译失败的问题
- 进程:进程生命周期
- Python 知识巩固:通过主进程带起多个子进程实现多进程执行逻辑
- Python 中子进程与父进程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构、算法与应用(原书第2版)
Sartaj Sahni / 王立柱、刘志红 / 机械工业出版社 / 2015-4 / 79.00元
《数据结构、算法与应用——C++语言描述》是享有盛誉的数据结构教科书的第2版。它完整地包含了基本数据结构的内容,是CS2课程的理想用书。作者Sartaj Sahni通过循循善诱的讲解、直观具体的讨论和基于现实的应用,让读者轻松、愉快地学习。新版书着重利用标准模板库(STL),把书中开发的数据结构和算法与相应的STL实现方法相互关联。本书还增加了很多新的实例和练习题。 书中的应用实例是它的特色......一起来看看 《数据结构、算法与应用(原书第2版)》 这本书的介绍吧!