diff --git a/docs/tests/hnbigset-benchmark.html b/docs/tests/hnbigset-benchmark.html
new file mode 100644
index 000000000..112b6c120
--- /dev/null
+++ b/docs/tests/hnbigset-benchmark.html
@@ -0,0 +1,271 @@
+
+
+
+
+
+
+
+Benchmark of large hostname-lookup from small to large set: Set, HNTrie
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/tests/hnset-benchmark.html b/docs/tests/hnset-benchmark.html
index db97a1e18..43cd91881 100644
--- a/docs/tests/hnset-benchmark.html
+++ b/docs/tests/hnset-benchmark.html
@@ -5,7 +5,7 @@
-Benchmark of hostname-lookup data structures: Set, RegExp, HNTrie
+Benchmark of hostname-lookup from small to medium set: Set, RegExp, HNTrie
@@ -17,6 +17,7 @@
+
@@ -202,6 +203,27 @@ var trieBasedDictTestWASM = function(haystack, needle) {
/******************************************************************************/
+const hnBigTrieJS = new HNTrieContainer();
+const hnBigTrieWASM = new HNTrieContainer();
+
+const bigtrieBasedDictCreateJS = function(domainOpt) {
+ return hnBigTrieJS.fromIterable(domainOpt.split('|'), 'addJS');
+}
+
+const bigtrieBasedDictTestJS = function(haystack, needle) {
+ return haystack.matchesJS(needle);
+};
+
+const bigtrieBasedDictCreateWASM = function(domainOpt) {
+ return hnBigTrieWASM.fromIterable(domainOpt.split('|'), 'addWASM');
+}
+
+const bigtrieBasedDictTestWASM = function(haystack, needle) {
+ return haystack.matchesWASM(needle);
+};
+
+/******************************************************************************/
+
const gBenchmarks = [ null ];
let gWhich;
@@ -258,19 +280,23 @@ function initBenchmarks() {
var bms = new Benchmark.Suite();
bms
- .add(' - Set-based', function() {
+ .add(' - Set-based', function() {
createDict(setBasedDictCreate);
})
- .add(' - Regex-based', function() {
+ .add(' - Regex-based', function() {
createDict(regexBasedDictCreate);
})
- .add(' - Trie-based (1st-gen)', function() {
+ .add(' - Trie-based (1st-gen)', function() {
createDict(oldTrieBasedDictCreate);
})
- .add(' - Trie-based (2nd-gen)', function() {
+ .add(' - Trie-based (2nd-gen)', function() {
hnTrieManager.reset();
createDict(trieBasedDictCreate);
})
+ .add(' - Trie-based JS (3rd-gen)', function() {
+ hnBigTrieJS.reset();
+ createDict(bigtrieBasedDictCreateJS);
+ })
.on('start', function() {
dicts = [];
stdout(gWhich, '');
@@ -281,6 +307,13 @@ function initBenchmarks() {
})
.on('complete', exitBenchmark);
+ if ( hnBigTrieWASM.addWASM !== null ) {
+ bms.add(' - Trie-based WASM (3rd-gen)', function() {
+ hnBigTrieWASM.reset();
+ createDict(bigtrieBasedDictCreateWASM);
+ })
+ }
+
return bms;
})());
@@ -294,6 +327,7 @@ function initBenchmarks() {
let regexDicts;
let oldTrieDicts;
let newTrieDicts;
+ let bigTrieDicts;
let results;
const lookupDict = function(dicts, fn) {
@@ -315,9 +349,6 @@ function initBenchmarks() {
.add(' - Trie-based (1st-gen)', function() {
lookupDict(oldTrieDicts, oldTrieBasedDictTest);
})
- .add(' - Trie-based JS (2nd-gen)', function() {
- lookupDict(newTrieDicts, trieBasedDictTest);
- })
.on('start', function() {
for ( let i = 0; i < lookupCount; i++ ) {
needles[i] = randomNeedle();
@@ -326,6 +357,7 @@ function initBenchmarks() {
regexDicts = [];
oldTrieDicts = []
newTrieDicts = []
+ bigTrieDicts = []
results = [];
hnTrieManager.reset();
for ( const domainOpt of domainOpts ) {
@@ -333,6 +365,7 @@ function initBenchmarks() {
regexDicts.push(regexBasedDictCreate(domainOpt));
oldTrieDicts.push(oldTrieBasedDictCreate(domainOpt));
newTrieDicts.push(trieBasedDictCreate(domainOpt));
+ bigTrieDicts.push(bigtrieBasedDictCreateJS(domainOpt));
}
stdout(gWhich, '');
@@ -347,11 +380,22 @@ function initBenchmarks() {
exitBenchmark();
});
+ bms.add(' - Trie-based JS (2nd-gen)', function() {
+ lookupDict(newTrieDicts, trieBasedDictTest);
+ })
if ( hnTrieManager.matchesWASM !== null ) {
bms.add(' - Trie-based WASM (2nd-gen)', function() {
lookupDict(newTrieDicts, trieBasedDictTestWASM);
})
}
+ bms.add(' - Trie-based JS (3rd-gen)', function() {
+ lookupDict(newTrieDicts, bigtrieBasedDictTestJS);
+ })
+ if ( hnBigTrieWASM.matchesWASM !== null ) {
+ bms.add(' - Trie-based WASM (3rd-gen)', function() {
+ lookupDict(bigTrieDicts, bigtrieBasedDictTestWASM);
+ })
+ }
return bms;
})());
@@ -361,6 +405,8 @@ function initBenchmarks() {
Promise.all([
hnTrieManager.readyToUse(),
+ hnBigTrieJS.readyToUse(),
+ hnBigTrieWASM.readyToUse(),
]).then(( ) => {
initBenchmarks();
});
diff --git a/docs/tests/hntrie-test.html b/docs/tests/hntrie-test.html
index 39d99dfae..de763ef82 100644
--- a/docs/tests/hntrie-test.html
+++ b/docs/tests/hntrie-test.html
@@ -5,10 +5,9 @@
-HNTrie test
+HNTrieContainer test
-
@@ -35,7 +34,45 @@ const stdout = function(s) {
/******************************************************************************/
-const testFlavor = function(hostnames, name, matchesFn, hit, miss) {
+// Dictionary of hostnames
+//
+const FilterHostnameDict = function(hostnames) {
+ this.h = ''; // short-lived register
+ this.dict = new Set();
+ if ( hostnames !== undefined ) {
+ this.fromIterable(hostnames);
+ }
+};
+
+FilterHostnameDict.prototype = {
+ add: function(hn) {
+ if ( this.dict.has(hn) ) { return false; }
+ this.dict.add(hn);
+ return true;
+ },
+ fromIterable: function(hostnames) {
+ for ( let hn of hostnames ) {
+ this.add(hn);
+ }
+ return this;
+ },
+ matches: function(needle) {
+ while ( this.dict.has(needle) === false ) {
+ const pos = needle.indexOf('.');
+ if ( pos === -1 ) {
+ this.h = '';
+ return false;
+ }
+ needle = needle.slice(pos + 1);
+ }
+ this.h = needle;
+ return true;
+ },
+};
+
+/******************************************************************************/
+
+const testFlavor = function(hostnames, name, matchesFn, hitFn) {
stdout('\xA0');
stdout('Testing ' + name + '...');
@@ -44,25 +81,25 @@ const testFlavor = function(hostnames, name, matchesFn, hit, miss) {
for ( let i = 0; i < hostnames.length; i++ ) {
// Exact hits
let needle = hostnames[i];
- if ( matchesFn(needle) !== hit ) {
+ if ( hitFn(matchesFn(needle)) === false ) {
stdout('Exact hits failed: ' + needle);
}
// Subdomain hits
needle = createRandomLabel() + '.' + hostnames[i];
- if ( matchesFn(needle) !== hit ) {
+ if ( hitFn(matchesFn(needle)) === false ) {
stdout('Subdomain hits failed: ' + needle);
}
// Misses batch 1
needle = createRandomLabel() + '.com';
- if ( matchesFn(needle) !== miss ) {
+ if ( hitFn(matchesFn(needle)) !== false ) {
stdout('Misses batch 1: ' + needle);
}
// Misses batch 2
needle = hostnames[i] + '.' + createRandomLabel();
- if ( matchesFn(needle) !== miss ) {
+ if ( hitFn(matchesFn(needle)) !== false ) {
stdout('Misses batch 2: ' + needle);
}
@@ -71,7 +108,7 @@ const testFlavor = function(hostnames, name, matchesFn, hit, miss) {
let pos = needle.lastIndexOf('.');
if ( pos !== -1 ) {
needle = needle.slice(0, pos) + needle.slice(pos + 1);
- if ( matchesFn(needle) !== miss ) {
+ if ( hitFn(matchesFn(needle)) !== false ) {
stdout('Misses batch 3: ' + needle);
}
}
@@ -87,19 +124,98 @@ const testFlavor = function(hostnames, name, matchesFn, hit, miss) {
);
};
-hnTrieManager.readyToUse().then(( ) => {
- const oldTrie = HNTrieBuilder.fromIterable(hostnamePool);
- const theTrie = hnTrieManager.fromIterable(hostnamePool);
+const hnBigTrieJS = new HNTrieContainer();
+const hnBigTrieWASM = new HNTrieContainer();
+const hnBigTrieUnserialized = new HNTrieContainer();
+
+Promise.all([
+ hnBigTrieJS.readyToUse(),
+ hnBigTrieWASM.readyToUse()
+]).then(( ) => {
+ let t0 = performance.now();
+ const theSet = new FilterHostnameDict(hostnamePool);
+ let t1 = performance.now();
+ stdout('\xA0');
+ stdout(
+ 'Set creation completed in ' +
+ (t1 - t0).toFixed(2) + ' ms'
+ );
+
+ t0 = performance.now();
+ const theTrieJS = hnBigTrieJS.fromIterable(hostnamePool, 'addJS');
+ hnBigTrieJS.optimize();
+ t1 = performance.now();
+ stdout('\xA0');
+ stdout(
+ 'HNTrieContainer creation (JS) completed in ' +
+ (t1 - t0).toFixed(2) + ' ms'
+ );
+
+ let theTrieWASM;
+ if ( hnBigTrieWASM.addWASM instanceof Function ) {
+ t0 = performance.now();
+ theTrieWASM = hnBigTrieWASM.fromIterable(hostnamePool, 'addWASM');
+ hnBigTrieWASM.optimize();
+ t1 = performance.now();
+ stdout('\xA0');
+ stdout(
+ 'HNTrieContainer creation (WASM) completed in ' +
+ (t1 - t0).toFixed(2) + ' ms'
+ );
+
+ const bufJS = theTrieJS.container.buf;
+ const bufWASM = theTrieWASM.container.buf;
+ for ( let i = 0; i < bufJS.length; i++ ) {
+ if ( bufJS[i] !== bufWASM[i] ) {
+ stdout('theTrieWASM failure at index ' + i);
+ break;
+ }
+ }
+ }
+
+ let selfie = hnBigTrieJS.serialize();
+ t0 = performance.now();
+ hnBigTrieUnserialized.unserialize(selfie);
+ const theTrieUnserialized = hnBigTrieUnserialized.createOne(hnBigTrieJS.compileOne(theTrieJS));
+ t1 = performance.now();
+ stdout('\xA0');
+ stdout(
+ 'HNTrieContainer creation (unserialized) completed in ' +
+ (t1 - t0).toFixed(2) + ' ms'
+ );
+ selfie = undefined;
+
document.getElementById('test').addEventListener('click', ( ) => {
let parent = document.getElementById('stdout');
while ( parent.childElementCount !== 0 ) {
parent.removeChild(parent.firstChild);
}
- testFlavor(hostnamePool, 'Old Trie (JS)', oldTrie.matches.bind(oldTrie), true, false);
- testFlavor(hostnamePool, 'New Trie (JS)', theTrie.matchesJS.bind(theTrie), 1, 0);
- if ( hnTrieManager.matchesWASM instanceof Function ) {
- testFlavor(hostnamePool, 'New Trie (WASM)', theTrie.matchesWASM.bind(theTrie), 1, 0);
+ testFlavor(
+ hostnamePool,
+ 'Set (JS)',
+ theSet.matches.bind(theSet),
+ r => r
+ );
+ testFlavor(
+ hostnamePool,
+ 'HNTrieContainer (JS)',
+ theTrieJS.matchesJS.bind(theTrieJS),
+ r => r >= 0
+ );
+ if ( theTrieWASM !== undefined ) {
+ testFlavor(
+ hostnamePool,
+ 'HNTrieContainer (WASM)',
+ theTrieWASM.matchesWASM.bind(theTrieWASM),
+ r => r >= 0
+ );
}
+ testFlavor(
+ hostnamePool,
+ 'HNTrieContainer (unserialized)',
+ theTrieUnserialized.matchesJS.bind(theTrieUnserialized),
+ r => r >= 0
+ );
});
});
diff --git a/docs/tests/index.html b/docs/tests/index.html
index 347ec9eee..125716138 100644
--- a/docs/tests/index.html
+++ b/docs/tests/index.html
@@ -10,7 +10,8 @@
Some of the pages below are hosted on raw.githack.com in order to ensure some of the secondary resources can be properly loaded (specifically, the WebAssembly modules, as they require to be loaded using same-origin policy).