Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 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
c62ee26
Merge branch 'main' into 2025-08-11_d3
camillobruni Aug 26, 2025
5b9f6ea
convert more
camillobruni Aug 26, 2025
86c457a
Merge branch 'main' into 2025-08-11_d3
camillobruni Aug 26, 2025
7094780
update and use main StartupBenchmark
camillobruni Aug 26, 2025
321532a
Merge branch 'main' into 2025-08-11_d3
camillobruni Aug 26, 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
17 changes: 17 additions & 0 deletions JetStreamDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -2026,6 +2026,23 @@ let BENCHMARKS = [
],
tags: ["Default", "Proxy"],
}),
new AsyncBenchmark({
name: "jsdom-d3-startup",
files: [
"./startup-helper/StartupBenchmark.js",
"./jsdom-d3-startup/benchmark.js",
],
preload: {
// Unminified sources for profiling.
// BUNDLE: "./jsdom-d3-startup/dist/bundle.js",
BUNDLE: "./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
```
20 changes: 20 additions & 0 deletions jsdom-d3-startup/benchmark-node.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { performance } from 'perf_hooks';
import fs from 'fs';
import * as d3 from 'd3';
import { runTest } from './src/test.mjs';

async function main() {
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();
75 changes: 75 additions & 0 deletions jsdom-d3-startup/benchmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2025 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// Load D3 and data loading utilities for d8

const EXPECTED_LAST_RESULT_LENGTH = 691366;
const EXPECTED_LAST_RESULT_HASH = 144487595;

globalThis.clearTimeout = () => { };

class Benchmark extends StartupBenchmark {
lastResult;
totalHash = 0xdeadbeef;
currentIteration = 0;

constructor(iterations) {
super({
iterationCount: iterations,
expectedCacheCommentCount: 10028,
sourceCodeReuseCount: 2,
});
}

async init() {
await super.init();
this.airportsCsvString = (await JetStream.getString(JetStream.preload.AIRPORTS));
console.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);
console.assert(this.usDataJsonString.length == 2880996, `Expected this.usData.length to be 2880996 but got ${this.usDataJsonString.length}`);
this.usData = JSON.parse(this.usDataJsonString);
}

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);
const htmlHash = this.quickHash(html);
this.lastResult = { html, htmlHash };
this.totalHash ^= this.lastResult.htmlHash;
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