Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
90 changes: 90 additions & 0 deletions __tests__/demos/perf/benchmark-panel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
export interface BenchmarkResult {
[key: string]: string | number;
}

export class BenchmarkPanel {
private container: HTMLDivElement;
private title: string;
private configInfo: string | null = null;

constructor(title: string) {
this.title = title;

// Create result container on page
this.container = document.createElement('div');
this.container.style.position = 'absolute';
this.container.style.bottom = '10px';
this.container.style.left = '10px';
this.container.style.backgroundColor = 'rgba(0,0,0,0.8)';
this.container.style.color = 'white';
this.container.style.padding = '10px';
this.container.style.borderRadius = '4px';
this.container.style.fontFamily = 'monospace';
this.container.style.fontSize = '12px';
this.container.style.zIndex = '1000';
this.container.style.maxWidth = '500px';
this.container.id = 'benchmark-results-panel';

document.body.appendChild(this.container);

// Show initial status
this.showRunningStatus();
}

showRunningStatus(configInfo?: string) {
// Store config info for later use in updateResultsDisplay
this.configInfo = configInfo || null;

this.container.innerHTML = `
<h3>${this.title}</h3>
${configInfo ? `<div>${configInfo}</div><hr />` : ''}
<div>Running benchmark tests... Please wait.</div>
`;
}

updateResultsDisplay(results: BenchmarkResult[]) {
// Debug: log the actual structure of results
console.log('Benchmark results structure:', results);

// Generate table dynamically based on the keys in the first result
let tableHtml =
'<table style="width:100%; border-collapse: collapse; margin: 10px 0;">';
tableHtml += '<thead><tr style="background-color: #333;">';

if (results && results.length > 0) {
const firstResult = results[0];
Object.keys(firstResult).forEach((key) => {
tableHtml += `<th style="border: 1px solid #666; padding: 4px; text-align: left;">${key}</th>`;
});
}

tableHtml += '</tr></thead><tbody>';

if (results && results.length > 0) {
// Create table rows for each result
results.forEach((result, index) => {
// Alternate row colors
const bgColor =
index % 2 === 0 ? 'rgba(50, 50, 50, 0.3)' : 'rgba(70, 70, 70, 0.3)';
tableHtml += `<tr style="background-color: ${bgColor};">`;

Object.keys(result).forEach((key) => {
const value = result[key];
tableHtml += `<td style="border: 1px solid #666; padding: 4px;">${value}</td>`;
});

tableHtml += '</tr>';
});
} else {
tableHtml += '<tr><td colspan="3">No results available</td></tr>';
}

tableHtml += '</tbody></table>';

this.container.innerHTML = `
<h3>${this.title}</h3>
${this.configInfo ? `<div>${this.configInfo}</div><hr />` : ''}
${tableHtml}
`;
}
}
145 changes: 145 additions & 0 deletions __tests__/demos/perf/custom-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { Canvas, ElementEvent, Rect, Group, CustomEvent } from '@antv/g';
import * as tinybench from 'tinybench';
import * as lil from 'lil-gui';
import { BenchmarkPanel, BenchmarkResult } from './benchmark-panel';

/**
* Custom Event Performance Test
* Compare performance between sharing a single event instance and creating new event instances each time
*/
export async function customEvent(context: { canvas: Canvas; gui: lil.GUI }) {
const { canvas, gui } = context;
console.log(canvas);

await canvas.ready;

const { width, height } = canvas.getConfig();
const root = new Group();
let count = 1e4;
let rects = [];

// Shared event instance
const sharedEvent = new CustomEvent(ElementEvent.BOUNDS_CHANGED);

function render() {
root.destroyChildren();
rects = [];

for (let i = 0; i < count; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const size = 10 + Math.random() * 40;

const rect = new Rect({
style: {
x,
y,
width: size,
height: size,
fill: 'white',
stroke: '#000',
lineWidth: 1,
},
});
root.appendChild(rect);
rects[i] = { x, y, size, el: rect };
}
}

render();
canvas.appendChild(root);

// benchmark
// ----------
const bench = new tinybench.Bench({
name: 'Custom Event Performance Comparison',
time: 1e3,
iterations: 100,
});

// Test performance of shared event instance
// Update event properties each time to simulate realistic usage
bench.add('Shared Event Instance', () => {
rects.forEach((rect) => {
// Update event properties to simulate realistic usage
sharedEvent.detail = {
affectChildren: true,
timestamp: performance.now(),
};
rect.el.dispatchEvent(sharedEvent);
});
});

// Test performance of creating new event instances each time
bench.add('New Event Instance', () => {
rects.forEach((rect) => {
const event = new CustomEvent(ElementEvent.BOUNDS_CHANGED);
event.detail = {
affectChildren: true,
timestamp: performance.now(),
};
rect.el.dispatchEvent(event);
});
});

// Test performance of creating same number of events but dispatching only once on root
bench.add('Create Events, Dispatch Once on Root', () => {
const events = [];
// Create same number of event instances
for (let i = 0; i < count; i++) {
const event = new CustomEvent(ElementEvent.BOUNDS_CHANGED);
event.detail = {
affectChildren: true,
timestamp: performance.now(),
};
events.push(event);
}
// But dispatch only once on root
root.dispatchEvent(events[0]);
});

// Test performance of dispatching event on each element without sharing event instance
bench.add('Dispatch on Each Element', () => {
rects.forEach((rect) => {
const event = new CustomEvent(ElementEvent.BOUNDS_CHANGED);
event.detail = {
affectChildren: true,
timestamp: performance.now(),
};
rect.el.dispatchEvent(event);
});
});

// Create benchmark panel
const benchmarkPanel = new BenchmarkPanel(bench.name);

// Show initial status with object count
benchmarkPanel.showRunningStatus(`Object Count: ${count}`);

// ----------

// GUI
const config = {
objectCount: count,
runBenchmark: async () => {
benchmarkPanel.showRunningStatus(`Object Count: ${count}`);

setTimeout(async () => {
await bench.run();
console.log(bench.name);
console.table(bench.table());

benchmarkPanel.updateResultsDisplay(
bench.table() as unknown as BenchmarkResult[],
);
}, 1e2);
},
};

gui.add(config, 'objectCount', 100, 50000, 100).onChange((value) => {
count = value;
render();
});

gui.add(config, 'runBenchmark');
}
1 change: 1 addition & 0 deletions __tests__/demos/perf/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { canvasApi } from './canvas-api';
export { javascript } from './javascript';
export { event } from './event';
export { destroyEvent } from './destroy-event';
export { customEvent } from './custom-event';
63 changes: 59 additions & 4 deletions __tests__/demos/perf/javascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export async function javascript(context: { canvas: Canvas; gui: lil.GUI }) {
// ----------
const bench = new tinybench.Bench({
name: 'javascript benchmark',
time: 1e2,
time: 1e3,
});
const array = [
'stroke',
Expand Down Expand Up @@ -98,6 +98,64 @@ export async function javascript(context: { canvas: Canvas; gui: lil.GUI }) {
// bench.add('typeof - isNil', async () => {
// !(typeof value === 'undefined' || value === null);
// });

// region attr assign ---------------------------------------
// Performance comparison: direct property access vs method calls
class TestClass {
public prop1: number = 0;
private _prop2: number = 0;

setProp2(value: number) {
this._prop2 = value;
}

getProp2() {
return this._prop2;
}
}

const testObj = new TestClass();
const iterations = 1000000;

bench.add('attr assign - Direct property assignment', () => {
for (let i = 0; i < iterations; i++) {
testObj.prop1 = i;
}
});

bench.add('attr assign - Method call assignment', () => {
for (let i = 0; i < iterations; i++) {
testObj.setProp2(i);
}
});

bench.add('attr assign - Direct property access', () => {
let sum = 0;
for (let i = 0; i < iterations; i++) {
sum += testObj.prop1;
}
return sum;
});

bench.add('attr assign - Method call access', () => {
let sum = 0;
for (let i = 0; i < iterations; i++) {
sum += testObj.getProp2();
}
return sum;
});
// endregion ---------------------------------------

// region typeof ---------------------------------------
const testTypeof = undefined;
bench.add('typeof - typeof', async () => {
typeof testTypeof !== 'undefined';
});
bench.add('typeof - !==', async () => {
testTypeof !== undefined;
});
// endregion ---------------------------------------

// bench.add('@antv/util - isNil', async () => {
// !isNil(value);
// });
Expand All @@ -114,10 +172,7 @@ export async function javascript(context: { canvas: Canvas; gui: lil.GUI }) {

await bench.run();

console.log(bench.name);
console.table(bench.table());
console.log(bench.results);
console.log(bench.tasks);

// ----------
}
1 change: 1 addition & 0 deletions __tests__/unit/css/properties/transform.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ describe('CSSPropertyTransform', () => {
cy: 10,
r: 50,
transform: 'scale(0)',
transformOrigin: 'center',
},
});

Expand Down
1 change: 1 addition & 0 deletions __tests__/unit/dom/event.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ describe('Event API', () => {
ul.appendChild(li2);

const event = new CustomEvent('build', { detail: { prop1: 'xx' } });

// delegate to parent
ul.addEventListener('build', (e) => {
expect(e.target).toBe(li1);
Expand Down
Loading
Loading