Skip to content

New Workload: JSDOM + d3 #124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
24e8065
adding files
camillobruni Aug 11, 2025
c924f3f
adding more
camillobruni Aug 12, 2025
f5f6127
adding more
camillobruni Aug 12, 2025
2999c23
fix data
camillobruni Aug 12, 2025
8972aa9
re-adding data
camillobruni Aug 12, 2025
8803b58
adding more
camillobruni Aug 12, 2025
9bf6bf2
save
camillobruni Aug 12, 2025
3a318f8
update packages
camillobruni Aug 12, 2025
e1711de
cleanup
camillobruni Aug 12, 2025
492d7f8
fixing rendering
camillobruni Aug 12, 2025
005e47e
adding data license files
camillobruni Aug 12, 2025
b90a7d7
adding dist LICENSE files
camillobruni Aug 12, 2025
2df0ab3
adidng mock text encoding for smaller package
camillobruni Aug 12, 2025
bdac8e0
Merge branch 'main' of github.com:camillobruni/JetStream into 2025-08…
camillobruni Aug 12, 2025
b4e8623
update benchmark
camillobruni Aug 12, 2025
47d0a6c
rename
camillobruni Aug 12, 2025
21e164b
remove unused filed3-startup/build/loader.mjs
camillobruni Aug 12, 2025
3b9ec49
fix tags
camillobruni Aug 13, 2025
c8aee7f
Merge branch 'main' into 2025-08-11_d3
camillobruni Aug 14, 2025
d83c139
move files
camillobruni Aug 14, 2025
7d69135
adding back files
camillobruni Aug 14, 2025
5eef7e3
merge main and warm up hash
camillobruni Aug 14, 2025
2b8b532
Merge branch 'main' into 2025-08-11_d3
camillobruni Aug 14, 2025
1bd445e
Merge branch 'main' into 2025-08-11_d3
camillobruni Aug 15, 2025
3dbb0d1
adding README
camillobruni Aug 15, 2025
26fd058
adding readme
camillobruni Aug 15, 2025
3203291
update
camillobruni Aug 15, 2025
695a6b7
re-adding files
camillobruni Aug 15, 2025
d945426
Merge branch 'main' into 2025-08-11_d3
camillobruni Aug 15, 2025
c3457a8
use new JetStream global
camillobruni Aug 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions JetStreamDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -2024,6 +2024,22 @@ let BENCHMARKS = [
],
tags: ["Default", "Proxy"],
}),
new AsyncBenchmark({
name: "jsdom-d3-startup",
files: [
"./jsdom-d3-startup/benchmark.js",
],
preload: {
// Unminified sources for profiling.
// SOURCE_CODE: "./jsdom-d3-startup/dist/bundle.js",
SOURCE_CODE: "./jsdom-d3-startup/dist/bundle.min.js",
US_DATA: "./jsdom-d3-startup/data/counties-albers-10m.json",
AIRPORTS: "./jsdom-d3-startup/data/airports.csv",
},
tags: ["d3", "startup", "jsdom"],
iterations: 10,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These iterations are pretty fast, it seems like we could do more like 15-20.

worstCaseCount: 4,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a high worstCaseCount for the number of iterations.

}),
// Class fields
new DefaultBenchmark({
name: "raytrace-public-class-fields",
Expand Down
25 changes: 25 additions & 0 deletions jsdom-d3-startup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# JSDOM D3 Startup Benchmark

The benchmark reads airport and US geography data, then uses D3 to create a Voronoi diagram of the airports overlaid on a map of the US.
It uses jsdom to simulate a browser environment for D3 to render to an SVG element.

## JetStream integration
- We use a custom `./build/build/cache-buster-comment-plugin.cjs` which injects a known comment into every function in the bundle
- The JetStream benchmark replaces these comments with a unique string per iteration
- Each benchmark iteration includes parse and top-level eval time

## Setup
```bash
# Install node deps from package-lock.json
npm ci;
# Bundle sources to dist/*.
npm run build
# Use build:dev for non-minified sources.
npm run build:dev
```

# Testing
```bash
# Run the basic node benchmark implementation for development.
npm run test
```
21 changes: 21 additions & 0 deletions jsdom-d3-startup/benchmark-node.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { performance } = require('perf_hooks');
const fs = require('fs');
const d3 = require('d3');

async function main() {
const { runTest } = await import('./src/test.mjs');

const usData = JSON.parse(fs.readFileSync('./data/counties-albers-10m.json', 'utf-8'));
const airportsData = fs.readFileSync('.//data/airports.csv', 'utf-8');

const startTime = performance.now();

const svg = await runTest(airportsData, usData);

const endTime = performance.now();

// console.log(svg); // The SVG output
console.log(`Execution time: ${endTime - startTime} ms`);
}

main();
83 changes: 83 additions & 0 deletions jsdom-d3-startup/benchmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Load D3 and data loading utilities for d8

function quickHash(str) {
let hash = 5381;
let i = str.length;
while (i > 0) {
hash = (hash * 33) ^ (str.charCodeAt(i) | 0);
i -= 919;
}
return hash | 0;
}

const CACHE_BUST_COMMENT = "/*ThouShaltNotCache*/";
const CACHE_BUST_COMMENT_RE = new RegExp(`\n${RegExp.escape(CACHE_BUST_COMMENT)}\n`, "g");

const EXPECTED_LAST_RESULT_LENGTH = 691366;
const EXPECTED_LAST_RESULT_HASH = 144487595;

globalThis.clearTimeout = () => { };

class Benchmark {
measureStartup = true;

sourceCode = "";
sourceHash = 0;
iterationSourceCodes = [];
lastResult = "";
currentIteration = 0;

constructor(iterations) {
this.iterations = iterations;
}

assert(test, message) {
if (!test) {
throw new Error(message);
}
}

async init(verbose = 0) {
this.sourceCode = await JetStream.getString(JetStream.preload.SOURCE_CODE);
for (let i = 0; i < this.iterations; i++)
this.iterationSourceCodes[i] = this.prepareCode(i);

this.airportsCsvString = (await JetStream.getString(JetStream.preload.AIRPORTS));
this.assert(this.airportsCsvString.length == 145493, `Expected this.airportsCsvString.length to be 141490 but got ${this.airportsCsvString.length}`);
this.usDataJsonString = await JetStream.getString(JetStream.preload.US_DATA);
this.assert(this.usDataJsonString.length == 2880996, `Expected this.usData.length to be 2880996 but got ${this.usDataJsonString.length}`);
this.usData = JSON.parse(this.usDataJsonString);
}

prepareCode(iteration) {
if (!this.measureStartup)
return this.sourceCode;
// Alter the code per iteration to prevent caching.
const iterationSourceCode = this.sourceCode.replaceAll(CACHE_BUST_COMMENT_RE, `/*${iteration}*/`);
// Warm up hash function.
this.sourceHash = quickHash(iterationSourceCode);
return iterationSourceCode;
}

runIteration() {
let iterationSourceCode = this.iterationSourceCodes[this.currentIteration];
if (!iterationSourceCode)
throw new Error(`Could not find source for iteration ${this.currentIteration}`);
// Module in sourceCode it assigned to the ReactRenderTest variable.
let D3Test;
eval(iterationSourceCode);
const html = D3Test.runTest(this.airportsCsvString, this.usData);
this.lastResult = {
html,
htmlHash: quickHash(html),
};
this.currentIteration++;
}

validate() {
if (this.lastResult.html.length != EXPECTED_LAST_RESULT_LENGTH)
throw new Error(`Expected this.lastResult.html.length to be ${EXPECTED_LAST_RESULT_LENGTH} but got ${this.lastResult.length}`);
if (this.lastResult.htmlHash != EXPECTED_LAST_RESULT_HASH)
throw new Error(`Expected this.lastResult.htmlHash to be ${EXPECTED_LAST_RESULT_HASH} but got ${this.lastResult.htmlHash}`);
}
}
29 changes: 29 additions & 0 deletions jsdom-d3-startup/build/cache-buster-comment-plugin.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Babel plugin that adds CACHE_BUST_COMMENT to every function body.
const CACHE_BUST_COMMENT = "ThouShaltNotCache";


module.exports = function({ types: t }) {
return {
visitor: {
Function(path) {
const bodyPath = path.get("body");
// Handle arrow functions: () => "value"
// Convert them to block statements: () => { return "value"; }
if (!bodyPath.isBlockStatement()) {
const newBody = t.blockStatement([t.returnStatement(bodyPath.node)]);
path.set("body", newBody);
}

// Handle empty function bodies: function foo() {}
// Add an empty statement so we have a first node to attach the comment to.
if (path.get("body.body").length === 0) {
path.get("body").pushContainer("body", t.emptyStatement());
}

const firstNode = path.node.body.body[0];
t.addComment(firstNode, "leading", CACHE_BUST_COMMENT);

}
},
};
};
Loading
Loading