fastbitset
Version:
Speed-optimized BitSet implementation for modern browsers and JavaScript engines
1,309 lines (1,262 loc) • 39.1 kB
JavaScript
/* performance benchmark */
/* This script expects node.js */
"use strict";
const FastBitSet = require("../FastBitSet.js");
const TypedFastBitSet = require("typedfastbitset");
const BitSet = require("bitset.js");
const Benchmark = require("benchmark");
const tBitSet = require("bitset");
const fBitSet = require("fast-bitset");
const roaring = require("roaring");
const os = require("os");
const smallgap = 3;
const largegap = 210;
const genericSetChange = function (set1, set2) {
const answer = new Set();
if (set2.length > set1.length) {
for (const j of set2) {
if (!set1.has(j)) {
answer.add(j);
}
}
} else {
for (const j of set1) {
if (!set2.has(j)) {
answer.add(j);
}
}
}
return answer;
};
const genericInplaceSetChange = function (set1, set2) {
if (set2.length > set1.length) {
for (const j of set2) {
if (set1.has(j)) {
set2.delete(j);
}
}
return set2;
} else {
for (const j of set1) {
if (set2.has(j)) {
set1.delete(j);
}
}
return set1;
}
};
const genericSetIntersection = function (set1, set2) {
const answer = new Set();
if (set2.length > set1.length) {
for (const j of set1) {
if (set2.has(j)) {
answer.add(j);
}
}
} else {
for (const j of set2) {
if (set1.has(j)) {
answer.add(j);
}
}
}
return answer;
};
const genericInplaceSetIntersection = function (set1, set2) {
for (const j of set2) {
if (!set1.has(j)) {
set1.delete(j);
}
}
return set1;
};
const genericSetIntersectionCard = function (set1, set2) {
let answer = 0;
if (set2.length > set1.length) {
for (const j of set1) {
if (set2.has(j)) {
answer++;
}
}
} else {
for (const j of set2.values()) {
if (set1.has(j)) {
answer++;
}
}
}
return answer;
};
const genericSetUnion = function (set1, set2) {
const answer = new Set(set1);
for (const j of set2) {
answer.add(j);
}
return answer;
};
const genericInplaceSetUnion = function (set1, set2) {
for (const j of set2) {
set1.add(j);
}
return set1;
};
const genericSetUnionCard = function (set1, set2) {
return set1.size + set2.size - genericSetIntersectionCard(set1, set2);
};
const genericSetDifference = function (set1, set2) {
const answer = new Set(set1);
for (const j of set2) {
answer.delete(j);
}
return answer;
};
const genericInplaceSetDifference = function (set1, set2) {
for (const j of set2) {
set1.delete(j);
}
return set1;
};
const genericSetDifferenceCard = function (set1, set2) {
return set1.size - genericSetIntersectionCard(set1, set2);
};
const N = 1024 * 1024;
function CreateBench() {
console.log("starting dynamic bitmap creation benchmark");
const b = new FastBitSet();
const tb = new TypedFastBitSet();
let bs = new BitSet();
const bt = new tBitSet();
const fb = new fBitSet(smallgap * N + 5);
for (let i = 0; i < N; i++) {
b.add(smallgap * i + 5);
bs = bs.set(smallgap * i + 5, true);
bt.set(smallgap * i + 5);
fb.set(smallgap * i + 5);
}
if (bs.cardinality() != b.size()) throw "something is off";
if (bs.cardinality() != bt.cardinality()) throw "something is off";
if (bs.cardinality() != fb.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet", function () {
const b = new FastBitSet();
for (let i = 0; i < N; i++) {
b.add(smallgap * i + 5);
}
return b;
})
.add("TypedFastBitSet", function () {
const b = new TypedFastBitSet();
for (let i = 0; i < N; i++) {
b.add(smallgap * i + 5);
}
return b;
})
.add("infusion.BitSet.js", function () {
let bs = new BitSet();
for (let i = 0; i < N; i++) {
bs = bs.set(smallgap * i + 5, true);
}
return bs;
})
.add("tdegrunt.BitSet", function () {
const bt = new tBitSet();
for (let i = 0; i < N; i++) {
bt.set(smallgap * i + 5);
}
return bt;
})
.add("Set", function () {
const bt = new Set();
for (let i = 0; i < N; i++) {
bt.add(smallgap * i + 5);
}
return bt;
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function ArrayBench() {
console.log("starting array extraction benchmark");
const b = new FastBitSet();
const tb = new TypedFastBitSet();
let bs = new BitSet();
const bt = new tBitSet();
const fb = new fBitSet(smallgap * N + 5);
for (let i = 0; i < N; i++) {
b.add(smallgap * i + 5);
tb.add(smallgap * i + 5);
bs = bs.set(smallgap * i + 5, true);
bt.set(smallgap * i + 5);
fb.set(smallgap * i + 5);
}
if (bs.cardinality() != b.size()) throw "something is off";
if (bs.cardinality() != bt.cardinality()) throw "something is off";
if (bs.cardinality() != fb.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet", function () {
return b.array();
})
.add("TypedFastBitSet", function () {
return tb.array();
})
// .add("infusion.BitSet.js", function () {
// return bs.toArray();
// })
.add("mattkrick.fast-bitset", function () {
return fb.getIndices();
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function ForEachBench() {
console.log("starting forEach benchmark");
const b = new FastBitSet();
const tb = new TypedFastBitSet();
let bs = new BitSet();
const bt = new tBitSet();
const fb = new fBitSet(smallgap * N + 5);
const s = new Set();
for (let i = 0; i < N; i++) {
b.add(smallgap * i + 5);
tb.add(smallgap * i + 5);
bs = bs.set(smallgap * i + 5, true);
bt.set(smallgap * i + 5);
fb.set(smallgap * i + 5);
s.add(smallgap * i + 5);
}
let card = 0;
for (const i in b.array()) {
card++;
}
if (bs.cardinality() != b.size()) throw "something is off";
if (bs.cardinality() != bt.cardinality()) throw "something is off";
if (bs.cardinality() != fb.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet", function () {
let card = 0;
const inc = function () {
card++;
};
b.forEach(inc);
return card;
})
.add("TypedFastBitSet", function () {
let card = 0;
const inc = function () {
card++;
};
tb.forEach(inc);
return card;
})
.add("FastBitSet (via array)", function () {
const card = 0;
for (const i in b.array()) {
card++;
}
return card;
})
.add("TypedFastBitSet (via array)", function () {
let card = 0;
for (const i in tb.array()) {
card++;
}
return card;
})
.add("mattkrick.fast-bitset", function () {
let card = 0;
const inc = function () {
card++;
};
fb.forEach(inc);
return card;
})
.add("Set", function () {
let card = 0;
const inc = function () {
card++;
};
fb.forEach(inc);
return card;
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function CardBench() {
console.log("starting cardinality benchmark");
const b = new FastBitSet();
const tb = new TypedFastBitSet();
let bs = new BitSet();
const bt = new tBitSet();
const fb = new fBitSet(smallgap * N + 5);
for (let i = 0; i < N; i++) {
b.add(smallgap * i + 5);
tb.add(smallgap * i + 5);
bs = bs.set(smallgap * i + 5, true);
bt.set(smallgap * i + 5);
fb.set(smallgap * i + 5);
}
if (bs.cardinality() != b.size()) throw "something is off";
if (bs.cardinality() != bt.cardinality()) throw "something is off";
if (bs.cardinality() != fb.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet", function () {
return b.size();
})
.add("TypedFastBitSet", function () {
return b.size();
})
.add("infusion.BitSet.js", function () {
return bs.cardinality();
})
.add("tdegrunt.BitSet", function () {
return bt.cardinality();
})
.add("mattkrick.fast-bitset", function () {
return fb.getCardinality();
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function QueryBench() {
console.log("starting query benchmark");
const b = new FastBitSet();
const tb = new TypedFastBitSet();
let bs = new BitSet();
const bt = new tBitSet();
const fb = new fBitSet(smallgap * N + 5);
const s = new Set();
for (let i = 0; i < N; i++) {
b.add(smallgap * i + 5);
tb.add(smallgap * i + 5);
bs = bs.set(smallgap * i + 5, true);
bt.set(smallgap * i + 5);
fb.set(smallgap * i + 5);
s.add(smallgap * i + 5);
}
if (bs.cardinality() != b.size()) throw "something is off";
if (bs.cardinality() != bt.cardinality()) throw "something is off";
if (bs.cardinality() != fb.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet", function () {
return b.has(122);
})
.add("TypedFastBitSet", function () {
return tb.has(122);
})
.add("infusion.BitSet.js", function () {
return bs.get(122);
})
.add("tdegrunt.BitSet", function () {
return bt.get(122);
})
.add("mattkrick.fast-bitset", function () {
return fb.get(122);
})
.add("Set", function () {
return s.has(122);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function CloneBench() {
console.log("starting clone benchmark");
const b = new FastBitSet();
const tb = new TypedFastBitSet();
let bs = new BitSet();
const bt = new tBitSet();
const fb = new fBitSet(smallgap * N + 5);
const s = new Set();
for (let i = 0; i < N; i++) {
b.add(smallgap * i + 5);
tb.add(smallgap * i + 5);
bs = bs.set(smallgap * i + 5, true);
bt.set(smallgap * i + 5);
fb.set(smallgap * i + 5);
s.add(smallgap * i + 5);
}
if (bs.cardinality() != b.size()) throw "something is off";
if (bs.cardinality() != bt.cardinality()) throw "something is off";
if (bs.cardinality() != fb.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet", function () {
return b.clone();
})
.add("TypedFastBitSet", function () {
return tb.clone();
})
.add("infusion.BitSet.js", function () {
return bs.clone();
})
.add("mattkrick.fast-bitset", function () {
return fb.clone();
})
.add("Set", function () {
return new Set(s);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function AndBench() {
console.log("starting intersection query benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
let bs1 = new BitSet();
let bs2 = new BitSet();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet (creates new bitset)", function () {
return b1.new_intersection(b2);
})
.add("TypedFastBitSet (creates new bitset)", function () {
return tb1.new_intersection(tb2);
})
.add("mattkrick.fast-bitset (creates new bitset)", function () {
return fb1.and(fb2);
})
.add("roaring", function () {
return roaring.RoaringBitmap32.and(r1, r2);
})
.add("Set", function () {
return genericSetIntersection(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function AndCardBench() {
console.log("starting intersection cardinality query benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
let bs1 = new BitSet();
let bs2 = new BitSet();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (genericSetIntersectionCard(s1, s2) != b1.intersection_size(b2))
throw "potential bug";
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet (creates new bitset)", function () {
return b1.new_intersection(b2).size();
})
.add("FastBitSet (fast way)", function () {
return b1.intersection_size(b2);
})
.add("TypedFastBitSet (creates new bitset)", function () {
return tb1.new_intersection(tb2).size();
})
.add("TypedFastBitSet (fast way)", function () {
return tb1.intersection_size(tb2);
})
.add("roaring", function () {
return r1.andCardinality(r2);
})
.add("mattkrick.fast-bitset (creates new bitset)", function () {
return fb1.and(fb2).getCardinality();
})
.add("Set", function () {
return genericSetIntersectionCard(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function OrCardBench() {
console.log("starting union cardinality query benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
let bs1 = new BitSet();
let bs2 = new BitSet();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet (creates new bitset)", function () {
return b1.new_union(b2).size();
})
.add("FastBitSet (fast way)", function () {
return b1.union_size(b2);
})
.add("TypedFastBitSet (creates new bitset)", function () {
return tb1.new_union(tb2).size();
})
.add("TypedFastBitSet (fast way)", function () {
return tb1.union_size(tb2);
})
.add("roaring", function () {
return r1.orCardinality(r2);
})
.add("mattkrick.fast-bitset (creates new bitset)", function () {
return fb1.or(fb2).getCardinality();
})
.add("Set", function () {
return genericSetUnionCard(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function DifferenceCardBench() {
console.log("starting difference cardinality query benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
let bs1 = new BitSet();
let bs2 = new BitSet();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet (creates new bitset)", function () {
return b1.new_union(b2).size();
})
.add("FastBitSet (fast way)", function () {
return b1.difference_size(b2);
})
.add("TypedFastBitSet (creates new bitset)", function () {
return tb1.new_union(tb2).size();
})
.add("TypedFastBitSet (fast way)", function () {
return tb1.difference_size(tb2);
})
.add("roaring", function () {
return r1.andNotCardinality(r2);
})
.add("Set", function () {
return genericSetDifferenceCard(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function OrInplaceBench() {
console.log("starting inplace union benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
let bs1 = new BitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
let bs2 = new BitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet (inplace)", function () {
return b1.union(b2);
})
.add("TypedFastBitSet (inplace)", function () {
return tb1.union(tb2);
})
.add("infusion.BitSet.js (inplace)", function () {
return bs1.or(bs2);
})
.add("tdegrunt.BitSet (inplace)", function () {
return bt1.or(bt2);
})
.add("roaring", function () {
return r1.orInPlace(r2);
})
.add("Set (inplace)", function () {
return genericInplaceSetUnion(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function AndInplaceBench() {
console.log("starting inplace intersection benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
let bs1 = new BitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
let bs2 = new BitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
b1.intersection(b2);
bt1.and(bt2);
genericInplaceSetIntersection(s1, s2);
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet (inplace)", function () {
return b1.intersection(b2);
})
.add("TypedFastBitSet (inplace)", function () {
return tb1.intersection(tb2);
})
.add("infusion.BitSet.js (inplace)", function () {
return bs1.and(bs2);
})
.add("tdegrunt.BitSet (inplace)", function () {
return bt1.and(bt2);
})
.add("roaring", function () {
return r1.andInPlace(r2);
})
.add("Set (inplace)", function () {
return genericInplaceSetIntersection(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function AndNotInplaceBench() {
console.log("starting inplace difference benchmark");
const b1 = new FastBitSet();
const bb1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
const tbb1 = new TypedFastBitSet();
let bs1 = new BitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const bb2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
const tbb2 = new TypedFastBitSet();
let bs2 = new BitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
bb1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
bb2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bb1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bb2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
b1.difference(b2);
bb1.difference2(bb2);
bt1.andNot(bt2);
genericInplaceSetDifference(s1, s2);
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet (inplace)", function () {
return b1.difference(b2);
})
.add("FastBitSet (inplace2)", function () {
return bb1.difference2(bb2);
})
.add("TypedFastBitSet (inplace)", function () {
return tb1.difference(tb2);
})
.add("TypedFastBitSet (inplace2)", function () {
return tbb1.difference2(tbb2);
})
.add("infusion.BitSet.js (inplace)", function () {
return bs1.andNot(bs2);
})
.add("tdegrunt.BitSet (inplace)", function () {
return bt1.andNot(bt2);
})
.add("roaring", function () {
return r1.andNotInPlace(r2);
})
.add("Set (inplace)", function () {
return genericInplaceSetDifference(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function OrBench() {
console.log("starting union query benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
let bs1 = new BitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
let bs2 = new BitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
roaring.RoaringBitmap32.or(r1, r2);
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("roaring", function () {
return roaring.RoaringBitmap32.or(r1, r2);
})
.add("FastBitSet (creates new bitset)", function () {
return b1.new_union(b2);
})
.add("TypedFastBitSet (creates new bitset)", function () {
return tb1.new_union(tb2);
})
.add("mattkrick.fast-bitset (creates new bitset)", function () {
return fb1.or(fb2);
})
.add("Set", function () {
return genericSetUnion(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function DifferenceBench() {
console.log("starting difference query benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
let bs1 = new BitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
let bs2 = new BitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet (creates new bitset)", function () {
return b1.clone().difference(b2);
})
.add("TypedFastBitSet (creates new bitset)", function () {
return tb1.clone().difference(tb2);
})
.add("Set", function () {
return genericSetDifference(s1, s2);
})
//.add('roaring', function() {
// return roaring.RoaringBitmap32.andNot(r1,r2);
// })
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function XORInplaceBench() {
console.log("starting inplace change (XOR) benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
let bs1 = new BitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
let bs2 = new BitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
b1.change(b2);
bt1.xor(bt2);
genericInplaceSetChange(s1, s2);
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("FastBitSet (inplace)", function () {
return b1.change(b2);
})
.add("TypedFastBitSet (inplace)", function () {
return tb1.change(tb2);
})
.add("infusion.BitSet.js (inplace)", function () {
return bs1.xor(bs2);
})
.add("tdegrunt.BitSet (inplace)", function () {
return bt1.xor(bt2);
})
.add("roaring", function () {
return r1.xorInPlace(r2);
})
.add("Set (inplace)", function () {
return genericInplaceSetChange(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
function XORBench() {
console.log("starting change (XOR) query benchmark");
const b1 = new FastBitSet();
const tb1 = new TypedFastBitSet();
let bs1 = new BitSet();
const bt1 = new tBitSet();
const r1 = new roaring.RoaringBitmap32();
const r2 = new roaring.RoaringBitmap32();
const fb1 = new fBitSet(largegap * N + 5);
const b2 = new FastBitSet();
const tb2 = new TypedFastBitSet();
let bs2 = new BitSet();
const bt2 = new tBitSet();
const fb2 = new fBitSet(largegap * N + 5);
const s1 = new Set();
const s2 = new Set();
for (let i = 0; i < N; i++) {
b1.add(smallgap * i + 5);
tb1.add(smallgap * i + 5);
bs1 = bs1.set(smallgap * i + 5, true);
bt1.set(smallgap * i + 5);
fb1.set(smallgap * i + 5);
s1.add(smallgap * i + 5);
r1.add(smallgap * i + 5);
r2.add(largegap * i + 5);
b2.add(largegap * i + 5);
tb2.add(largegap * i + 5);
bs2 = bs2.set(largegap * i + 5, true);
bt2.set(largegap * i + 5);
fb2.set(largegap * i + 5);
s2.add(largegap * i + 5);
}
if (bs1.cardinality() != b1.size()) throw "something is off";
if (bs1.cardinality() != bt1.cardinality()) throw "something is off";
if (bs1.cardinality() != fb1.getCardinality()) throw "something is off";
if (bs2.cardinality() != b2.size()) throw "something is off";
if (bs2.cardinality() != bt2.cardinality()) throw "something is off";
if (bs2.cardinality() != fb2.getCardinality()) throw "something is off";
roaring.RoaringBitmap32.xor(r1, r2);
const suite = new Benchmark.Suite();
// add tests
const ms = suite
.add("roaring", function () {
return roaring.RoaringBitmap32.xor(r1, r2);
})
.add("FastBitSet (creates new bitset)", function () {
return b1.new_change(b2);
})
.add("TypedFastBitSet (creates new bitset)", function () {
return tb1.new_change(tb2);
})
.add("mattkrick.fast-bitset (creates new bitset)", function () {
return fb1.xor(fb2);
})
.add("Set", function () {
return genericSetChange(s1, s2);
})
// add listeners
.on("cycle", function (event) {
console.log(String(event.target));
})
// run async
.run({ async: false });
}
const main = function () {
console.log("Benchmarking against:");
console.log(
"TypedFastBitSet.js: https://github.com/lemire/TypedFastBitSet.js"
);
console.log("roaring: https://www.npmjs.com/package/roaring");
console.log(
"infusion.BitSet.js from https://github.com/infusion/BitSet.js",
require("bitset.js/package.json").version
);
console.log(
"tdegrunt.BitSet from https://github.com/tdegrunt/bitset",
require("bitset/package.json").version
);
console.log(
"mattkrick.fast-bitset from https://github.com/mattkrick/fast-bitset",
require("fast-bitset/package.json").version
);
console.log("standard Set object from JavaScript");
console.log("");
console.log(
"Not all libraries support all operations. We benchmark what is available."
);
console.log("");
console.log(
"Platform: " + process.platform + " " + os.release() + " " + process.arch
);
console.log(os.cpus()[0]["model"]);
console.log(
"Node version " +
process.versions.node +
", v8 version " +
process.versions.v8
);
console.log();
console.log(
"We proceed with the logical operations generating new bitmaps: "
);
console.log("");
OrBench();
console.log("");
XORBench();
console.log("");
AndBench();
console.log("");
DifferenceBench();
console.log("");
console.log("We benchmark the in-place logical operations: ");
console.log("(Notice how much faster they are.)");
console.log("");
XORInplaceBench();
console.log("");
OrInplaceBench();
console.log("");
AndInplaceBench();
console.log("");
AndNotInplaceBench();
console.log("");
console.log("We benchmark the operations computing the set sizes: ");
console.log("");
CardBench();
console.log("");
OrCardBench();
console.log("");
AndCardBench();
console.log("");
DifferenceCardBench();
console.log("");
console.log("We conclude with other benchmarks: ");
console.log("");
CreateBench();
console.log("");
QueryBench();
console.log("");
ArrayBench();
console.log("");
ForEachBench();
console.log("");
CloneBench();
};
if (require.main === module) {
main();
}