浏览代码

Add Javascript binary byte array test.

Compares normal Javascript arrays with Canvas ImageData arrays and
Typed Arrays (ArrayBuffers from WebGL).
Joel Martin 14 年之前
父节点
当前提交
d3da7c4c4f
共有 4 个文件被更改,包括 660 次插入0 次删除
  1. 39 0
      tests/arrays.html
  2. 369 0
      tests/arrays.js
  3. 199 0
      tests/json2graph.py
  4. 53 0
      tests/stats.js

+ 39 - 0
tests/arrays.html

@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Javascript Arrays Performance Test</title>
+        <!--
+        <script type='text/javascript' 
+            src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
+        -->
+        <script src="include/util.js"></script>
+        <script src="include/webutil.js"></script>
+        <script src="browser.js"></script>
+        <script src="stats.js"></script>
+        <script src="arrays.js"></script>
+    </head>
+    <body>
+        <h3>Javascript Arrays Performance Test</h3>
+        Iterations: <input id='iterations' style='width:50'>&nbsp;
+        Array Size: <input id='arraySize' style='width:40'>*1024&nbsp;
+
+        <input id='startButton' type='button' value='Run Tests'
+            onclick="begin();">&nbsp;
+
+        <br><br>
+        Results:<br>
+        <textarea id="messages" style="font-size: 9;" cols=80 rows=50></textarea>
+        </br>
+        <canvas id="canvas" style="display: none;"></canvas>
+
+    </body>
+
+    <script>
+        var verbose = true;
+        window.onload = function() {
+            vmessage("in onload");
+            init();
+
+        }
+    </script>
+</html>

+ 369 - 0
tests/arrays.js

@@ -0,0 +1,369 @@
+var ctx, i, j, randlist,
+    new_normal, new_imageData, new_arrayBuffer,
+    browser = Browser.browser + " " +
+              Browser.version + " on " +
+              Browser.OS,
+    do_imageData   = false,
+    do_arrayBuffer = false,
+    conf = {
+        'create_cnt'     : 2000,
+        'read_cnt'       : 5000000,
+        'write_cnt'      : 5000000,
+        'iterations'     : 0,
+        'order_l1'       : [browser],
+        'order_l2'       : ['normal',
+                            'imageData',
+                            'arrayBuffer'],
+        'order_l3'       : ['create',
+                            'sequentialRead',
+                            'randomRead',
+                            'sequentialWrite']
+    },
+    stats = {},
+    testFunc = {},
+    iteration, arraySize;
+
+var newline = "\n";
+if (Util.Engine.trident) {
+    var newline = "<br>\n";
+}
+function message(str) {
+    console.log(str);
+    cell = $D('messages');
+    cell.innerHTML += str + newline;
+    cell.scrollTop = cell.scrollHeight;
+}
+
+function vmessage(str) {
+    if (verbose) {
+        message(str);
+    } else {
+        console.log(str);
+    }
+}
+
+new_normal = function() {
+    var arr = [], i;
+    for (i = 0; i < arraySize; i++) {
+        arr[i] = 0;
+    }
+    return arr;
+}
+
+/* Will be overridden with real function */
+new_imageData = function() {
+    throw("imageData not supported");
+};
+
+new_imageData_createImageData = function() {
+    var imageData = ctx.createImageData(1024/4, arraySize / 1024);
+    return imageData.data;
+};
+
+new_imageData_getImageData = function() {
+    var imageData = ctx.getImageData(0, 0, 1024/4, arraySize / 1024),
+        arr = imageData.data;
+    for (i = 0; i < arraySize; i++) {
+        arr[i] = 0;
+    }
+    return arr;
+};
+
+new_arrayBuffer = function() {
+    var arr = new ArrayBuffer(arraySize);
+    return new Uint8Array(arr);
+}
+
+function init_randlist() {
+    randlist = [];
+    for (var i=0; i < arraySize; i++) {
+        randlist[i] = parseInt(Math.random() * 256, 10);
+    }
+}
+function copy_randlist(arr) {
+    for (var i=0; i < arraySize; i++) {
+        arr[i] = randlist[i];
+    }
+}
+
+function begin() {
+    var i, j;
+    conf.iterations = parseInt($D('iterations').value, 10);
+    arraySize = parseInt($D('arraySize').value, 10) * 1024;
+
+    init_randlist();
+
+    // TODO: randomize test_list
+    
+    stats = {};
+    for (i = 0; i < conf.order_l2.length; i++) {
+        stats[conf.order_l2[i]] = {};
+        for (j = 0; j < conf.order_l3.length; j++) {
+            stats[conf.order_l2[i]][conf.order_l3[j]] = [];
+        }
+    }
+
+    $D('startButton').value = "Running";
+    $D('startButton').disabled = true;
+
+    message("running " + conf.iterations + " test iterations");
+    iteration = 1;
+    setTimeout(run_next_iteration, 250);
+}
+
+function finish() {
+    var totalTime, arrayType, testType, times;
+    message("tests finished");
+
+    for (j = 0; j < conf.order_l3.length; j++) {
+        testType = conf.order_l3[j];
+        message("Test '" + testType + "'");
+        for (i = 0; i < conf.order_l2.length; i++) {
+            arrayType = conf.order_l2[i];
+            message("  Array Type '" + arrayType);
+            times = stats[arrayType][testType];
+            message("    Average : " + times.mean() + "ms" +
+                    " (Total: " + times.sum() + "ms)");
+            message("    Min/Max : " + times.min() + "ms/" +
+                                        times.max() + "ms");
+            message("    StdDev  : " + times.stdDev() + "ms");
+        }
+    }
+
+    vmessage("array_chart.py JSON data:");
+    chart_data = {'conf' : conf, 'stats' : { } };
+    chart_data.stats[browser] = stats;
+    chart_data.stats['next_browser'] = {};
+    vmessage(JSON.stringify(chart_data, null, 2));
+
+    $D('startButton').disabled = false;
+    $D('startButton').value = "Run Tests";
+}
+
+function run_next_iteration() {
+    var arrayType, testType, deltaTime;
+    
+    for (i = 0; i < conf.order_l2.length; i++) {
+        arrayType = conf.order_l2[i];
+        if (arrayType === 'imageData' && (!do_imageData)) {
+            continue;
+        }
+        if (arrayType === 'arrayBuffer' && (!do_arrayBuffer)) {
+            continue;
+        }
+        for (j = 0; j < conf.order_l3.length; j++) {
+            testType = conf.order_l3[j];
+
+            deltaTime = testFunc[arrayType + "_" + testType]();
+
+            stats[arrayType][testType].push(deltaTime);
+            vmessage("test " + (arrayType + "_" + testType) +
+                        " time: " + (deltaTime) + "ms");
+        }
+    }
+
+    message("finished test iteration " + iteration);
+    if (iteration >= conf.iterations) {
+        setTimeout(finish, 1);
+        return;
+    }
+    iteration++;
+    setTimeout(run_next_iteration, 1);
+}
+
+/*
+    * Test functions
+    */
+
+testFunc["normal_create"] = function() {
+    var cnt, arrNormal, startTime, endTime;
+    vmessage("create normal array " + conf.create_cnt + "x, initialized to 0");
+
+    startTime = (new Date()).getTime();
+    for (cnt = 0; cnt < conf.create_cnt; cnt++) {
+        arrNormal = new_normal();
+    }
+    endTime = (new Date()).getTime();
+
+    return endTime - startTime;
+};
+
+testFunc["imageData_create"] = function() {
+    var cnt, arrImage, startTime, endTime;
+    vmessage("create imageData array " + conf.create_cnt + "x, initialized to 0");
+
+    startTime = (new Date()).getTime();
+    for (cnt = 0; cnt < conf.create_cnt; cnt++) {
+        arrImage = new_imageData();
+    }
+    endTime = (new Date()).getTime();
+
+    if (arrImage[103] !== 0) {
+        message("Initialization failed, arrImage[103] is: " + arrImage[103]);
+        throw("Initialization failed, arrImage[103] is: " + arrImage[103]);
+    }
+    return endTime - startTime;
+};
+
+testFunc["arrayBuffer_create"] = function() {
+    var cnt, arrBuffer, startTime, endTime;
+    vmessage("create arrayBuffer array " + conf.create_cnt + "x, initialized to 0");
+
+    startTime = (new Date()).getTime();
+    for (cnt = 0; cnt < conf.create_cnt; cnt++) {
+        arrBuffer = new_arrayBuffer();
+    }
+    endTime = (new Date()).getTime();
+
+    if (arrBuffer[103] !== 0) {
+        message("Initialization failed, arrBuffer[103] is: " + arrBuffer[103]);
+        throw("Initialization failed, arrBuffer[103] is: " + arrBuffer[103]);
+    }
+    return endTime - startTime;
+};
+
+function test_sequentialRead(arr) {
+    var i, j, cnt, startTime, endTime;
+    /* Initialize the array */
+    copy_randlist(arr);
+
+    startTime = (new Date()).getTime();
+    i = 0;
+    j = 0;
+    for (cnt = 0; cnt < conf.read_cnt; cnt++) {
+        j = arr[i];
+        i++;
+        if (i >= arraySize) {
+            i = 0;
+        }
+    }
+    endTime = (new Date()).getTime();
+
+    return endTime - startTime;
+}
+
+function test_randomRead(arr) {
+    var i, cnt, startTime, endTime;
+    /* Initialize the array */
+    copy_randlist(arr);   // used as jumplist
+
+    startTime = (new Date()).getTime();
+    i = 0;
+    for (cnt = 0; cnt < conf.read_cnt; cnt++) {
+        i = (arr[i] + cnt) % arraySize;
+    }
+    endTime = (new Date()).getTime();
+
+    return endTime - startTime;
+}
+
+function test_sequentialWrite(arr) {
+    var i, cnt, startTime, endTime;
+    /* Initialize the array */
+    copy_randlist(arr);
+
+    startTime = (new Date()).getTime();
+    i = 0;
+    for (cnt = 0; cnt < conf.write_cnt; cnt++) {
+        arr[i] = (cnt % 256);
+        i++;
+        if (i >= arraySize) {
+            i = 0;
+        }
+    }
+    endTime = (new Date()).getTime();
+
+    return endTime - startTime;
+}
+
+/* Sequential Read Tests */
+testFunc["normal_sequentialRead"] = function() {
+    vmessage("read normal array " + conf.read_cnt + "x");
+    return test_sequentialRead(new_normal());
+};
+
+testFunc["imageData_sequentialRead"] = function() {
+    vmessage("read imageData array " + conf.read_cnt + "x");
+    return test_sequentialRead(new_imageData());
+};
+
+testFunc["arrayBuffer_sequentialRead"] = function() {
+    vmessage("read arrayBuffer array " + conf.read_cnt + "x");
+    return test_sequentialRead(new_arrayBuffer());
+};
+
+
+/* Random Read Tests */
+testFunc["normal_randomRead"] = function() {
+    vmessage("read normal array " + conf.read_cnt + "x");
+    return test_randomRead(new_normal());
+};
+
+testFunc["imageData_randomRead"] = function() {
+    vmessage("read imageData array " + conf.read_cnt + "x");
+    return test_randomRead(new_imageData());
+};
+
+testFunc["arrayBuffer_randomRead"] = function() {
+    vmessage("read arrayBuffer array " + conf.read_cnt + "x");
+    return test_randomRead(new_arrayBuffer());
+};
+
+
+/* Sequential Write Tests */
+testFunc["normal_sequentialWrite"] = function() {
+    vmessage("write normal array " + conf.write_cnt + "x");
+    return test_sequentialWrite(new_normal());
+};
+
+testFunc["imageData_sequentialWrite"] = function() {
+    vmessage("write imageData array " + conf.write_cnt + "x");
+    return test_sequentialWrite(new_imageData());
+};
+
+testFunc["arrayBuffer_sequentialWrite"] = function() {
+    vmessage("write arrayBuffer array " + conf.write_cnt + "x");
+    return test_sequentialWrite(new_arrayBuffer());
+};
+
+init = function() {
+    vmessage(">> init");
+
+    $D('iterations').value = 10;
+    $D('arraySize').value = 10;
+    arraySize = parseInt($D('arraySize').value, 10) * 1024;
+
+    message("Browser: " + browser);
+
+    /* Determine browser binary array support */
+    try {
+        ctx = $D('canvas').getContext('2d');
+        new_imageData = new_imageData_createImageData;
+        new_imageData();
+        do_imageData = true;
+    } catch (exc) {
+        vmessage("createImageData not supported: " + exc);
+        try {
+            ctx = $D('canvas').getContext('2d');
+            new_imageData = new_imageData_getImageData;
+            blah = new_imageData();
+            do_imageData = true;
+        } catch (exc) {
+            vmessage("getImageData not supported: " + exc);
+        }
+    }
+    if (! do_imageData) {
+        message("imageData arrays not supported");
+    }
+
+    try {
+        new_arrayBuffer();
+        do_arrayBuffer = true;
+    } catch (exc) {
+        vmessage("Typed Arrays not supported: " + exc);
+    }
+    if (! do_arrayBuffer) {
+        message("Typed Arrays (ArrayBuffers) not suppoted");
+    }
+    vmessage("<< init");
+}

+ 199 - 0
tests/json2graph.py

@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+# a bar plot with errorbars
+import sys, json, pprint
+import numpy as np
+import matplotlib.pyplot as plt
+from matplotlib.font_manager import FontProperties
+
+def usage():
+    print "%s json_file level1 level2 level3\n\n" % sys.argv[0]
+    print "Description:\n"
+    print "level1, level2, and level3 are one each of the following:\n";
+    print "  select=ITEM - select only ITEM at this level";
+    print "  bar         - each item on this level becomes a graph bar";
+    print "  group       - items on this level become groups of bars";
+    print "\n";
+    print "json_file is a file containing json data in the following format:\n"
+    print '  {';
+    print '    "conf": {';
+    print '      "order_l1": [';
+    print '        "level1_label1",';
+    print '        "level1_label2",';
+    print '        ...';
+    print '      ],';
+    print '      "order_l2": [';
+    print '        "level2_label1",';
+    print '        "level2_label2",';
+    print '        ...';
+    print '      ],';
+    print '      "order_l3": [';
+    print '        "level3_label1",';
+    print '        "level3_label2",';
+    print '        ...';
+    print '      ]';
+    print '    },';
+    print '    "stats": {';
+    print '      "level1_label1": {';
+    print '        "level2_label1": {';
+    print '          "level3_label1": [val1, val2, val3],';
+    print '          "level3_label2": [val1, val2, val3],';
+    print '          ...';
+    print '        },';
+    print '        "level2_label2": {';
+    print '        ...';
+    print '        },';
+    print '      },';
+    print '      "level1_label2": {';
+    print '        ...';
+    print '      },';
+    print '      ...';
+    print '    },';
+    print '  }';
+    sys.exit(2)
+
+def error(msg):
+    print msg
+    sys.exit(1)
+
+
+#colors = ['#ff0000', '#0863e9', '#00f200', '#ffa100',
+#          '#800000', '#805100', '#013075', '#007900']
+colors = ['#ff0000', '#00ff00', '#0000ff',
+          '#dddd00', '#dd00dd', '#00dddd',
+          '#dd6622', '#dd2266', '#66dd22',
+          '#8844dd', '#44dd88', '#4488dd']
+
+if len(sys.argv) < 5:
+    usage()
+
+filename = sys.argv[1]
+L1 = sys.argv[2]
+L2 = sys.argv[3]
+L3 = sys.argv[4]
+if len(sys.argv) > 5:
+    legendHeight = float(sys.argv[5])
+else:
+    legendHeight = 0.75
+
+# Load the JSON data from the file
+data = json.loads(file(filename).read())
+conf = data['conf']
+stats = data['stats']
+
+# Sanity check data hierarchy
+if len(conf['order_l1']) != len(stats.keys()):
+    error("conf.order_l1 does not match stats level 1")
+for l1 in stats.keys():
+    if len(conf['order_l2']) != len(stats[l1].keys()):
+        error("conf.order_l2 does not match stats level 2 for %s" % l1)
+    if conf['order_l1'].count(l1) < 1:
+        error("%s not found in conf.order_l1" % l1)
+    for l2 in stats[l1].keys():
+        if len(conf['order_l3']) != len(stats[l1][l2].keys()):
+            error("conf.order_l3 does not match stats level 3")
+        if conf['order_l2'].count(l2) < 1:
+            error("%s not found in conf.order_l2" % l2)
+        for l3 in stats[l1][l2].keys():
+            if conf['order_l3'].count(l3) < 1:
+                error("%s not found in conf.order_l3" % l3)
+
+#
+# Generate the data based on the level specifications
+#
+bar_labels = None
+group_labels = None
+bar_vals = []
+bar_sdvs = []
+if L3.startswith("select="):
+    select_label = l3 = L3.split("=")[1]
+    bar_labels = conf['order_l1']
+    group_labels = conf['order_l2']
+    bar_vals = [[0]*len(group_labels) for i in bar_labels]
+    bar_sdvs = [[0]*len(group_labels) for i in bar_labels]
+    for b in range(len(bar_labels)):
+        l1 = bar_labels[b]
+        for g in range(len(group_labels)):
+            l2 = group_labels[g]
+            bar_vals[b][g] = np.mean(stats[l1][l2][l3])
+            bar_sdvs[b][g] = np.std(stats[l1][l2][l3])
+elif L2.startswith("select="):
+    select_label = l2 = L2.split("=")[1]
+    bar_labels = conf['order_l1']
+    group_labels = conf['order_l3']
+    bar_vals = [[0]*len(group_labels) for i in bar_labels]
+    bar_sdvs = [[0]*len(group_labels) for i in bar_labels]
+    for b in range(len(bar_labels)):
+        l1 = bar_labels[b]
+        for g in range(len(group_labels)):
+            l3 = group_labels[g]
+            bar_vals[b][g] = np.mean(stats[l1][l2][l3])
+            bar_sdvs[b][g] = np.std(stats[l1][l2][l3])
+elif L1.startswith("select="):
+    select_label = l1 = L1.split("=")[1]
+    bar_labels = conf['order_l2']
+    group_labels = conf['order_l3']
+    bar_vals = [[0]*len(group_labels) for i in bar_labels]
+    bar_sdvs = [[0]*len(group_labels) for i in bar_labels]
+    for b in range(len(bar_labels)):
+        l2 = bar_labels[b]
+        for g in range(len(group_labels)):
+            l3 = group_labels[g]
+            bar_vals[b][g] = np.mean(stats[l1][l2][l3])
+            bar_sdvs[b][g] = np.std(stats[l1][l2][l3])
+else:
+    usage()
+
+# If group is before bar then flip (zip) the data
+if [L1, L2, L3].index("group") < [L1, L2, L3].index("bar"):
+    bar_labels, group_labels = group_labels, bar_labels
+    bar_vals = zip(*bar_vals)
+    bar_sdvs = zip(*bar_sdvs)
+
+print "bar_vals:", bar_vals
+
+#
+# Now render the bar graph
+#
+ind = np.arange(len(group_labels))  # the x locations for the groups
+width = 0.8 * (1.0/len(bar_labels)) # the width of the bars
+
+fig = plt.figure(figsize=(10,6), dpi=80)
+plot = fig.add_subplot(1, 1, 1)
+
+rects = []
+for i in range(len(bar_vals)):
+    rects.append(plot.bar(ind+width*i, bar_vals[i], width, color=colors[i],
+                          yerr=bar_sdvs[i], align='center'))
+
+# add some
+plot.set_ylabel('Milliseconds (less is better)')
+plot.set_title("Javascript array test: %s" % select_label)
+plot.set_xticks(ind+width)
+plot.set_xticklabels( group_labels )
+
+fontP = FontProperties()
+fontP.set_size('small')
+plot.legend( [r[0] for r in rects], bar_labels, prop=fontP,
+            loc = 'center right', bbox_to_anchor = (1.0, legendHeight))
+
+def autolabel(rects):
+    # attach some text labels
+    for rect in rects:
+        height = rect.get_height()
+        if np.isnan(height):
+            height = 0.0
+        plot.text(rect.get_x()+rect.get_width()/2., height+20, '%d'%int(height),
+                ha='center', va='bottom', size='7')
+
+for rect in rects:
+    autolabel(rect)
+
+# Adjust axis sizes
+axis = list(plot.axis())
+axis[0] = -width          # Make sure left side has enough for bar
+#axis[1] = axis[1] * 1.20  # Add 20% to the right to make sure it fits
+axis[2] = 0               # Make y-axis start at 0
+axis[3] = axis[3] * 1.10  # Add 10% to the top
+plot.axis(axis)
+
+plt.show()

+ 53 - 0
tests/stats.js

@@ -0,0 +1,53 @@
+/*
+ * Define some useful statistical functions on arrays of numbers
+ */
+
+Array.prototype.sum = function() {
+    var i, sum = 0;
+    for (i = 0; i < this.length; i++) {
+        sum += this[i];
+    }
+    return sum;
+}
+
+Array.prototype.max = function() {
+    return Math.max.apply(null, this);
+}
+
+Array.prototype.min = function() {
+    return Math.min.apply(null, this);
+}
+
+Array.prototype.mean = function() {
+    return this.sum() / this.length;
+}
+Array.prototype.average = Array.prototype.mean;
+
+Array.prototype.median = function() {
+    var sorted = this.sort( function(a,b) { return a-b; }),
+        len = sorted.length;
+    if (len % 2) {
+        return sorted[Math.floor(len / 2)]; // Odd
+    } else {
+        return (sorted[len/2 - 1] + sorted[len/2]) / 2; // Even
+    }
+}
+
+Array.prototype.stdDev = function(sample) {
+    var i, sumSqr = 0, mean = this.mean(), N;
+
+    if (sample) {
+        // Population correction if this is a sample
+        N = this.length - 1;
+    } else {
+        // Standard deviation of just the array
+        N = this.length;
+    }
+
+    for (i = 0; i < this.length; i++) {
+        sumSqr += Math.pow(this[i] - mean, 2);
+    }
+
+    return Math.sqrt(sumSqr / N);
+}
+