Skip to content

Instantly share code, notes, and snippets.

@svidgen
Last active November 18, 2024 22:46
Show Gist options
  • Save svidgen/fc49f10e76daa9afc39804e5a07caed8 to your computer and use it in GitHub Desktop.
Save svidgen/fc49f10e76daa9afc39804e5a07caed8 to your computer and use it in GitHub Desktop.
JavaScript/TypeScript loop performance
/**
* 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
}
*/
@svidgen
Copy link
Author

svidgen commented Nov 15, 2024

Try it here.

@svidgen
Copy link
Author

svidgen commented Nov 15, 2024

The Uint32 version is limited to 32 bit ints, naturally. But, this can be adapted to use BigInt's as well. E.g.,

image

Compiled an run just the BigInt version on the CLI at 10k x 100k (BASE_LOOP_SIZE = 10_000).

image

@svidgen
Copy link
Author

svidgen commented Nov 18, 2024

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