Last active
November 18, 2024 22:46
-
-
Save svidgen/fc49f10e76daa9afc39804e5a07caed8 to your computer and use it in GitHub Desktop.
JavaScript/TypeScript loop performance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Incorrect example from https://x.com/BenjDicken/status/1857449788893286484 | |
*/ | |
function incorrect(baseLoopSize: number) { | |
const array = new Array(baseLoopSize); | |
const innerLoopSize = baseLoopSize * 10; | |
for (let i = 0; i < baseLoopSize; i++) { | |
for (let j = 0; j < innerLoopSize; j++) { | |
array[i] = array[i] + j; | |
} | |
} | |
return array; | |
} | |
/** | |
* Corrected to perform math against `Number` types instead of `NaN` | |
*/ | |
function corrected(baseLoopSize: number) { | |
const array = new Array(baseLoopSize).fill(0); | |
const innerLoopSize = baseLoopSize * 10; | |
for (let i = 0; i < baseLoopSize; i++) { | |
for (let j = 0; j < innerLoopSize; j++) { | |
array[i] = array[i] + j; | |
} | |
} | |
return array; | |
} | |
/** | |
* Optimized to leverage `Uint32` math. | |
*/ | |
function uInts(baseLoopSize: number) { | |
const i = new Uint32Array([0, 0]); | |
const array = new Uint32Array(baseLoopSize).fill(0); | |
const innerLoopSize = baseLoopSize * 10; | |
for (; i[0] < baseLoopSize; i[0]++) { | |
for (; i[1] < innerLoopSize; i[1]++) { | |
array[i[0]] = array[i[0]] + i[1]; | |
} | |
} | |
return array; | |
} | |
function benchmark<T>(f: () => T): { time: number, result: T } { | |
const start = new Date(); | |
const result = f(); | |
const end = new Date(); | |
return { | |
time: end.getTime() - start.getTime(), | |
result | |
} | |
} | |
const BASE_LOOP_SIZE = 2000; | |
console.clear(); | |
const { time: incorrectTime, result: [ resultIncorrect ] } = benchmark(() => incorrect(BASE_LOOP_SIZE)); | |
const { time: correctedTime, result: [ resultCorrected ] } = benchmark(() => corrected(BASE_LOOP_SIZE)); | |
const { time: uIntTime, result: [ resultUInt ] } = benchmark(() => uInts(BASE_LOOP_SIZE)); | |
console.log({ | |
incorrectTime, | |
correctedTime, | |
uIntTime, | |
resultIncorrect, | |
resultCorrected, | |
resultUInt | |
}) | |
/** Outputs: | |
{ | |
"incorrectTime": 147, | |
"correctedTime": 96, | |
"uIntTime": 1, | |
"resultIncorrect": null, | |
"resultCorrected": 199990000, | |
"resultUInt": 199990000 | |
} | |
*/ |
Updated playground shows that the biggest savings actually comes from forcing the loops to use UInt32
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Try it here.