JavaScript 引擎性能比较之一SpiderMonkey

来源:互联网 发布:唱歌挣钱的软件 编辑:程序博客网 时间:2024/05/24 02:33

JavaScript 是最常用的前端语言, 在后端也渐渐有所应用, 比如 NodeJS, 在C++应用中嵌入JavaScript 到底性能如何?

就拿最流行的 Mozilla SpiderMonkey 和 Google V8 做一个比较测试, 先以 SpiderMonkey 为例, 来执行一个一万个字串的数据排序和反转


1. 下载

https://people.mozilla.org/~sstangl/mozjs-31.2.0.rc0.tar.bz2
bunzip2 mozjs-31.2.0.rc0.tar.bz2
tar xvf mozjs-31.2.0.rc0.tar

2. 构建
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Build_Documentation

2.1 下载 autoconf2.13
http://ftp.gnu.org/gnu/autoconf/autoconf-2.13.tar.gz
tar xvfz autoconf-2.13.tar.gz

./configure --prefix=/usr --program-suffix=-2.13
make acdatadir=/usr/share/autoconf-2.13
make acdatadir=/usr/share/autoconf-2.13 install

2.2 编译步骤
cd js/src
autoconf-2.13

# This name should end with "_OPT.OBJ" to make the version control system ignore it.
mkdir build_OPT.OBJ
cd build_OPT.OBJ
../configure
# Use "mozmake" on Windows
make
make install

------------
../../dist/bin/nsinstall -t js-config /usr/local/bin
../../dist/bin/nsinstall -t libjs_static.a /usr/local/lib
mv -f /usr/local/lib/libjs_static.a /usr/local/lib/libmozjs-31.a
../../dist/bin/nsinstall -t libmozjs-31.dylib /usr/local/lib
/Applications/Xcode.app/Contents/Developer/usr/bin/make -C shell install
../../../dist/bin/nsinstall -t js /usr/local/bin
------------

2.3 编译验证

# ./dist/bin/js
js> print("hello world");
hello world
js> quit();

3. 参考资料
*https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_User_Guide
*https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/How_to_embed_the_JavaScript_engine

4. 测试程序

#include "jsapi.h"#include <stdio.h>#include <stdlib.h>#include <sys/time.h>#include <time.h>#include <unistd.h>#define TRACE_MINARGS 2#define TIME_FMT_LEN 50using namespace JS;// The class of the global object.static JSClass globalClass = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub,        JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,        JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, nullptr,        nullptr, nullptr, JS_GlobalObjectTraceHook };long long current_timestamp(char arrTimeStr[TIME_FMT_LEN]) {    struct timeval tv;    struct tm* ptm;    char time_string[40];    gettimeofday(&tv, NULL); // get current time    if (arrTimeStr) {        ptm = localtime(&tv.tv_sec);        /* Format the date and time, down to a single second.  */        strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", ptm);        /* Compute milliseconds from microseconds.  */        //snprintf(char * restrict str, size_t size, const char * restrict format,        snprintf(arrTimeStr, TIME_FMT_LEN, "%s.%06d", time_string, tv.tv_usec);    }    long long total_us = tv.tv_sec * 1000000LL + tv.tv_usec ; // caculate milliseconds    // printf("milliseconds: %lld\n", milliseconds);    return total_us;}// [SpiderMonkey 24] Use JSBool instead of bool.static bool debug_trace(JSContext *cx, unsigned argc, jsval *vp) {    JS::CallArgs args = CallArgsFromVp(argc, vp);        if (args.length() > 0) {    char szTimeStr[TIME_FMT_LEN] = { '\0' };    current_timestamp(szTimeStr);        JSString *str = args[0].toString();        printf("[%s] %s\n", szTimeStr, JS_EncodeString(cx, str));    }    return true;}//typedef void (* JSErrorReporter)(JSContext *cx, const char *message, JSErrorReport *report);// The error reporter callback.void report_error(JSContext *cx, const char *message, JSErrorReport *report) {    fprintf(stderr, "%s:%u:%s\n",            report->filename ? report->filename : "[no filename]",            (unsigned int) report->lineno, message);}int load_file_malloc(const char* szFile, char*& pBuffer,        long* pBufSize = NULL) {    FILE * pFile = NULL;    long lSize = 0;    size_t result = 0;    pFile = fopen(szFile, "r");    if (pFile == NULL) {        fputs("File open error", stderr);        return 1;    }    // obtain file size:    fseek(pFile, 0, SEEK_END);    lSize = ftell(pFile);    rewind(pFile);    // allocate memory to contain the whole file:    pBuffer = (char*) malloc(sizeof(char) * lSize);    if (pBuffer == NULL) {        fputs("Memory allocate error", stderr);        fclose(pFile);        return 2;    }    // copy the file into the buffer:    result = fread(pBuffer, 1, lSize, pFile);    if (result != lSize) {        fputs("Reading file error", stderr);        fclose(pFile);        return 3;    }    if (pBufSize)        *pBufSize = lSize;    fclose(pFile);    return 0;}int test(JSContext *cx, RootedObject* pGlobal, const char* pScript) {    //RootedObject global = *pGlobal;    JS::RootedValue rval(cx);    JSAutoCompartment ac(cx, *pGlobal);    JS_InitStandardClasses(cx, *pGlobal);    const char *filename = "noname";    int lineno = 1;    bool ok = JS_DefineFunction(cx, *pGlobal, "debug_trace", debug_trace,            TRACE_MINARGS, 0);    if (!ok)        return 1;    ok = JS_EvaluateScript(cx, *pGlobal, pScript, strlen(pScript), filename,            lineno, &rval);    if (!ok)        return 2;    //JSString *str = rval.toString();    //printf("%s\n", JS_EncodeString(cx, str));    return 0;}int run(JSContext *cx, const char* pScript) {    // Enter a request before running anything in the context.    JSAutoRequest ar(cx);    // Create the global object and a new compartment.    RootedObject global(cx);    global = JS_NewGlobalObject(cx, &globalClass, nullptr,            JS::DontFireOnNewGlobalHook);    if (!global)        return 1;    // Enter the new global object's compartment.    JSAutoCompartment ac(cx, global);    // Populate the global object with the standard globals, like Object and    // Array.    if (!JS_InitStandardClasses(cx, global))        return 1;    // Your application code here. This may include JSAPI calls to create your    // own custom JS objects and run scripts.    long long begin_time = current_timestamp(NULL);    test(cx, &global, pScript);    long long end_time = current_timestamp(NULL);    printf("calling costs %lld microseconds\n", end_time - begin_time);    return 0;}int main(int argc, const char *argv[]) {    // Initialize the JS engine.    if (!JS_Init())        return 1;    // Create a JS runtime.    JSRuntime *rt = JS_NewRuntime(8L * 1024L * 1024L, JS_USE_HELPER_THREADS);    if (!rt)        return 1;    // Create a context.    JSContext *cx = JS_NewContext(rt, 8192);    if (!cx)        return 1;    //JS_SetErrorReporter(JSContext *cx, JSErrorReporter er);    JS_SetErrorReporter(cx, report_error);    int status = 0;    if (argc > 1) {        char* buffer = NULL;        int ret = load_file_malloc(argv[1], buffer);        if (ret != 0) {            return ret;        }        status = run(cx, buffer);        // free        if (buffer)            free(buffer);    } else {        const char *script = "'hello'+'world, it is '+new Date()";        status = run(cx, script);    }    // Shut everything down.    JS_DestroyContext(cx);    JS_DestroyRuntime(rt);    JS_ShutDown();    return status;}


测试JavaScript 脚本

function random_str(){    var text = "";    var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";    for( var i=0; i < 8; i++ )        text += possible.charAt(Math.floor(Math.random() * possible.length));    return text;}var array = new Array();for ( var i = 0; i < 10000; i++) {array[array.length] = random_str();}debug_trace("begin sort and reverse array which length=" + array.length );array.sort();array.reverse();debug_trace("done, first element=" + array[0]+ ", " + "last element=" + array[array.length-1] );



测试结果

$ ./test/SpiderMonkeyTest ./test/arraysort.js

[2015-05-07 21:07:29.762895] begin sort and reverse array which length=10000
[2015-05-07 21:07:29.766270] done, first element=zzjG0Pnh, last element=0000LZbe

calling costs 52492 microseconds


性能还可以, 总执行时间花费52 毫秒, JS数组排序反转大约用了4 毫秒多, 下一步看看 Google V8 的表现如何

0 0
原创粉丝点击