Skip to content

Instantly share code, notes, and snippets.

@katyo
Last active February 23, 2017 22:15
Show Gist options
  • Save katyo/4c7c469c28b5976e16ca87d89bac382a to your computer and use it in GitHub Desktop.
Save katyo/4c7c469c28b5976e16ca87d89bac382a to your computer and use it in GitHub Desktop.
Comparison of js string testing techniques
var allAttrs = ["abbr","accept","accept-charset","accesskey","action","alt","async","autocomplete","autocomplete","autofocus","autoplay","border","challenge","charset","charset","checked","cite","class","cols","colspan","content","contenteditable","controls","coords","crossorigin","data","datetime","datetime","default","defer","dir","dir","dirname","disabled","download","enctype","for","for","form","formaction","formenctype","formmethod","formnovalidate","formtarget","headers","height","hidden","high","href","href","href","hreflang","http-equiv","id","ismap","keytype","kind","label","lang","list","loop","low","manifest","max","max","maxlength","media","mediagroup","method","min","min","minlength","multiple","muted","name","name","name","name","name","name","novalidate","optimum","pattern","placeholder","poster","preload","readonly","rel","required","reversed","rows","rowspan","sandbox","spellcheck","scope","selected","shape","size","sizes","span","src","srcdoc","srclang","start","step","style","tabindex","target","target","target","title","title","title","title","title","translate","type","type","type","type","type","typemustmatch","usemap","value","value","value","value","value","value","width","wrap"];
var booleanAttrsArray = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare", "default", "defaultchecked", "defaultmuted", "defaultselected", "defer", "disabled", "draggable", "enabled", "formnovalidate", "hidden", "indeterminate", "inert", "ismap", "itemscope", "loop", "multiple", "muted", "nohref", "noresize", "noshade", "novalidate", "nowrap", "open", "pauseonexit", "readonly", "required", "reversed", "scoped", "seamless", "selected", "sortable", "spellcheck", "translate", "truespeed", "typemustmatch", "visible"];
var booleanAttrsHash = {
allowfullscreen: 1,
async: 1,
autofocus: 1,
autoplay: 1,
checked: 1,
compact: 1,
controls: 1,
declare: 1,
default: 1,
defaultchecked: 1,
defaultmuted: 1,
defaultselected: 1,
defer: 1,
disabled: 1,
draggable: 1,
enabled: 1,
formnovalidate: 1,
hidden: 1,
indeterminate: 1,
inert: 1,
ismap: 1,
itemscope: 1,
loop: 1,
multiple: 1,
muted: 1,
nohref: 1,
noresize: 1,
noshade: 1,
novalidate: 1,
nowrap: 1,
open: 1,
pauseonexit: 1,
readonly: 1,
required: 1,
reversed: 1,
scoped: 1,
seamless: 1,
selected: 1,
sortable: 1,
spellcheck: 1,
translate: 1,
truespeed: 1,
typemustmatch: 1,
visible: 1
};
var booleanAttrsRegex = /^(?:a(?:llowfullscreen|sync|uto(?:focus|play))|c(?:hecked|o(?:mpact|ntrols))|d(?:e(?:clare|f(?:ault(?:(?:check|(?:mu|selec)t)ed)?|er))|isabled|raggable)|enabled|formnovalidate|hidden|i(?:n(?:determinate|ert)|smap|temscope)|loop|mu(?:ltiple|ted)|no(?:href|resize|shade|validate|wrap)|open|pauseonexit|re(?:adonly|(?:quir|vers)ed)|s(?:coped|e(?:amless|lected)|ortable|pellcheck)|t(?:r(?:anslate|uespeed)|ypemustmatch)|visible)$/;
var booleanAttrsSet = new Set(booleanAttrsArray);
var n = 10e3;
function bench(t, f) {
var s = Date.now();
for (var i = 0; i < n; i++) {
f();
}
var e = Date.now() - s;
console.log(t, (n/1000) + "k", e, "mS");
}
bench("impelling", function() {
var res;
for(var i = 0; i < allAttrs.length; i++) {
res = allAttrs.indexOf(allAttrs[i]);
}
});
bench("array search", function() {
var res;
for(var i = 0; i < allAttrs.length; i++) {
res = booleanAttrsArray.indexOf(allAttrs[i]);
}
});
bench("hash lookup", function() {
var res;
for(var i = 0; i < allAttrs.length; i++) {
res = booleanAttrsHash[allAttrs[i]];
}
});
bench("regex match", function() {
var res;
for(var i = 0; i < allAttrs.length; i++) {
res = booleanAttrsRegex.test(allAttrs[i]);
}
});
bench("set lookup", function() {
var res;
for(var i = 0; i < allAttrs.length; i++) {
res = booleanAttrsSet.has(allAttrs[i]);
}
});
@katyo
Copy link
Author

katyo commented Jan 17, 2017

$ node bench.js

Node: v4.3.1

impelling 100k 3474 mS
array search 100k 2016 mS
hash lookup 100k 2364 mS
regex match 100k 466 mS
set lookup 100k 267 mS

@katyo
Copy link
Author

katyo commented Jan 17, 2017

Firefox: 45.6.0

impelling 10k 897 mS
array search 10k 766 mS
hash lookup 10k 776 mS
regex match 10k 663 mS
set lookup 10k 705 mS

Chromium: 55.0.2883.75

impelling 10k 411 mS
array search 10k 218 mS
hash lookup 10k 194 mS
regex match 10k 86 mS
set lookup 10k 27 mS

Node: v4.3.1

impelling 10k 355 mS
array search 10k 203 mS
hash lookup 10k 235 mS
regex match 10k 50 mS
set lookup 10k 29 mS

@YarnSphere
Copy link

This is quite interesting.
Just out of curiosity, could you add a test using Set?

@jorgebucaran
Copy link

How is a regex faster than a hash lookup? 🤔

And, what about the bundle size? Does it increase / decrease?

@katyo
Copy link
Author

katyo commented Jan 18, 2017

It may looks surprising, but testing string using regex really faster than hash(?)/array lookup. At least all mainstream JS engines, which I tested, confirm this. But real increasing of speed is differs from engine to engine. Look at my results above or you can run test code yourself.

ECMAScript Environment Speed increase comparing with hash lookup
Firefox: 45.6.0 15%
Chromium: 55.0.2883.75 56%
Node: v4.3.1 80%

Of course, the bundle size also decrease when we use regex. It's so because regex is short itself, but optimized regex is yet more short than array with strings or object with keys.

@katyo
Copy link
Author

katyo commented Jan 18, 2017

@nunocastromartins: I added test using Set too. The sets works really much faster in v8-based environments (Node, Chromium).

@mightyiam
Copy link

Finding a real use case for something less commonly used it exciting.

@katyo
Copy link
Author

katyo commented Jan 19, 2017

Finding a techniques for optimization of hot code path is exciting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment