为什么V8在Node.JS中比我的本地c++插件快

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

本文关键字:c++ 插件 我的 V8 Node JS 为什么      更新时间:2023-10-16

为什么谷歌的V8 JavaScript引擎在我的c++插件工作明显慢于在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++版本中,脚本源中声明的所有变量(result, primeNumberCounter, i, j, isPrime, start, end, time)都是global,因为脚本的顶级作用域是全局作用域。

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

如果将源代码包装成一个立即调用的函数,那么差异就会消失。