【问题标题】:Why V8 in Node.JS is faster than in my native C++ addon?为什么 Node.JS 中的 V8 比我的原生 C++ 插件更快?
【发布时间】:2013-07-15 23:49:12
【问题描述】:

为什么我的 C++ 插件中的 Google V8 JavaScript 引擎的运行速度比在 Node.JS 中慢很多?

我尝试编写一些愚蠢的简单代码以在 JavaScript 中生成素数,并通过我的 C++ 插件在 V8 中运行它,并直接在 Node.JS 中运行。

我很震惊,因为两者应该使用相同的 JavaScript 引擎并且都执行了相同的代码(时间以毫秒为单位,越少越好):

V8 in Node.JS:  495517
V8 in Node.JS C++ Addon:  623598

这里是运行相同 JavaScript 代码的 JavaScript 模块和 C++ 插件的源代码(我认为问题不在互操作中,因为时间测量直接在 JS 中工作):

index.js

var jsInNodeJsPrimeGeneratorBenchmark = require("./javascript.js");
var jsInNativePrimeGeneratorBenchmark = require("./native");

console.log("V8 in Node.JS: ", jsInNodeJsPrimeGeneratorBenchmark.primeGeneratorBenchmark());
console.log("V8 in Node.JS C++ Addon: ", jsInNativePrimeGeneratorBenchmark.primeGeneratorBenchmark());

javascript.js

function primeGeneratorBenchmark() {
    var result, primeNumberCounter, i, j, isPrime, start, end;

    i = 3;
    primeNumberCounter = 1;

    start = Date.now();

    while (primeNumberCounter < 100000) {
        isPrime = true;
        for (j = 2; j < i; j++) {
            if (i % j === 0) {
                isPrime = false;
                break;
            }
        }

        if (isPrime) {
            result = i;
            primeNumberCounter++;
        }

        i++;
    }

    end = Date.now();

    return end - start;
}

exports.primeGeneratorBenchmark = primeGeneratorBenchmark;

native.cpp

#include <node.h>

v8::Handle<v8::Value> primeGeneratorBenchmark(const v8::Arguments &arguments);
void registerModule(v8::Handle<v8::Object> target);

v8::Handle<v8::Value> primeGeneratorBenchmark(const v8::Arguments &arguments) {
    v8::HandleScope handleScope;

    v8::Local<v8::Context> context = arguments.Holder()->CreationContext();

    v8::Context::Scope scope(context);

    const char *sourceStringC =
        "var result, primeNumberCounter, i, j, isPrime, start, end, time;\n"
        "i = 3;\n"
        "primeNumberCounter = 1;\n"
        "start = Date.now();\n"
        "while (primeNumberCounter < 100000) {\n"
        "    isPrime = true;\n"
        "    for (j = 2; j < i; j++) {\n"
        "        if (i % j === 0) {\n"
        "            isPrime = false;\n"
        "            break;\n"
        "        }\n"
        "    }\n"
        "    if (isPrime) {\n"
        "        result = i;\n"
        "        primeNumberCounter++;\n"
        "    }\n"
        "    i++;\n"
        "}\n"
        "end = Date.now();\n"
        "time = end - start;\n";

    v8::Local<v8::String> sourceStringV8 = v8::String::New(sourceStringC);

    v8::Local<v8::Script> script = v8::Script::Compile(sourceStringV8);
    script->Run();

    v8::Local<v8::Value> timeResult = v8::Context::GetCurrent()->Global()->Get(v8::String::New("time"));

    return handleScope.Close(timeResult);
}

void registerModule(v8::Handle<v8::Object> target) {
    target->Set(v8::String::NewSymbol("primeGeneratorBenchmark"), v8::FunctionTemplate::New(primeGeneratorBenchmark)->GetFunction());
}

NODE_MODULE(native, registerModule);

【问题讨论】:

  • 只是一个有趣的想法:如果你在 C++ 中实现相同的算法而不是让 V8 在 JS 中解析并执行它,结果会怎样?
  • 这看起来只是在使用 JS 编译器,还没有原生执行任何东西,对吧?

标签: javascript c++ performance node.js v8


【解决方案1】:

在 C++ 版本中,脚本源中声明的所有变量(resultprimeNumberCounterijisPrimestart、end、time)都是全局变量 因为脚本的顶级作用域是全局作用域。

为了优化编译器,很容易将局部变量分配到机器寄存器(或堆栈上的溢出槽)并跟踪它们的类型。另一方面,使用全局变量需要持续的内存访问和类型检查,因为 V8(当前)不执行寄存器提升优化。

如果您将源代码包装到立即调用的函数中,差异应该会消失。

【讨论】:

  • 谢谢你,维亚切斯拉夫,你节省了我的时间!我将所有内容都包装在匿名函数中,该函数会立即调用自身,有时 C++ 插件运行得更快。我只是将变量 time 留在全局范围内,以便在不重写 C++ 代码的情况下获得结果。
猜你喜欢
  • 2013-09-23
  • 2015-02-10
  • 2011-05-12
  • 2012-06-14
  • 1970-01-01
  • 2018-08-11
  • 1970-01-01
  • 1970-01-01
  • 2013-07-22
相关资源
最近更新 更多