简体   繁体   English

为什么Node.JS中的V8比我的原生C ++插件更快?

[英]Why V8 in Node.JS is faster than in my native C++ addon?

Why Google's V8 JavaScript engine in my C++ addon works significantly slower than in Node.JS? 为什么我的C ++插件中的Google V8 JavaScript引擎的工作速度明显慢于Node.JS?

I've tried to write some stupidly simple code for generating prime numbers in the JavaScript and ran it in V8 via my C++ addon and directly in Node.JS. 我试着编写一些简单的代码来生成JavaScript中的素数,并通过我的C ++插件在V8中直接在Node.JS中运行。

I was quite shocked, because both should be using the same JavaScript engine and both have executed the same code (time in milliseconds, less is better): 我非常震惊,因为两者都应该使用相同的JavaScript引擎,并且两者都执行相同的代码(时间以毫秒为单位,越少越好):

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

Here is the source of JavaScript module and source of C++ addon that runs same JavaScript code (and I think problem not in the interop, because measuring of time works directly in JS): 这是JavaScript模块的源代码和运行相同JavaScript代码的C ++插件的源代码(我认为问题不在interop中,因为测量时间直接在JS中工作):

index.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 : 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 : 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);

In the C++ version all variables declared in the script source ( result , primeNumberCounter , i , j , isPrime , start , end, time ) are global because script's top level scope is global scope. 在C ++版本中,脚本源( resultprimeNumberCounterijisPrimestart ,end, time )中声明的所有变量都是全局变量,因为脚本的顶级作用域是全局作用域。

For optimizing compiler it is easy to allocate local variables into machine registers (or spill slots on stack) and keep track of their type. 为了优化编译器,很容易将局部变量分配到机器寄存器(或堆栈中的溢出槽)并跟踪它们的类型。 Working with global variables on the other hand requires constant memory accesses and type checks because V8 does not (currently) perform a register promotion optimization. 另一方面,使用全局变量需要恒定的内存访问和类型检查,因为V8(当前)不执行寄存器升级优化。

If you wrap the source into an immediately called function difference should go away. 如果将源包装成一个立即调用的函数,则应该消失。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM