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
<script>
/*!
* Amplify 1.0beta - Core, Store, Request
*
* Copyright 2011 appendTo LLC. (http://appendto.com/team)
* Dual licensed under the MIT or GPL licenses.
* http://appendto.com/open-source-licenses
*
* http://amplifyjs.com
*/
/*!
* Amplify Core 1.0beta
*
* Copyright 2011 appendTo LLC. (http://appendto.com/team)
* Dual licensed under the MIT or GPL licenses.
* http://appendto.com/open-source-licenses
*
* http://amplifyjs.com
*/
(function(global, undefined) {
var slice = [].slice,
subscriptions = {};
var amplify = global.amplify = {
publish: function(topic) {
var args = slice.call(arguments, 1),
subscription, length, i = 0,
ret;
if (!subscriptions[topic]) {
return true;
}
for (length = subscriptions[topic].length; i < length; i++) {
subscription = subscriptions[topic][i];
ret = subscription.callback.apply(subscription.context, args);
if (ret === false) {
break;
}
}
return ret !== false;
},
subscribe: function(topic, context, callback, priority) {
if (arguments.length === 3 && typeof callback === "number") {
priority = callback;
callback = context;
context = null;
}
if (arguments.length === 2) {
callback = context;
context = null;
}
priority = priority || 10;
if (!subscriptions[topic]) {
subscriptions[topic] = [];
}
var i = subscriptions[topic].length,
subscriptionInfo = {
callback: callback,
context: context,
priority: priority
};
while (i--) {
if (subscriptions[topic][i].priority <= priority) {
subscriptions[topic].splice(i + 1, 0, subscriptionInfo);
return callback;
}
}
subscriptions[topic].unshift(subscriptionInfo);
return callback;
},
unsubscribe: function(topic, callback) {
if (!subscriptions[topic]) {
return;
}
var length = subscriptions[topic].length,
i = 0;
for (; i < length; i++) {
if (subscriptions[topic][i].callback === callback) {
subscriptions[topic].splice(i, 1);
break;
}
}
}
};
}(this));
/*!
* Amplify Store - Persistent Client-Side Storage 1.0beta
*
* Copyright 2011 appendTo LLC. (http://appendto.com/team)
* Dual licensed under the MIT or GPL licenses.
* http://appendto.com/open-source-licenses
*
* http://amplifyjs.com
*/
(function(amplify, undefined) {
var store = amplify.store = function(key, value, options, type) {
var type = store.type;
if (options && options.type && options.type in store.types) {
type = options.type;
}
return store.types[type](key, value, options || {});
};
store.types = {};
store.type = null;
store.addType = function(type, storage) {
if (!store.type) {
store.type = type;
}
store.types[type] = storage;
store[type] = function(key, value, options) {
options = options || {};
options.type = type;
return store(key, value, options);
};
}
store.error = function() {
return "amplify.store quota exceeded";
};
function createSimpleStorage(storageType, storage) {
var values = storage.__amplify__ ? JSON.parse(storage.__amplify__) : {};
function remove(key) {
if (storage.removeItem) {
storage.removeItem(key);
} else {
delete storage[key];
}
delete values[key];
}
store.addType(storageType, function(key, value, options) {
var ret = value,
now = (new Date()).getTime(),
storedValue, parsed;
if (!key) {
ret = {};
for (key in values) {
storedValue = storage[key];
parsed = storedValue ? JSON.parse(storedValue) : {
expires: -1
};
if (parsed.expires && parsed.expires <= now) {
remove(key);
} else {
ret[key.replace(/^__amplify__/, "")] = parsed.data;
}
}
storage.__amplify__ = JSON.stringify(values);
return ret;
}
// protect against overwriting built-in properties
key = "__amplify__" + key;
if (value === undefined) {
if (values[key]) {
storedValue = storage[key];
parsed = storedValue ? JSON.parse(storedValue) : {
expires: -1
};
if (parsed.expires && parsed.expires <= now) {
remove(key);
} else {
return parsed.data;
}
}
} else {
if (value === null) {
remove(key);
} else {
parsed = JSON.stringify({
data: value,
expires: options.expires ? now + options.expires : null
});
try {
storage[key] = parsed;
values[key] = true;
// quota exceeded
} catch (error) {
// expire old data and try again
store[storageType]();
try {
storage[key] = parsed;
values[key] = true;
} catch (error) {
throw store.error();
}
}
}
}
storage.__amplify__ = JSON.stringify(values);
return ret;
});
}
// localStorage + sessionStorage
// IE 8+, Firefox 3.5+, Safari 4+, Chrome 4+, Opera 10.5+, iPhone 2+, Android 2+
for (var webStorageType in {
localStorage: 1,
sessionStorage: 1
}) {
// try/catch for file protocol in Firefox
try {
if (window[webStorageType].getItem) {
createSimpleStorage(webStorageType, window[webStorageType]);
}
} catch (e) {}
}
// globalStorage
// non-standard: Firefox 2+
// https://developer.mozilla.org/en/dom/storage#globalStorage
if (window.globalStorage) {
// try/catch for file protocol in Firefox
try {
createSimpleStorage("globalStorage", window.globalStorage[window.location.hostname]);
// Firefox 2.0 and 3.0 have sessionStorage and globalStorage
// make sure we defualt to globalStorage
// but don't default to globalStorage in 3.5+ which also has localStorage
if (store.type === "sessionStorage") {
store.type = "globalStorage";
}
} catch (e) {}
}
// userData
// non-standard: IE 5+
// http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx
(function() {
// append to html instead of body so we can do this from the head
var div = document.createElement("div"),
attrKey = "amplify",
attrs;
div.style.display = "none";
document.getElementsByTagName("head")[0].appendChild(div);
if (div.addBehavior) {
div.addBehavior("#default#userdata");
div.load(attrKey);
attrs = div.getAttribute(attrKey) ? JSON.parse(div.getAttribute(attrKey)) : {};
store.addType("userData", function(key, value, options) {
var ret = value,
now = (new Date()).getTime(),
attr, parsed, prevValue;
if (!key) {
ret = {};
for (key in attrs) {
attr = div.getAttribute(key);
parsed = attr ? JSON.parse(attr) : {
expires: -1
};
if (parsed.expires && parsed.expires <= now) {
div.removeAttribute(key);
delete attrs[key];
} else {
ret[key] = parsed.data;
}
}
div.setAttribute(attrKey, JSON.stringify(attrs));
div.save(attrKey);
return ret;
}
// convert invalid characters to dashes
// http://www.w3.org/TR/REC-xml/#NT-Name
// simplified to assume the starting character is valid
// also removed colon as it is invalid in HTML attribute names
key = key.replace(/[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g, "-");
if (value === undefined) {
if (key in attrs) {
attr = div.getAttribute(key);
parsed = attr ? JSON.parse(attr) : {
expires: -1
};
if (parsed.expires && parsed.expires <= now) {
div.removeAttribute(key);
delete attrs[key];
} else {
return parsed.data;
}
}
} else {
if (value === null) {
div.removeAttribute(key);
delete attrs[key];
} else {
// we need to get the previous value in case we need to rollback
prevValue = div.getAttribute(key);
parsed = JSON.stringify({
data: value,
expires: (options.expires ? (now + options.expires) : null)
});
div.setAttribute(key, parsed);
attrs[key] = true;
}
}
div.setAttribute(attrKey, JSON.stringify(attrs));
try {
div.save(attrKey);
// quota exceeded
} catch (error) {
// roll the value back to the previous value
if (prevValue === null) {
div.removeAttribute(key);
delete attrs[key];
} else {
div.setAttribute(key, prevValue);
}
// expire old data and try again
store.userData();
try {
div.setAttribute(key, parsed);
attrs[key] = true;
div.save(attrKey);
} catch (error) {
// roll the value back to the previous value
if (prevValue === null) {
div.removeAttribute(key);
delete attrs[key];
} else {
div.setAttribute(key, prevValue);
}
throw store.error();
}
}
return ret;
});
}
}());
// in-memory storage
// fallback for all browsers to enable the API even if we can't persist data
createSimpleStorage("memory", {});
}(this.amplify = this.amplify || {}));
/*!
* Amplify Request 1.0beta
*
* Copyright 2011 appendTo LLC. (http://appendto.com/team)
* Dual licensed under the MIT or GPL licenses.
* http://appendto.com/open-source-licenses
*
* http://amplifyjs.com
*/
(function(amplify, undefined) {
function noop() {}
function isFunction(obj) {
return ({}).toString.call(obj) === "[object Function]";
}
amplify.request = function(resourceId, data, callback) {
// default to an empty hash just so we can handle a missing resourceId
// in one place
var settings = resourceId || {};
if (typeof settings === "string") {
if (isFunction(data)) {
callback = data;
data = {};
}
settings = {
resourceId: resourceId,
data: data || {},
success: callback
};
}
var request = {
abort: noop
},
resource = amplify.request.resources[settings.resourceId],
success = settings.success || noop,
error = settings.error || noop;
settings.success = function(data, status) {
status = status || "success";
amplify.publish("request.success", settings, data, status);
amplify.publish("request.complete", settings, data, status);
success(data, status);
};
settings.error = function(data, status) {
status = status || "error";
amplify.publish("request.error", settings, data, status);
amplify.publish("request.complete", settings, data, status);
error(data, status);
};
if (!resource) {
if (!settings.resourceId) {
throw "amplify.request: no resourceId provided";
}
throw "amplify.request: unknown resourceId: " + settings.resourceId;
}
if (!amplify.publish("request.before", settings)) {
settings.error(null, "abort");
return;
}
amplify.request.resources[settings.resourceId](settings, request);
return request;
};
amplify.request.types = {};
amplify.request.resources = {};
amplify.request.define = function(resourceId, type, settings) {
if (typeof type === "string") {
if (!(type in amplify.request.types)) {
throw "amplify.request.define: unknown type: " + type;
}
settings.resourceId = resourceId;
amplify.request.resources[resourceId] =
amplify.request.types[type](settings);
} else {
// no pre-processor or settings for one-off types (don't invoke)
amplify.request.resources[resourceId] = type;
}
};
}(amplify));
(function(amplify, $, undefined) {
var xhrProps = ["status", "statusText", "responseText", "responseXML", "readyState"];
amplify.request.types.ajax = function(defnSettings) {
defnSettings = $.extend({
type: "GET"
}, defnSettings);
return function(settings, request) {
var regex, xhr, url = defnSettings.url,
data = settings.data,
abort = request.abort,
ajaxSettings = {},
aborted = false,
ampXHR = {
readyState: 0,
setRequestHeader: function(name, value) {
return xhr.setRequestHeader(name, value);
},
getAllResponseHeaders: function() {
return xhr.getAllResponseHeaders();
},
getResponseHeader: function(key) {
return xhr.getResponseHeader(key);
},
overrideMimeType: function(type) {
return xhr.overrideMideType(type);
},
abort: function() {
aborted = true;
try {
xhr.abort();
// IE 7 throws an error when trying to abort
} catch (e) {}
handleResponse(null, "abort");
},
success: function(data, status) {
settings.success(data, status);
},
error: function(data, status) {
settings.error(data, status);
}
};
if (typeof data !== "string") {
data = $.extend(true, {}, defnSettings.data, data);
$.each(data, function(key, value) {
regex = new RegExp("{" + key + "}", "g");
if (regex.test(url)) {
url = url.replace(regex, value);
delete data[key];
}
});
}
$.extend(ajaxSettings, defnSettings, {
url: url,
type: defnSettings.type,
data: data,
dataType: defnSettings.dataType,
success: function(data, status) {
handleResponse(data, status);
},
error: function(_xhr, status) {
handleResponse(null, status);
},
beforeSend: function(_xhr, _ajaxSettings) {
xhr = _xhr;
ajaxSettings = _ajaxSettings;
var ret = defnSettings.beforeSend ? defnSettings.beforeSend.call(this, ampXHR, ajaxSettings) : true;
return ret && amplify.publish("request.before.ajax", defnSettings, settings, ajaxSettings, ampXHR);
}
});
$.ajax(ajaxSettings);
function handleResponse(data, status) {
$.each(xhrProps, function(i, key) {
try {
ampXHR[key] = xhr[key];
} catch (e) {}
});
if (ampXHR.statusText === "OK") {
ampXHR.statusText = "success";
}
if (data === undefined) {
// TODO: add support for ajax errors with data
data = null;
}
if (aborted) {
status = "abort";
}
if (/timeout|error|abort/.test(status)) {
ampXHR.error(data, status);
} else {
ampXHR.success(data, status);
}
// avoid handling a response multiple times
// this can happen if a request is aborted
// TODO: figure out if this breaks polling or multi-part responses
handleResponse = $.noop;
}
request.abort = function() {
ampXHR.abort();
abort.call(this);
};
};
};
var cache = amplify.request.cache = {
_key: function(resourceId, url, data) {
data = url + data;
var length = data.length,
i = 0,
checksum = chunk();
while (i < length) {
checksum ^= chunk();
}
function chunk() {
return data.charCodeAt(i++) << 24 | data.charCodeAt(i++) << 16 | data.charCodeAt(i++) << 8 | data.charCodeAt(i++) << 0;
}
return "request-" + resourceId + "-" + checksum;
},
_default: (function() {
var memoryStore = {};
return function(resource, settings, ajaxSettings, ampXHR) {
// data is already converted to a string by the time we get here
var cacheKey = cache._key(settings.resourceId, ajaxSettings.url, ajaxSettings.data),
duration = resource.cache;
if (cacheKey in memoryStore) {
ampXHR.success(memoryStore[cacheKey]);
return false;
}
var success = ampXHR.success;
ampXHR.success = function(data) {
memoryStore[cacheKey] = data;
if (typeof duration === "number") {
setTimeout(function() {
delete memoryStore[cacheKey];
}, duration);
}
success.apply(this, arguments);
};
};
}())
};
if (amplify.store) {
$.each(amplify.store.types, function(type) {
cache[type] = function(resource, settings, ajaxSettings, ampXHR) {
var cacheKey = cache._key(settings.resourceId, ajaxSettings.url, ajaxSettings.data),
cached = amplify.store[type](cacheKey);
if (cached) {
ajaxSettings.success(cached);
return false;
}
var success = ampXHR.success;
ampXHR.success = function(data) {
amplify.store[type](cacheKey, data, {
expires: resource.cache.expires
});
success.apply(this, arguments);
};
};
});
cache.persist = cache[amplify.store.type];
}
amplify.subscribe("request.before.ajax", function(resource) {
var cacheType = resource.cache;
if (cacheType) {
// normalize between objects and strings/booleans/numbers
cacheType = cacheType.type || cacheType;
return cache[cacheType in cache ? cacheType : "_default"].apply(this, arguments);
}
});
amplify.request.decoders = {
// http://labs.omniti.com/labs/jsend
jsend: function(data, status, ampXHR, success, error) {
if (data.status === "success") {
success(data.data);
} else if (data.status === "fail") {
error(data.data, "fail");
} else if (data.status === "error") {
delete data.status;
error(data, "error");
}
}
};
amplify.subscribe("request.before.ajax", function(resource, settings, ajaxSettings, ampXHR) {
var _success = ampXHR.success,
_error = ampXHR.error,
decoder = $.isFunction(resource.decoder) ? resource.decoder : resource.decoder in amplify.request.decoders ? amplify.request.decoders[resource.decoder] : amplify.request.decoders._default;
if (!decoder) {
return;
}
function success(data, status) {
_success(data, status);
}
function error(data, status) {
_error(data, status);
}
ampXHR.success = function(data, status) {
decoder(data, status, ampXHR, success, error);
};
ampXHR.error = function(data, status) {
decoder(data, status, ampXHR, success, error);
};
});
}(amplify, jQuery));
</script>
Ready to run.
Test | Ops/sec | |
---|---|---|
Subscribe |
| ready |
Unsubscribe |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.