jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
A simple benchmark of asm.js runtime. The setup uses a custom helper object to allocate memory, and the physics is simple, 1D with no acceleration. Basically, just a glorified adder.
NOTE: if your browser doesn't support asm.js (as of today, only firefox nightly does), then this test won't be informative.
Code by Jasper Palfree (wellcaffeinated.net). MIT License.
<script>
var ASM = (function(ASM){
ASM.Types = {
'bool' : { // uint8
size: 1,
pow: 0,
view: Uint8Array
},
'uint8' : {
size: 1,
pow: 0,
view: Uint8Array
},
'int8' : {
size: 1,
pow: 0,
view: Int8Array
},
'uint16' : {
size: 2,
pow: 1,
view: Int16Array
},
'int16' : {
size: 2,
pow: 1,
view: Int16Array
},
'uint32' : {
size: 4,
pow: 2,
view: Int32Array
},
'uint' : { // uint32
size: 4,
pow: 2,
view: Uint32Array
},
'int32' : {
size: 4,
pow: 2,
view: Int32Array
},
'int' : { // int32
size: 4,
pow: 2,
view: Int32Array
},
'float32': {
size: 4,
pow: 2,
view: Float32Array
},
'float' : { // float32
size: 4,
pow: 2,
view: Float32Array
},
'float64': {
size: 8,
pow: 3,
view: Float64Array
},
'double' : { // float64
size: 8,
pow: 3,
view: Float64Array
},
};
var Struct = function Struct( data, ptr, schema, buffer ){
var key
,props
;
this.setPtr( ptr );
this.buffer = buffer;
// setup getters/setters
for (key in schema){
props = schema[ key ];
this.addProp( key, props.ptr, props.type );
}
// set data
for (key in data){
this[ key ] = data[ key ];
}
};
Struct.prototype = {
setPtr: function( ptr ){
this.ptr = ptr;
},
addProp: function( name, ptr, type ){
var self = this
,typeProps = ASM.Types[ type ]
,viewInst = new typeProps.view( self.buffer )
,pow = typeProps.pow
;
self.__defineGetter__(name, function(){
return viewInst[ (self.ptr + ptr) >> pow ];
});
self.__defineSetter__(name, function( val ){
return viewInst[ (self.ptr + ptr) >> pow ] = val;
});
}
};
var Collection = function Collection( schema, options ){
if (!(this instanceof Collection)){
return new Collection( schema, options );
}
options = options || {};
var key
,type
,size
,objSize = 0
,tmp
,idx
,bufferSize
,largest = 0
,sc = {}
,table = [ {},{},{},{} ] // four hashes to guide the order of memory allocation
,maxObjects = options.maxObjects || 1000
;
for (key in schema){
type = schema[ key ];
if (typeof type === 'string'){
type = type.toLowerCase();
tmp = ASM.Types[ type ];
size = tmp.size;
if (!size){
throw 'Type ' + type + ' not supported.';
}
// assemble total object size
objSize += size;
largest = ( size > largest ) ? size : largest;
idx = tmp.pow;
table[ idx ][ key ] = type;
}
}
// round up to the nearest multiple of the largest object size
objSize = Math.ceil(objSize / largest) * largest;
this.objSize = objSize;
this.blockSize = largest;
// need the largest power of 2 greater than required size
bufferSize = 2 << Math.floor(Math.log(objSize * maxObjects)/Math.LN2);
this.buffer = new ArrayBuffer( bufferSize );
tmp = 0;
for ( var i = table.length - 1; i >= 0; i-- ){
for (key in table[ i ]){
!function(key, type, ptr){
var params = ASM.Types[ type ];
sc[ key ] = {
ptr: ptr,
pow: params.pow,
size: params.size,
type: type
};
tmp += params.size;
}(key, table[ i ][ key ], tmp);
}
}
this._schema = sc;
this.objs = [];
// TODO now make members...
};
Collection.prototype = {
add: function( obj ){
var ptr = this.objSize * this.objs.length
inst = new Struct( obj, ptr, this._schema, this.buffer )
;
this.objs.push( inst );
},
include: function( fn ){
var self = this
,stdlib = {
Uint8Array: Uint8Array,
Int8Array: Int8Array,
Uint16Array: Uint16Array,
Int16Array: Int16Array,
Uint32Array: Uint32Array,
Int32Array: Int32Array,
Float32Array: Float32Array,
Float64Array: Float64Array,
Math: Math
}
,coln = {
getLen: function(){
return self.objs.length;
},
ptr: self.ptr,
objSize: self.objSize
}
,mixin
,key
;
for ( key in self._schema ){
coln[ '$'+key ] = self._schema[ key ].ptr;
}
mixin = fn( stdlib, coln, self.buffer );
for ( key in mixin ){
self[ key ] = mixin[ key ];
}
}
};
ASM.Collection = Collection;
return ASM;
})(this.ASM || {});
// return test environments with "num" objects in them
function createWorldObjects(num){
var bodies = new ASM.Collection({
'fixed': 'bool',
'moi' : 'float',
'x' : 'float',
'vx' : 'float'
});
// this "new Function" thing is to get around
// the asm compilation limitation of the firefox nightly
// that only allows one instance of an asm module at a time.
// So we create multiple ones...
bodies.include(new Function('a', 'b', 'c', 'return (' + (function (stdlib, coln, heap){
var float32 = new stdlib.Float32Array( heap );
var uint32 = new stdlib.Uint32Array( heap );
var $x = coln.$x|0;
var $vx = coln.$vx|0;
var getLen = coln.getLen;
var size = coln.objSize|0;
var iterator = coln.ptr|0;
function integrate( dt ){
dt = dt|0;
var i = 0, l = 0, ptr = 0;
ptr = iterator|0;
l = getLen()|0;
while ((i|0) < (l|0)){
integrateOne( ptr, dt );
i = ((i|0) + 1)|0;
ptr = ((ptr|0) + (size|0))|0;
}
}
function integrateOne( $obj, dt ){
$obj = $obj|0;
dt = dt|0;
var x = 0.0, vx = 0.0;
x = +float32[($obj + $x) >> 2];
vx = +float32[($obj + $vx) >> 2];
float32[($obj + $x) >> 2] = +(+x + +vx * +(dt|0));
}
return {
integrate: integrate
};
}).toString() + '(a,b,c))'));
// now use asm
var bodiesASM = new ASM.Collection({
'fixed': 'bool',
'moi' : 'float',
'x' : 'float',
'vx' : 'float'
});
bodiesASM.include(new Function('a', 'b', 'c', 'return (' + (function(stdlib, coln, heap){
var float32 = new stdlib.Float32Array( heap );
var uint32 = new stdlib.Uint32Array( heap );
var $x = coln.$x|0;
var $vx = coln.$vx|0;
var getLen = coln.getLen;
var size = coln.objSize|0;
var iterator = coln.ptr|0;
function integrate( dt ){
dt = dt|0;
var i = 0, l = 0, ptr = 0;
ptr = iterator|0;
l = getLen()|0;
while ((i|0) < (l|0)){
integrateOne( ptr, dt );
i = ((i|0) + 1)|0;
ptr = ((ptr|0) + (size|0))|0;
}
}
function integrateOne( $obj, dt ){
$obj = $obj|0;
dt = dt|0;
var x = 0.0, vx = 0.0;
x = +float32[($obj + $x) >> 2];
vx = +float32[($obj + $vx) >> 2];
float32[($obj + $x) >> 2] = +(+x + +vx * +(dt|0));
}
return {
integrate: integrate
};
}).toString().replace(/^function\s?\([^)]*\){/, '$& "use asm";') + '(a,b,c))'));
// previous line just inserts "use asm" at the top of the function content
// initialize objects with random positions and velocities
for (var i = 0; i < num; i++){
var obj = {
x: Math.random(),
vx: Math.random()
};
bodies.add(obj);
bodiesASM.add(obj);
}
return {
bodies: bodies,
bodiesASM: bodiesASM
};
}
// set up tests
var test10 = createWorldObjects(10);
var test100 = createWorldObjects(100);
var test500 = createWorldObjects(500);
var test1000 = createWorldObjects(1000);
var test10000 = createWorldObjects(10000);
</script>
Ready to run.
Test | Ops/sec | |
---|---|---|
ASM x 10 |
| ready |
No ASM x 10 |
| ready |
ASM x 100 |
| ready |
No ASM x 100 |
| ready |
ASM x 500 |
| ready |
No ASM x 500 |
| ready |
ASM x 1000 |
| ready |
No ASM x 1000 |
| ready |
ASM x 10000 |
| ready |
No ASM x 10000 |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.