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 src="https://cdn.jsdelivr.net/npm/dequal@2.0.3/dist/index.min.js"></script>
<script src="https://unpkg.com/equivalent-key-map/dist/equivalent-key-map.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/immutable@4.3.7/dist/immutable.min.js"></script>
class DeepMapC {
map = new Map();
constructor(equals = dequal.dequal) {
this.equals = equals;
}
findKey(key) {
for (let k of this.map.keys()) {
if (this.equals(k, key)) return k;
}
}
set(key, value) {
this.map.set(this.findKey(key) ?? key, value);
return this;
}
get(key) {
const existingKey = this.findKey(key);
if (existingKey) return this.map.get(existingKey);
}
has(key) {
return !!this.findKey(key);
}
delete(key) {
const existingKey = this.findKey(key);
if (existingKey) return this.map.delete(existingKey);
else return false;
}
clear() {
this.map.clear();
}
get size() {
return this.map.size;
}
keys() {
return this.map.keys();
}
values() {
return this.map.values();
}
entries() {
return this.map.entries();
}
[Symbol.iterator]() {
return this.map[Symbol.iterator]();
}
}
class DeepMapS {
map = new Map();
keyMap = new Map();
constructor(serialize, initialValues) {
this.serialize = serialize;
if (initialValues) {
for (const [key, value] of initialValues) this.set(key, value);
}
}
set(key, value) {
const keyStr = this.serialize(key);
this.keyMap.set(keyStr, key);
this.map.set(keyStr, value);
return this;
}
get(key) {
return this.map.get(this.serialize(key));
}
has(key) {
return this.keyMap.has(this.serialize(key));
}
delete(key) {
const keyStr = this.serialize(key);
this.keyMap.delete(keyStr);
return this.map.delete(keyStr);
}
clear() {
this.map.clear();
this.keyMap.clear();
}
get size() {
return this.map.size;
}
keys() {
return this.keyMap.values();
}
values() {
return this.map.values();
}
forEach(cb, thisArg) {
this.keyMap.forEach((key, keyStr) => cb.call(thisArg, this.map.get(keyStr), key, this));
}
copy() {
const newMap = new DeepMap(this.serialize);
// directly copying the map and keymap (instead of passing it to the constructor), avoids having to recalculate each key hash
newMap.map = new Map(this.map);
newMap.keyMap = new Map(this.keyMap);
return newMap;
}
*entries() {
for (let [keyStr, key] of this.keyMap.entries()) {
yield [key, this.map.get(keyStr)];
}
}
[Symbol.iterator]() {
return this.entries();
}
}
class DeepMapO {
map = {};
keyMap = {};
constructor(serialize, initialValues) {
this.serialize = serialize;
if (initialValues?.[Symbol.iterator]) {
for (const [key, value] of initialValues) this.set(key, value);
}
}
set(key, value) {
const keyStr = this.serialize(key);
this.keyMap[keyStr] = key;
this.map[keyStr] = value;
return this;
}
get(key) {
return this.map[this.serialize(key)];
}
has(key) {
return !!this.map[this.serialize(key)];
}
delete(key) {
const keyStr = this.serialize(key);
const hasKey = !!this.map[keyStr];
delete this.keyMap[hasKey];
delete this.map[keyStr];
return hasKey;
}
clear() {
this.map = {};
this.keyMap = {};
}
get size() {
return Object.keys(this.map).length;
}
*keys() {
for (let keyStr in this.keyMap) yield this.keyMap[keyStr];
}
*values() {
for (let keyStr in this.keyMap) yield this.map[keyStr];
}
*entries() {
for (let keyStr in this.keyMap) yield [this.keyMap[keyStr], this.map[keyStr]];
}
forEach(cb, thisArg) {
for (let keyStr in this.keyMap) cb.call(thisArg, this.map[keyStr], this.keyMap[keyStr], this);
}
copy() {
const newMap = new DeepMapO(this.serialize);
// directly copying the map and keymap (instead of passing it to the constructor), avoids having to recalculate each key hash
newMap.map = { ...this.map };
newMap.keyMap = { ...this.keyMap };
return newMap;
}
[Symbol.iterator]() {
return this.entries();
}
}
const serialize = k => `${k.chain}}_{${k.joint}`;
const deSerialize = str => {
const parts = str.split('}_{');
return { chain: parts[0], joint: parts[1] };
};
const map = new Map();
const obj = {};
const mapIsEqual = new DeepMapC(dequal.dequal);
const deepMapMap = new DeepMapS(serialize);
const deepMapObj = new DeepMapO(serialize);
const equivalentKeyMap = new EquivalentKeyMap();
let immutableMap = new Immutable.Map();
for (let chain = 0; chain < 5; chain++) {
for (let joint = 0; joint < 10; joint++) {
const key = {
chain: `cf221f55-957c-43bf-be45-f5be6d2cebe4-${chain}`,
joint: `dc7d8abf-871c-4980-84ed-c7049a67558d-${joint}`
};
const value = `value-${chain}-${joint}`;
map.set(serialize(key), value);
obj[serialize(key)] = value;
equivalentKeyMap.set(key, value);
immutableMap = immutableMap.set(Immutable.fromJS(key), value);
mapIsEqual.set(key, value);
deepMapMap.set(key, value);
deepMapObj.set(key, value);
}
}
const testKey = {
chain: `cf221f55-957c-43bf-be45-f5be6d2cebe4-${4}`,
joint: `dc7d8abf-871c-4980-84ed-c7049a67558d-${8}`
};
Ready to run.
Test | Ops/sec | |
---|---|---|
native map |
| ready |
native obj |
| ready |
equivalentKeyMap |
| ready |
immutableMap |
| ready |
deepMap deep isEqual |
| ready |
deepMap map |
| ready |
deepMap obj |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.