Commit d3da7c4c authored by Joel Martin's avatar Joel Martin

Add Javascript binary byte array test.

Compares normal Javascript arrays with Canvas ImageData arrays and
Typed Arrays (ArrayBuffers from WebGL).
parent 43cf7bd8
<!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>
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");
}
#!/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()
/*
* 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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment