forked from foxglove/mcap
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Typescript: make parsing faster (foxglove#1185)
### Changelog - Typescript: improved record parsing time. ### Docs None. ### Description I was trying to port some of the improvements from foxglove#1168 to typescript, and noticed that this change to `parse.ts` improves reading speed significantly with no additional API changes. This change switches from `new Uint8Array(fromBuffer.slice(start, end)` to `new Uint8Array(fromBuffer, start, length).slice()`. These both have the same effect of producing a new Uint8Array with a copy of some part of `fromBuffer`. I also rewrote the typescript benchmark utility to remove the dependency on `benny`, so that I could gather memory statistics as part of the benchmark. #### Before: ``` (python-sNIFi2pF) j@192-168-1-105 benchmarks % yarn bench --suite reader Running 'reader' suite McapStreamReader 1.29±0.02 op/s Heap Used: 259.38±4.95 MB/op Heap Total: 288.73±3.47 MB/op ArrayBuffers: 214.31±1.60 MB/op McapIndexedReader 0.95±0.01 op/s Heap Used: 248.20±21.02 MB/op Heap Total: 281.67±20.46 MB/op ArrayBuffers: 98.33±5.42 MB/op McapIndexedReader_reverse 1.00±0.02 op/s Heap Used: 260.77±17.31 MB/op Heap Total: 295.44±17.31 MB/op ArrayBuffers: 102.49±4.62 MB/op ``` #### After ``` (python-sNIFi2pF) j@192-168-1-105 benchmarks % yarn bench --suite reader Running 'reader' suite McapStreamReader 3.04±0.02 op/s Heap Used: 261.58±8.20 MB/op Heap Total: 289.82±6.71 MB/op ArrayBuffers: 214.18±1.60 MB/op McapIndexedReader 1.83±0.01 op/s Heap Used: 281.22±4.06 MB/op Heap Total: 317.18±2.63 MB/op ArrayBuffers: 107.06±0.00 MB/op McapIndexedReader_reverse 1.86±0.00 op/s Heap Used: 278.37±2.01 MB/op Heap Total: 313.65±0.93 MB/op ArrayBuffers: 107.06±0.00 MB/op ```
- Loading branch information
Showing
6 changed files
with
257 additions
and
259 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { hrtime, memoryUsage } from "process"; | ||
|
||
const COUNT = 5; | ||
|
||
type BenchmarkResult = | ||
| { | ||
name: string; | ||
gcExposed: true; | ||
samples: { | ||
duration: bigint; | ||
memoryUsage: { | ||
rss: number; | ||
heapTotal: number; | ||
heapUsed: number; | ||
external: number; | ||
arrayBuffers: number; | ||
}; | ||
}[]; | ||
} | ||
| { | ||
name: string; | ||
gcExposed: false; | ||
samples: { | ||
duration: bigint; | ||
}[]; | ||
}; | ||
|
||
/** runs a benchmark and logs statistics about runtime and memory usage afterward. | ||
* | ||
* @param name A name for the benchmark. | ||
* @param run a routine that runs the benchmark code. | ||
*/ | ||
export async function runBenchmark(name: string, run: () => Promise<void>): Promise<void> { | ||
let result: BenchmarkResult; | ||
if (global.gc != undefined) { | ||
result = { | ||
name, | ||
gcExposed: true, | ||
samples: [], | ||
}; | ||
for (let i = 0; i < COUNT; i++) { | ||
global.gc(); | ||
const before = hrtime.bigint(); | ||
await run(); | ||
const after = hrtime.bigint(); | ||
result.samples.push({ | ||
duration: after - before, | ||
memoryUsage: memoryUsage(), | ||
}); | ||
} | ||
} else { | ||
result = { | ||
name, | ||
gcExposed: false, | ||
samples: [], | ||
}; | ||
for (let i = 0; i < COUNT; i++) { | ||
const before = hrtime.bigint(); | ||
await run(); | ||
const after = hrtime.bigint(); | ||
result.samples.push({ duration: after - before }); | ||
} | ||
} | ||
printStats(result); | ||
} | ||
|
||
function humanReadableStatistics(values: number[], unit: string): string { | ||
const count = values.length; | ||
if (count < 1) { | ||
return "(No samples)"; | ||
} | ||
if (count < 2) { | ||
return `${values[0]} ${unit}`; | ||
} | ||
const mean = values.reduce((a, b) => a + b, 0) / count; | ||
const stdDev = Math.sqrt( | ||
values.map((value) => (mean - value) ** 2).reduce((a, b) => a + b, 0) / (count - 1), | ||
); | ||
const stdErr = stdDev / Math.sqrt(count); | ||
return `${mean.toFixed(2)}±${stdErr.toFixed(2)} ${unit}`; | ||
} | ||
|
||
function printStats(result: BenchmarkResult) { | ||
let memoryResult = "(use --expose-gc to gather memory statistics)"; | ||
if (result.gcExposed) { | ||
const used = humanReadableStatistics( | ||
result.samples.map((sample) => sample.memoryUsage.heapUsed / 2 ** 20), | ||
"MB/op", | ||
); | ||
const total = humanReadableStatistics( | ||
result.samples.map((sample) => sample.memoryUsage.heapTotal / 2 ** 20), | ||
"MB/op", | ||
); | ||
const arrayBuffers = humanReadableStatistics( | ||
result.samples.map((sample) => sample.memoryUsage.arrayBuffers / 2 ** 20), | ||
"MB/op", | ||
); | ||
memoryResult = `Heap Used: ${used}\tHeap Total: ${total}\tArrayBuffers: ${arrayBuffers}`; | ||
} | ||
const name = result.name; | ||
const timeStat = humanReadableStatistics( | ||
result.samples.map((r) => 1 / (Number(r.duration) / 1e9)), | ||
"op/s", | ||
); | ||
console.log(name); | ||
console.log(`\t${timeStat}\t${memoryResult}`); | ||
} |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Oops, something went wrong.