📱 Erkannter Endgerättyp ⛱️ Tag und Nacht. Verbraucht keinen oder einen 🍪. 🖼️ Hintergrund ändern. Verbraucht keinen oder einen 🍪.
🧬 0 Ihre DNS in den Krei.se-DNS-Servern, führt zum Bio-Labor 🍪 1 Anzahl Ihrer gespeicherten Kekse, führt zur Keksdose       
 

🍥 Was sind die schnellsten Loops in 🪩 TypeScript?

Aktuelles Update: Für Objects kann noch schneller sein:

const keys = Object.keys(haploidCells); const len = keys.length;

for (let i = 0; i < len; i++) {
    const key = keys[i];
    const cell = haploidCells[key];
}

Das bringt die loops nochmal von 120ms auf 19ms runter - der Aufwand lohnt sich also wenn man Record oder interfaces nutzt.

Großes Aber: Das gilt für Objekte mit hundertausenden Einträgen - denn jedesmal keys und length setzen hat auch overhead.

Bei 5-20 Einträgen ist der Unterschied schon auf 10% zusammengeschrumpft. Mit random einträgen zw. 5 und 20 (üblich in meinen Fällen) ist der Unterschied nahe Null.

Daher gilt nach wie vor .............

Sooo, nach 3 Tagen Benchmarken und Browser-Wechsel, etc. gibt es eine Lösung die für alle Browser optimal ist und mit 2 Regeln auskommt:

Objects → for...in

Wer seine Typen selbst definiert oder Record<T,T> nutzt wird sich keine prototypes ins Haus holen. Man erhält Key und Value. Noch schneller geht es mit Map<T,T> statt Records

Arrays → for

Man erhält Index und Value - Key gibt es in Arrays ohnehin nicht.

Das wars. Higher-order-functions on arrays machen den Speed nie, wer Array.map() fährt holt sich mind. 10 bis 20x Geschwindigkeitsverlust ins Haus. Das ist total idiotisch. Das Problem ist ja nichtmal dass es mal eben bis zu 50x langsamer ist, das potenziert sich ja ohne Probleme in Frameworks.

Brave und Firefox haben unterschiedliche Werte für Object.keys/values/entries, aber in jedem Fall ist for ... in die schnellste Lösung.

Ich lasse den Beitrag trotzdem so stehen. Wer typsicher und threadsafe schreibt und vom Clienten holt wozu er fähig ist bekommt mittlerweile mit TS und Esbuild Möglichkeit nahezu an der Hardware Code zu schicken - das fetzt schon.

AirBNB Style decisions

Airbnb style guide wird einem erklären, man möge keine Iteratoren verwenden, da sie den state des Objektes ändern (tun Sie überhaupt nicht), aber ehrlichgesagt sind die higher-order-functions trotzdem mit overhead verbunden und wirklich lesbar ist das auch nicht.

Object.prototype

Das benutzt sowieso keine ernstzunehmende Library - systemweit den prototypen zu ergänzen ist völlig unnötig und wenn man das wirklich braucht kann man es mit .enumerable = false auch aus for ... in raushalten.

Benchmarks

Ich finde bei den Benchmarks mega interessant, dass Firefox an sich viel schneller ist - meine Usecases geben das aber nicht immer her, ThreeJS oder 2D-Canvas sind in Chrome doch irgendwie fixer. Es liegt wohl am besseren Multi-Threading in Chrome, d.h. wer selbst die Threads holt dürfte seine Page auf FF und Chrome gleichermaßen fix zum Kunden bekommen.

Wer sauberen Code schreibt und for-loops nutzt, seine Typen klar deklariert ist mit Firefox wahrscheinlich besser dran.

Das ist eigentlich ganz schön so, denn ehrlichgesagt kann man sich Chrome wirklich nur für den Speed ins Haus holen.

Brave

Esbuild TS -> JS. Results are for 10000 Cells, function repeats 1000x

Swarm[CellType] Tests with 10000 Cell Record-Types: 0 ms

Object.keys(): 1111 ms
Object.values(): 2162 ms
Object.entries(): 2741 ms
Object.entries().forEach with key,value and index: 2797 ms
for...in: 1184 ms
for...in counted: 1316 ms
for...of .entries(): 2610 ms
for... over index of keys from Object.keys(): 1113 ms

Array Tests with 100000 entries: 0 ms

array for loop: 53 ms
array for...of: 618 ms
array forEach: 478 ms
array for...in: 5284 ms
array Object.entries(): 11103 ms
array map(): 1264 ms

Records vs Map Tests: 0 ms

obj[key] Lookup random 1000 of 1M: 14062 ms
map.get(key) Lookup random 1000 of 1M: 11271 ms
map for...of - Loop 10000: 150 ms

FF

Esbuild TS -> JS. Results are for 10000 Cells, function repeats 1000x

Swarm[CellType] Tests with 10000 Cell Record-Types:

Object.keys(): 408 ms
Object.values(): 117 ms
Object.entries(): 380 ms
Object.entries().forEach with key,value and index: 380 ms
for...in: 335 ms
for...in counted: 557 ms
for...of .entries(): 377 ms
for... over index of keys from Object.keys(): 394 ms

Array Tests with 100000 entries:

array for loop: 78 ms
array for...of: 672 ms
array forEach: 544 ms
array for...in: 9059 ms
array Object.entries(): 8157 ms
array map(): 1121 ms

Records vs Map Tests:

obj[key] Lookup random 1000 of 1M: 58130 ms
map.get(key) Lookup random 1000 of 1M: 1202 ms
map for...of - Loop 10000: 1042 ms 

Cheat Sheet Decision Finder

TLDR: Use Map<T,T> and Array<T> only lol

Data Structure Recommended
Array of objects (T[]) forEach, for-of, map
Object as dictionary (Record) Object.entries(obj) + for-of
Object that needs array-like behavior Add [Symbol.iterator]
Fastest method for performance-sensitive tasks Classic for loop

Cheat Sheet Speed

keyed Record<string, T> Fastest: for
Construct Example How Index Key Value Use Case
for in for (const key in obj) enumerate properties, numeric ordered, string insertion order, symbol keys are ignored ✅ (obj[key]) Fast ⚠️ includes prototype properties
for of for (const value of obj) expects iterator to enumerate property-values Fast ⚠️ includes prototype properties
.forEach Keys Object.keys(obj).forEach(key => { ... }) These all will return you another object which maps index, keys and values. ↪️ (derived) ✅ (obj[key]) Safe, avoids prototype properties
.forEach Values Object.values(obj).forEach(value => { ... }) ↪️ (derived) Safe, values only
.forEach Keys+Values+Indizes Object.entries(obj).forEach(([key, value], index) => { ... }) ↪️ (derived) Safe
for of .entries for (const [key, value] of Object.entries(obj)) 🚫 in some styles "Clean and readable"
indexed Array<T> Fastest: for
Construct Example how Index Key Value Use Case
for for(let i = 0; i < arr.length; i++) ✅ Yes (arr[i]) Fastest, manual index handling
for of for (const value of arr) uses iterator of object Readable, avoids index
for in for (const index of arr) this will turn your index into strings ✅ (arr[parseFloat(index)]) Don't use that, Slow as f... (10%)
.forEach arr.forEach((value, index) => { ... }) Callback-based, readable, fast
.map, .reduce, .filter, etc. arr.map(value => value * 2) higher-order callbacks ✅ (Returns new array) surprisingly slow 65%
.entries arr.entries() + for (const [index, value] of arr.entries()) Best for index & value