v0.12 悠长的开发周期 (已经过去九个月了,并且还在继续,是有史以来最长的一次) 让核心团队和贡献者们有充分的机会对性能做一些优化。本文会介绍其中最值得注意的几个。
支持塞住模式的可写流
现在可写流可以支持“塞住(corked)”模式,类似于你执行 man tcp
时见到的 socket 选项TCP_CORK
和TCP_NOPUSH
。
当被塞住时,写到流中的数据会排队直到流被重新开塞 (uncorked)。这样 Node.js 可以将比较小的写操作合并成比较大的,从而减少系统调用和 TCP 往返。
http 模块已经升级了,在发送分块的请求或响应主体时透明地使用塞住模式。如果你经常看看 strace(1) 输出,就会发现 writev(2) 系统调用更多了,而 write(2) 变少了。
提升 TLS 性能
tls 模块在 Node.js v0.12 中做了相当大的重构。
在 Node.js v0.10 中,tls 模块在 net 模块之上,作为对网络流量透明地加密和解密的传输流。从工程角度来看,这样分层是有必要的,但会导致开销 - 更多的内存转移以及在 V8 VM 中超出绝对必要的调入调出–并妨碍优化。
所以在 node.js v0.12 中,直接用 libuv 重写了 tls 模块。现在它直接把接入的网络流量拉出来加密,无需通过中间层。
尽管这种评测不太科学,但用空密码时表明现在 TLS 一般要快 10%,并且用的内存更少。(我得说减少的内存占用可能部分是因为重构的内存管理,这是 v0.12 的另一项优化。)
(还有,如果你想知道,空密码时不会对负载加密的密码;它们可以用来测量架构和协议的开销。)
对于最终用户来说,tls 模块的大部分变化都是透明的。其中最显眼的一个是现在 TLS 连接是从 tls.TLSSocket
,而不是 tls.CryptoStream
中得到的。
提升 Crypto 性能
几种加解密算法现在应该更快了,有时是快 _ 很多 _。先介绍下背景:
Node.js 中的加解密使用 OpenSSL 库实现的。OpenSSL 中的算法有用 C 编写,并针对各种平台和架构的手动程序集版本。
Node.js v0.10 已经给一些东西用上了程序集版本,而 v0.12 又大范围扩充了这一范围。此外,现在在有 CPU 支持时用上了 AES-NI ,过去三四年生产的大多数 x86 处理器都支持。
在 Linux 上,如果执行grep ^flags /proc/cpuinfo | grep -w aes
找到任何匹配结果,那就说明你的系统支持 AES-NI。不过像 VMWare 或 VirtualBox 之类的虚拟机管理程序可能会对客户机操作系统隐藏 CPU 的真正能力,包括 AES-NI。
启用 AES-NI 有个有趣的结果,即像 AES128-GCM-SHA256 这种工业强度的加解密现在比 NULL-MD5 这样的非加密密码还要快很多!
减少垃圾收集的应激反应
多情景重构的一个副作用是极大减少了Node.js 核心中的持久句柄的数量。
持久句柄是对V8 堆上对象的强引用,防止对象在引用再次移除之前被垃圾收集器回收(按GC 的说法,它是一个人造的GC 根。)
Node.js 用持久句柄缓存经常使用的值,比如 strings 或 object 原型。然而持久句柄在垃圾收集器中需要一个特殊的后处理步骤,并且根据句柄数量有一个线性增长的开销。
因为多情景清理的作用,大部分持久句柄或者被消掉,或者切换成一个更轻量的机制(被称为’永恒句柄’;这个名字在暗示什么呢?)
最终效果是你的程序在垃圾收集器内部所用时间更少了,把更多的时间用在了实际的工作上。现在在node –prof
的输出中v8::internal::GlobalHandles::PostGarbageCollectionProcessing()
出现的次数应该少了很多。
更好的集群性能
Node.js v0.10 的 cluster 模块要靠操作系统给工人进程均匀地分发接入连接。
事实证明,在 Solaris 和 Linux 上,有些工作负载在工人进程之间分散地非常不均衡。为了缓解这种状况,Node.js v0.12 已经改了,默认使用轮转法。这篇文章中有更详细的介绍。
更快的计时器, 更快的 setImmediate()
, 更快的process.nextTick()
setTimeout()
和它的朋友们现在用的时间源不仅更快,而且对时钟偏移免疫。这一优化在所有平台上都启用了,但在 Linux 上我们更进一步,直接从 VDSO 上读取当前时间,因此极大降低了 gettimeofday(2)
和 clock_gettime(2)
系统调用的次数。
setImmediate()
和process.nextTick()
也给通常情况下的派发添加了快速的路径,可以见到性能上的调整。也就是说虽然这些函数已经相当快了,但它们现在更快了。
作者简介
本文最初由 Ben Noordhuis 发表在 StrongLoop 上。Ben Noordhuis 从 2010 年就跟着 Ryan Dahl 开发 Node.js 的核心代码。他一直在为改进 Node 核心代码而努力做着编码、调试和基准测试等工作。作为最高产的 Node 核心开发者之一,Ben 编写了 Node.js 和 libuv 中的很多代码。 StrongLoop 降低了在 Node 中开发APIs 的难度,还添加了监测、集群化以及私有注册的支持等DevOps 能力。
查看英文原文: What’s New in Node.js v0.12 – Performance Optimizations
2014 年 1 月 21 日
评论