Quantcast
Channel: CNode:Node.js专业中文社区
Viewing all articles
Browse latest Browse all 14821

介绍一次 c/python/node的性能对比

$
0
0

这篇文章找了一个具体的算法,比较C++/node/python 的性能,并对结果给出解释。最后部分介绍了使用 C/C++ 技术提升node 性能的一个技术框架。

过去有一道经典的面试题,内容如下:一个台阶总共有N级,如果一次可以跳3级,也可以跳5级,求总共有多少种跳法。假设现在算 N = 123 时,计算总共多少种跳法。下面用代码给出具体实现

C++ 实现

#include "stdafx.h"
/*
  Name: 
  Copyright: 
  Author: 
  Date: 
  Description: 跳台阶问题(递归)
题目:一个台阶总共有n级,如果一次可以跳3级,也可以跳5级,求总共有多少跳法
*/
using namespace std;
// 总共台阶数目
int n_;
// 工作数组
int *x_ = NULL;
// 记录总体数目
int v_ = 0;
void cfun(int t){
  int sum = 0;
  for(int i = 1; i < t; ++i)
    sum += x_[i];
  if(sum >= n_){
    if(sum == n_){
      ++v_;
    }else
      return;
  }else{
    for(int i = 1;i <= 2; ++i){
      if(1 == i)
        x_[t] = 3;
      else
        x_[t] = 5;
      cfun(t+1);
    }
  }
}

int _tmain(int argc, _TCHAR* argv[]) {
  cout<<"N=";
  cin>>n_;
  DWORD bef = timeGetTime();
  x_ = new int[n_+1];
  cfun(1);
  printf("花费时间 %d\n", timeGetTime() - bef);
  cout<<v_<<"种"<<endl;
  //del x_;
  system("pause");
  return 0;
}

Js 实现,可优化

'use strict';
var N = 123;
var x_ = new Array(N + 1);
var v_ = 0;

function cfun(t){
  var sum = 0;
  var i;
  for(i = 1; i < t; ++i){
    sum += x_[i];
  }
  if(sum >= N){
    if(sum == N){
      ++v_;
  }else
    return;
  }else{
    for(i = 1; i <= 2; ++i){
      if(1 == i)
        x_[t] = 3;
      else
        x_[t] = 5;
      cfun(t + 1);
    }
  }
}

var bec = new Date().getTime();
cfun(1);
console.log('花费时间(毫秒) ' + ((new Date()).getTime() - bec));
console.log(v_ + '种');

Js 实现,不可优化

'use strict';
var N = 123;
var x_ = new Array(N + 1);
var v_ = 0;

function cfun(t){
  let sum = 0;
  var i;
  for(i = 1; i < t; ++i){
    sum += x_[i];
  }
  if(sum >= N){
    if(sum == N){
      ++v_;
  }else
    return;
  }else{
    for(i = 1; i <= 2; ++i){
      if(1 == i)
        x_[t] = 3;
      else
        x_[t] = 5;
      cfun(t + 1);
    }
  }
}

var bec = new Date().getTime();
cfun(1);
console.log('花费时间(毫秒) ' + ((new Date()).getTime() - bec));
console.log(v_ + '种');

Python 实现

# encoding: utf-8
from random import  *
from  time  import  *

bef = int(time()*1000)
N = 123
x_ = [0 for i in range(N + 1)]
v_ = 0

def cfun(t):
  global x_
  global v_
  sum = 0
  i = 1
  while i < t:
    sum = sum + x_[i]
    i += 1
  if sum >= N:
    if sum == N:
      v_ += 1
    else:
      return
  else:
    i = 1
    while i <= 2:
      if i == 1:
        x_[t] = 3
      else:
        x_[t] = 5
      cfun(t + 1)
      i += 1
  return

cfun(1)
print(u'花费时间 ' + str(int(time()*1000) - bef))
print(v_)

N取 123,测试结果是 测试结果上面的这个图真实的记载了 N=123 时的运行时间,毫无疑问,C++ 代码最快,效率上秒杀脚本语言。Js 代码在开启优化和优化失败的时候,性能相差10倍。开启优化后,执行速度大概是 C++ 的五分之一。测试中,python 使用的是 2.7,最慢。

这次对比中,Python 的性能最差,这是在没有使用任何优化技术的情况下得出的。Python有一个超集 Cython ,可以将py源码编译成C 库,它支持确定类型,这种语法的扩展经过编译之后,其实就是静态语言编译为相应平台机器码的效率,性能已与C相当。

最有趣的是Js代码部分,使用如下命令 node --trace-opt --trace-deopt --prof分别运行这两个文件,会发现一个可以优化,一个无法优化,信息如下

 node --trace-opt --trace-deopt --prof jump.js
[marking 000002CBBBB6C4B9 <JS Function cfun (SharedFunctionInfo 0000029FB77F59C9)> for optimized recompi
lation, reason: hot and stable, ICs with typeinfo: 14/14 (100%), generic ICs: 0/14 (0%)]
[compiling method 000002CBBBB6C4B9 <JS Function cfun (SharedFunctionInfo 0000029FB77F59C9)> using Cranks
haft]
[optimizing 000002CBBBB6C4B9 <JS Function cfun (SharedFunctionInfo 0000029FB77F59C9)> - took 0.165, 0.28
1, 0.138 ms]
[completed optimizing 000002CBBBB6C4B9 <JS Function cfun (SharedFunctionInfo 0000029FB77F59C9)>]
 node --trace-opt --trace-deopt --prof jump-no-opt.js
[marking 00000116B516C5E1 <JS Function cfun (SharedFunctionInfo 0000018DB6E759E9)> for optimized recompilation, reason: hot and stabl
e, ICs with typeinfo: 14/14 (100%), generic ICs: 0/14 (0%)]
[compiling method 00000116B516C5E1 <JS Function cfun (SharedFunctionInfo 0000018DB6E759E9)> using Crankshaft]
[disabled optimization for 0000018DB6E759E9 <SharedFunctionInfo cfun>, reason: Unsupported let compound assignment]

V8 使用一个叫 Crankshaft 的编译优化器处理代码,两份文件差别仅在于一处 let 关键字的使用。正是这一处let,导致后面的那个源码无法被优化。

从原理上看,typescript 是 js 的一个超集,但与cython是python超集不同,typescript 是把代码编译为js 给 V8 执行,而不是翻译成c编译成机器码。typescript 支持静态定义类型,因此理论上使用静态类型的typescript模块也可以翻译成 c 然后调用 c 编译器编译成.node。但是这样做必要性没有python 强,因为V8优化之后,代码执行速度已经比较接近 c++了。所以这里需要注意的是,不要因为某些关键字的使用,让自己的代码无法被优化。

node 适合I/O密集型的场景,我们所有的代码运行在main线程,因此它特别不适合计算量稍大,或者处理大块数据,尤其伴随Buffer和String 的转换的场景,频繁的GC会极大的拖慢系统,计算型任务也会阻塞主线程,使得CPU飚的很高,但任务吞吐量上不去。

c++ 代码操作内存,拷贝大块数据不存在GC的过程;多线程利用多核,可以解决主线程被计算型函数的阻塞的风险。因此可以考虑给 node 做一个多线程扩展,在 js 代码里创建并管理线程,将复杂任务委托给线程执行。因为操作系统存在线程执行的亲和性,node 主线程占用一个核心,可以考虑把其他核作为一个 IO 资源来用。node-threadobject是这样一个模块。

本文系原创,转载请注明出处~


Viewing all articles
Browse latest Browse all 14821

Trending Articles