]> git.kaiwu.me - quickjs.git/commitdiff
Improve microbench.js
authorCharlie Gordon <github@chqrlie.org>
Sun, 25 Feb 2024 23:14:31 +0000 (00:14 +0100)
committerCharlie Gordon <github@chqrlie.org>
Sun, 25 Feb 2024 23:14:31 +0000 (00:14 +0100)
- ensure handler behavior does not depend on n argument
- load and save reference file in node.js
- add -s filename option to name the output reference file
- add targets in Makefile for tests and bencharks using node.js
- fix incorrect timings when not using high resolution timer
- use performance timer in node.js
- output performance factor instead of percentage
- use smaller threshold by default
- add benchmarks for:
    date_parse(), prop_update(), prop_clone(), array_slice()
    global_func_call(),

Makefile
tests/microbench.js

index 17a0b8e996c347b75dba8af67bdcf099a065de20..0270a6ad832531d2ff004da6f5b45c004b53337c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -525,10 +525,25 @@ testall-32: all test-32 microbench-32 test2o-32 test2-32
 
 testall-complete: testall testall-32
 
+node-test:
+       node tests/test_closure.js
+       node tests/test_language.js
+       node tests/test_builtin.js
+       node tests/test_loop.js
+       node tests/test_bignum.js
+
+node-microbench:
+       node tests/microbench.js -s microbench-node.txt
+       node --jitless tests/microbench.js -s microbench-node-jitless.txt
+
 bench-v8: qjs
        make -C tests/bench-v8
        ./qjs -d tests/bench-v8/combined.js
 
+node-bench-v8:
+       make -C tests/bench-v8
+       node --jitless tests/bench-v8/combined.js
+
 tests/bjson.so: $(OBJDIR)/tests/bjson.pic.o
        $(CC) $(LDFLAGS) -shared -o $@ $^ $(LIBS)
 
index 6bf0b836d859b5d2f2de415e889af16efafb5d13..b70e3fb5560f7bf2bbb0e19773937ff72b2a0a4e 100644 (file)
  * THE SOFTWARE.
  */
 
+if (typeof require !== 'undefined') {
+    var fs = require('fs');
+}
+
 function pad(str, n) {
     str += "";
     while (str.length < n)
@@ -63,9 +67,9 @@ function toPrec(n, prec) {
 var ref_data;
 var log_data;
 
-var heads  = [ "TEST", "N", "TIME (ns)", "REF (ns)", "SCORE (%)" ];
+var heads  = [ "TEST", "N", "TIME (ns)", "REF (ns)", "SCORE (1000)" ];
 var widths = [    22,   10,          9,     9,       9 ];
-var precs  = [     0,   0,           2,     2,       2 ];
+var precs  = [     0,   0,           2,     2,       0 ];
 var total  = [     0,   0,           0,     0,       0 ];
 var total_score = 0;
 var total_scale = 0;
@@ -89,13 +93,27 @@ function log_line() {
 }
 
 var clocks_per_sec = 1000;
-var max_iterations = 10;
-var clock_threshold = 100;  /* favoring short measuring spans */
+var max_iterations = 100;
+var clock_threshold = 2;  /* favoring short measuring spans */
 var min_n_argument = 1;
-var get_clock = Date.now;
-if (typeof(os) !== "undefined") {
+var get_clock;
+if (typeof performance !== "undefined") {
+    // use more precise clock on NodeJS
+    // need a method call on performance object
+    get_clock = () => performance.now();
+} else
+if (typeof os !== "undefined") {
     // use more precise clock on QuickJS
     get_clock = os.now;
+} else {
+    // use Date.now and round up to the next millisecond
+    get_clock = () => {
+        var t0 = Date.now();
+        var t;
+        while ((t = Date.now()) == t0)
+            continue;
+        return t;
+    }
 }
 
 function log_one(text, n, ti) {
@@ -109,7 +127,7 @@ function log_one(text, n, ti) {
     ti = Math.round(ti * 100) / 100;
     log_data[text] = ti;
     if (typeof ref === "number") {
-        log_line(text, n, ti, ref, ti * 100 / ref);
+        log_line(text, n, ti, ref, Math.round(ref * 1000 / ti));
         total_score += ti * 100 / ref;
         total_scale += 100;
     } else {
@@ -121,28 +139,27 @@ function log_one(text, n, ti) {
 
 function bench(f, text)
 {
-    var i, j, n, t, t1, ti, nb_its, ref, ti_n, ti_n1, min_ti;
+    var i, j, n, t, ti, nb_its, ref, ti_n, ti_n1;
 
     nb_its = n = 1;
     if (f.bench) {
         ti_n = f(text);
     } else {
+        // measure ti_n: the shortest time for an individual operation
         ti_n = 1000000000;
-        min_ti = clock_threshold / 10;
         for(i = 0; i < 30; i++) {
+            // measure ti: the shortest time for max_iterations iterations
             ti = 1000000000;
             for (j = 0; j < max_iterations; j++) {
                 t = get_clock();
-                while ((t1 = get_clock()) == t)
-                    continue;
                 nb_its = f(n);
+                t = get_clock() - t;
                 if (nb_its < 0)
                     return; // test failure
-                t1 = get_clock() - t1;
-                if (ti > t1)
-                    ti = t1;
+                if (ti > t)
+                    ti = t;
             }
-            if (ti >= min_ti) {
+            if (ti >= clock_threshold / 10) {
                 ti_n1 = ti / nb_its;
                 if (ti_n > ti_n1)
                     ti_n = ti_n1;
@@ -196,6 +213,32 @@ function date_now(n) {
     return n;
 }
 
+function date_parse(n) {
+    var x0 = 0, dx = 0;
+    var j;
+    for(j = 0; j < n; j++) {
+        var x1 = x0 - x0 % 1000;
+        var x2 = -x0;
+        var x3 = -x1;
+        var d0 = new Date(x0);
+        var d1 = new Date(x1);
+        var d2 = new Date(x2);
+        var d3 = new Date(x3);
+        if (Date.parse(d0.toISOString()) != x0
+        ||  Date.parse(d1.toGMTString()) != x1
+        ||  Date.parse(d1.toString()) != x1
+        ||  Date.parse(d2.toISOString()) != x2
+        ||  Date.parse(d3.toGMTString()) != x3
+        ||  Date.parse(d3.toString()) != x3) {
+            console.log("Date.parse error for " + x0);
+            return -1;
+        }
+        dx = (dx * 1.1 + 1) >> 0;
+        x0 = (x0 + dx) % 8.64e15;
+    }
+    return n * 6;
+}
+
 function prop_read(n)
 {
     var obj, sum, j;
@@ -224,30 +267,78 @@ function prop_write(n)
     return n * 4;
 }
 
-function prop_create(n)
+function prop_update(n)
 {
     var obj, j;
+    obj = {a: 1, b: 2, c:3, d:4 };
+    for(j = 0; j < n; j++) {
+        obj.a += j;
+        obj.b += j;
+        obj.c += j;
+        obj.d += j;
+    }
+    return n * 4;
+}
+
+function prop_create(n)
+{
+    var obj, i, j;
     for(j = 0; j < n; j++) {
-        obj = new Object();
+        obj = {};
         obj.a = 1;
         obj.b = 2;
         obj.c = 3;
         obj.d = 4;
+        obj.e = 5;
+        obj.f = 6;
+        obj.g = 7;
+        obj.h = 8;
+        obj.i = 9;
+        obj.j = 10;
+        for(i = 0; i < 10; i++) {
+            obj[i] = i;
+        }
     }
-    return n * 4;
+    return n * 20;
 }
 
-function prop_delete(n)
+function prop_clone(n)
 {
-    var obj, j;
-    obj = {};
-    for(j = 0; j < n; j++) {
-        obj[j] = 1;
+    var ref, obj, j, k;
+    ref = { a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10 };
+    for(k = 0; k < 10; k++) {
+        ref[k] = k;
     }
-    for(j = 0; j < n; j++) {
-        delete obj[j];
+    for (j = 0; j < n; j++) {
+        global_res = { ...ref };
     }
-    return n;
+    return n * 20;
+}
+
+function prop_delete(n)
+{
+    var ref, obj, j, k;
+    ref = { a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10 };
+    for(k = 0; k < 10; k++) {
+        ref[k] = k;
+    }
+    for (j = 0; j < n; j++) {
+        obj = { ...ref };
+        delete obj.a;
+        delete obj.b;
+        delete obj.c;
+        delete obj.d;
+        delete obj.e;
+        delete obj.f;
+        delete obj.g;
+        delete obj.h;
+        delete obj.i;
+        delete obj.j;
+        for(k = 0; k < 10; k++) {
+            delete obj[k];
+        }
+    }
+    return n * 20;
 }
 
 function array_read(n)
@@ -308,15 +399,32 @@ function array_prop_create(n)
     return len * n;
 }
 
+function array_slice(n)
+{
+    var ref, a, i, j, len;
+    len = 1000;
+    ref = [];
+    for(i = 0; i < len; i++)
+        ref[i] = i;
+    for(j = 0; j < n; j++) {
+        ref[0] = j;
+        a = ref.slice();
+        a[0] = 0;
+        global_res = a;
+    }
+    return len * n;
+}
+
 function array_length_decr(n)
 {
-    var tab, i, j, len;
+    var tab, ref, i, j, len;
     len = 1000;
-    tab = [];
+    ref = [];
     for(i = 0; i < len; i++)
-        tab[i] = i;
+        ref[i] = i;
     for(j = 0; j < n; j++) {
-        for(i = len - 1; i >= 0; i--)
+        tab = ref.slice();
+        for(i = len; i --> 0;)
             tab.length = i;
     }
     return len * n;
@@ -324,15 +432,16 @@ function array_length_decr(n)
 
 function array_hole_length_decr(n)
 {
-    var tab, i, j, len;
+    var tab, ref, i, j, len;
     len = 1000;
-    tab = [];
+    ref = [];
     for(i = 0; i < len; i++) {
-        if (i != 3)
-            tab[i] = i;
+        if (i % 10 == 9)
+            ref[i] = i;
     }
     for(j = 0; j < n; j++) {
-        for(i = len - 1; i >= 0; i--)
+        tab = ref.slice();
+        for(i = len; i --> 0;)
             tab.length = i;
     }
     return len * n;
@@ -352,12 +461,13 @@ function array_push(n)
 
 function array_pop(n)
 {
-    var tab, i, j, len, sum;
+    var tab, ref, i, j, len, sum;
     len = 500;
+    ref = [];
+    for(i = 0; i < len; i++)
+        ref[i] = i;
     for(j = 0; j < n; j++) {
-        tab = [];
-        for(i = 0; i < len; i++)
-            tab[i] = i;
+        tab = ref.slice();
         sum = 0;
         for(i = 0; i < len; i++)
             sum += tab.pop();
@@ -429,6 +539,7 @@ function global_read(n)
     return n * 4;
 }
 
+// non strict version
 var global_write =
     (1, eval)(`(function global_write(n)
            {
@@ -471,6 +582,7 @@ function local_destruct(n)
 var global_v1, global_v2, global_v3, global_v4;
 var global_a, global_b, global_c, global_d;
 
+// non strict version
 var global_destruct =
     (1, eval)(`(function global_destruct(n)
            {
@@ -498,6 +610,25 @@ function global_destruct_strict(n)
     return n * 8;
 }
 
+function g(a)
+{
+    return 1;
+}
+
+function global_func_call(n)
+{
+    var j, sum;
+    sum = 0;
+    for(j = 0; j < n; j++) {
+        sum += g(j);
+        sum += g(j);
+        sum += g(j);
+        sum += g(j);
+    }
+    global_res = sum;
+    return n * 4;
+}
+
 function func_call(n)
 {
     function f(a)
@@ -517,7 +648,7 @@ function func_call(n)
     return n * 4;
 }
 
-function closure_var(n)
+function func_closure_call(n)
 {
     function f(a)
     {
@@ -622,8 +753,8 @@ function bigint256_arith(n)
 function set_collection_add(n)
 {
     var s, i, j, len = 100;
-    s = new Set();
     for(j = 0; j < n; j++) {
+        s = new Set();
         for(i = 0; i < len; i++) {
             s.add(String(i), i);
         }
@@ -637,25 +768,25 @@ function set_collection_add(n)
 
 function array_for(n)
 {
-    var r, i, j, sum;
+    var r, i, j, sum, len = 100;
     r = [];
-    for(i = 0; i < 100; i++)
+    for(i = 0; i < len; i++)
         r[i] = i;
     for(j = 0; j < n; j++) {
         sum = 0;
-        for(i = 0; i < 100; i++) {
+        for(i = 0; i < len; i++) {
             sum += r[i];
         }
         global_res = sum;
     }
-    return n * 100;
+    return n * len;
 }
 
 function array_for_in(n)
 {
-    var r, i, j, sum;
+    var r, i, j, sum, len = 100;
     r = [];
-    for(i = 0; i < 100; i++)
+    for(i = 0; i < len; i++)
         r[i] = i;
     for(j = 0; j < n; j++) {
         sum = 0;
@@ -664,14 +795,14 @@ function array_for_in(n)
         }
         global_res = sum;
     }
-    return n * 100;
+    return n * len;
 }
 
 function array_for_of(n)
 {
-    var r, i, j, sum;
+    var r, i, j, sum, len = 100;
     r = [];
-    for(i = 0; i < 100; i++)
+    for(i = 0; i < len; i++)
         r[i] = i;
     for(j = 0; j < n; j++) {
         sum = 0;
@@ -680,7 +811,7 @@ function array_for_of(n)
         }
         global_res = sum;
     }
-    return n * 100;
+    return n * len;
 }
 
 function math_min(n)
@@ -700,11 +831,11 @@ function regexp_ascii(n)
     var i, j, r, s;
     s = "the quick brown fox jumped over the lazy dog"
     for(j = 0; j < n; j++) {
-        for(i = 0; i < 10000; i++)
+        for(i = 0; i < 1000; i++)
             r = /the quick brown fox/.exec(s)
         global_res = r;
     }
-    return n * 10000;
+    return n * 1000;
 }
 
 function regexp_utf16(n)
@@ -712,91 +843,91 @@ function regexp_utf16(n)
     var i, j, r, s;
     s = "the quick brown ᶠᵒˣ jumped over the lazy ᵈᵒᵍ"
     for(j = 0; j < n; j++) {
-        for(i = 0; i < 10000; i++)
+        for(i = 0; i < 1000; i++)
             r = /the quick brown ᶠᵒˣ/.exec(s)
         global_res = r;
     }
-    return n * 10000;
+    return n * 1000;
 }
 
 /* incremental string contruction as local var */
 function string_build1(n)
 {
     var i, j, r;
-    r = "";
     for(j = 0; j < n; j++) {
-        for(i = 0; i < 100; i++)
+        r = "";
+        for(i = 0; i < 1000; i++)
             r += "x";
         global_res = r;
     }
-    return n * 100;
+    return n * 1000;
 }
 
 /* incremental string contruction using + */
 function string_build1x(n)
 {
     var i, j, r;
-    r = "";
     for(j = 0; j < n; j++) {
-        for(i = 0; i < 100; i++)
+        r = "";
+        for(i = 0; i < 1000; i++)
             r = r + "x";
         global_res = r;
     }
-    return n * 100;
+    return n * 1000;
 }
 
 /* incremental string contruction using +2c */
 function string_build2c(n)
 {
     var i, j;
-    var r = "";
     for(j = 0; j < n; j++) {
-        for(i = 0; i < 100; i++)
+        var r = "";
+        for(i = 0; i < 1000; i++)
             r += "xy";
         global_res = r;
     }
-    return n * 100;
+    return n * 1000;
 }
 
 /* incremental string contruction as arg */
 function string_build2(n, r)
 {
     var i, j;
-    r = "";
     for(j = 0; j < n; j++) {
-        for(i = 0; i < 100; i++)
+        r = "";
+        for(i = 0; i < 1000; i++)
             r += "x";
         global_res = r;
     }
-    return n * 100;
+    return n * 1000;
 }
 
 /* incremental string contruction by prepending */
 function string_build3(n)
 {
     var i, j, r;
-    r = "";
     for(j = 0; j < n; j++) {
-        for(i = 0; i < 100; i++)
+        r = "";
+        for(i = 0; i < 1000; i++)
             r = "x" + r;
         global_res = r;
     }
-    return n * 100;
+    return n * 1000;
 }
 
 /* incremental string contruction with multiple reference */
 function string_build4(n)
 {
     var i, j, r, s;
-    r = "";
     for(j = 0; j < n; j++) {
-        for(i = 0; i < 100; i++) {
+        r = "";
+        for(i = 0; i < 1000; i++) {
             s = r;
             r += "x";
         }
         global_res = r;
     }
-    return n * 100;
+    return n * 1000;
 }
 
 /* sort bench */
@@ -940,21 +1071,22 @@ sort_bench.verbose = false;
 
 function int_to_string(n)
 {
-    var s, r, j;
-    r = 0;
+    var s, j;
     for(j = 0; j < n; j++) {
-        s = (j + 1).toString();
+        s = (j % 1000).toString();
+        s = (1234000 + j % 1000).toString();
     }
-    return n;
+    global_res = s;
+    return n * 2;
 }
 
 function float_to_string(n)
 {
-    var s, r, j;
-    r = 0;
+    var s, j;
     for(j = 0; j < n; j++) {
         s = (j + 0.1).toString();
     }
+    global_res = s;
     return n;
 }
 
@@ -963,7 +1095,6 @@ function string_to_int(n)
     var s, r, j;
     r = 0;
     s = "12345";
-    r = 0;
     for(j = 0; j < n; j++) {
         r += (s | 0);
     }
@@ -976,7 +1107,6 @@ function string_to_float(n)
     var s, r, j;
     r = 0;
     s = "12345.6";
-    r = 0;
     for(j = 0; j < n; j++) {
         r -= s;
     }
@@ -986,32 +1116,70 @@ function string_to_float(n)
 
 function load_result(filename)
 {
-    var f, str, res;
-    if (typeof std === "undefined")
+    var has_filename = filename;
+    var has_error = false;
+    var str, res;
+
+    if (!filename)
+        filename = "microbench.txt";
+
+    if (typeof fs !== "undefined") {
+        // read the file in Node.js
+        try {
+            str = fs.readFileSync(filename, { encoding: "utf8" });
+        } catch {
+            has_error = true;
+        }
+    } else
+    if (typeof std !== "undefined") {
+        // read the file in QuickJS
+        var f = std.open(filename, "r");
+        if (!f) {
+            has_error = true;
+        }
+        str = f.readAsString();
+        f.close();
+    } else {
         return null;
-    f = std.open(filename ? filename : "microbench.txt", "r");
-    if (!f) {
-        if (filename) {
+    }
+    if (has_error) {
+        if (has_filename) {
             // Should throw exception?
             console.log("cannot load " + filename);
         }
         return null;
     }
-    str = f.readAsString();
     res = JSON.parse(str);
-    f.close();
     return res;
 }
 
 function save_result(filename, obj)
 {
-    var f;
-    if (typeof std === "undefined")
+    var str = JSON.stringify(obj, null, 2) + "\n";
+    var has_error = false;
+
+    if (typeof fs !== "undefined") {
+        // save the file in Node.js
+        try {
+            str = fs.writeFileSync(filename, str, { encoding: "utf8" });
+        } catch {
+            has_error = true;
+        }
+    } else
+    if (typeof std !== "undefined") {
+        // save the file in QuickJS
+        var f = std.open(filename, "w");
+        if (f) {
+            f.puts(str);
+            f.close();
+        } else {
+            has_error = 'true';
+        }
+    } else {
         return;
-    f = std.open(filename, "w");
-    f.puts(JSON.stringify(obj, null, 2));
-    f.puts("\n");
-    f.close();
+    }
+    if (has_error)
+        console.log("cannot save " + filename);
 }
 
 function main(argc, argv, g)
@@ -1022,13 +1190,17 @@ function main(argc, argv, g)
         empty_down_loop2,
         empty_do_loop,
         date_now,
+        date_parse,
         prop_read,
         prop_write,
+        prop_update,
         prop_create,
+        prop_clone,
         prop_delete,
         array_read,
         array_write,
         array_prop_create,
+        array_slice,
         array_length_decr,
         array_hole_length_decr,
         array_push,
@@ -1041,8 +1213,9 @@ function main(argc, argv, g)
         local_destruct,
         global_destruct,
         global_destruct_strict,
+        global_func_call,
         func_call,
-        closure_var,
+        func_closure_call,
         int_arith,
         float_arith,
         set_collection_add,
@@ -1065,7 +1238,7 @@ function main(argc, argv, g)
     ];
     var tests = [];
     var i, j, n, f, name, found;
-    var ref_file;
+    var ref_file, new_ref_file = "microbench-new.txt";
 
     if (typeof BigInt === "function") {
         /* BigInt test */
@@ -1101,6 +1274,10 @@ function main(argc, argv, g)
             ref_file = argv[i++];
             continue;
         }
+        if (name == "-s") {
+            new_ref_file = argv[i++];
+            continue;
+        }
         for (j = 0, found = false; j < test_list.length; j++) {
             f = test_list[j];
             if (f.name.startsWith(name)) {
@@ -1128,12 +1305,12 @@ function main(argc, argv, g)
             n++;
     }
     if (ref_data)
-        log_line("total", "", total[2], total[3], total_score * 100 / total_scale);
+        log_line("total", "", total[2], total[3], Math.round(total_scale * 1000 / total_score));
     else
         log_line("total", "", total[2]);
 
-    if (tests == test_list)
-        save_result("microbench-new.txt", log_data);
+    if (tests == test_list && new_ref_file)
+        save_result(new_ref_file, log_data);
 }
 
 if (typeof scriptArgs === "undefined") {