Skip to content

Commit

Permalink
feat: add browser benchmarks via playwright
Browse files Browse the repository at this point in the history
  • Loading branch information
okikio committed Nov 30, 2022
1 parent 018500a commit 8649746
Show file tree
Hide file tree
Showing 15 changed files with 747 additions and 58 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,6 @@ dist
lib/
docs/
@types/
/test-results/
/playwright-report/
/playwright/.cache/
20 changes: 13 additions & 7 deletions .gitpod.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

image:
file: .gitpod.Dockerfile

Expand All @@ -12,17 +13,17 @@ github:
prebuilds:
# enable for the master/default branch (defaults to true)
master: true
# enable for all branches in this repo (defaults to false)
# enable for all branches in this repo (defaults to false)
branches: true
# enable for pull requests coming from this repo (defaults to true)
# enable for pull requests coming from this repo (defaults to true)
pullRequests: true
# enable for pull requests coming from forks (defaults to false)
# enable for pull requests coming from forks (defaults to false)
pullRequestsFromForks: true
# add a "Review in Gitpod" button as a comment to pull requests (defaults to true)
# add a "Review in Gitpod" button as a comment to pull requests (defaults to true)
addComment: true
# add a "Review in Gitpod" button to pull requests (defaults to false)
# add a "Review in Gitpod" button to pull requests (defaults to false)
addBadge: false
# add a label once the prebuild is ready to pull requests (defaults to false)
# add a label once the prebuild is ready to pull requests (defaults to false)
addLabel: prebuilt-in-gitpod

# List the start up tasks. You can start them in parallel in multiple terminals. See https://www.gitpod.io/docs/44_config_start_tasks/
Expand All @@ -34,4 +35,9 @@ tasks:
pnpm install
command: >
npm install -g pnpm &&
pnpm build
pnpm build
vscode:
extensions:
- denoland.vscode-deno
- ZixuanChen.vitest-explorer
- ms-playwright.playwright
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"deno.enablePaths": [
"./benchmark/deno.ts",
"./benchmark/deno.worker.ts",
"./benchmark/workers/deno.ts"
],
"deno.unstable": true
}
40 changes: 40 additions & 0 deletions benchmark/browsers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { test, expect } from '@playwright/test';

test('Benchmark transferables on browsers', async ({ page }) => {
await page.goto('/');

// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Benchmark/);

// create a locator
const structuredCloneBtn = page.locator('button#structuredClone');

// Expect an attribute "to be strictly equal" to the value.
await expect(structuredCloneBtn).toHaveAttribute('type', 'button');

// Click the get started link.
await structuredCloneBtn.click();

// Expects the URL to contain intro.
const structuredCloneResult = page.locator("#structuredCloneResult");
await structuredCloneResult.waitFor();

console.log(`structuredClone (browser)`)
console.log(structuredCloneResult.getAttribute("data-value"))

// create a locator
const postMessageBtn = page.locator('button#postMessage');

// Expect an attribute "to be strictly equal" to the value.
await expect(postMessageBtn).toHaveAttribute('type', 'button');

// Click the get started link.
await postMessageBtn.click();

// Expects the URL to contain intro.
const postMessageResult = page.locator("#postMessageResult");
await postMessageResult.waitFor();

console.log(`postMessage (browser)`)
console.log(postMessageResult.getAttribute("data-value"))
});
10 changes: 5 additions & 5 deletions benchmark/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ for (let cycle = 0; cycle < 5; cycle++) {
console.log("\n")
}

let Head = ["", ...head];
let table: object[] = [];
const Head = ["", ...head];
const table: Record<string, string[]>[] = [];

let strVal = 'Map {\n'
perfs.forEach((variants, name) => {
strVal += ` "${name}" => Map { `;

let obj = {};
const obj: Record<string, string[]> = {};
variants.forEach((durations, variant) => {
const [mean, std] = dmeanstdev(durations.length, 0, new Float64Array(durations), 1, new Float64Array(2), 1);

Expand All @@ -68,8 +68,8 @@ perfs.forEach((variants, name) => {
strVal += `},\n`;
})

let str = table.map((x) => {
let [key] = Object.keys(x);
const str = table.map((x) => {
const [key] = Object.keys(x);
return [key, ...x[key]]
})

Expand Down
15 changes: 8 additions & 7 deletions benchmark/deno.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface IIterationType {
variant: string;
cycle: number;
i: number;
obj?: object;
obj?: Record<string, unknown>;
}

interface ICreateWorkerIteratorOptions {
Expand Down Expand Up @@ -47,20 +47,20 @@ for (let cycle = 0; cycle < 5; cycle++) {
const sizeStr = bytes(num, { maximumFractionDigits: 3 });

await add(sizeStr, variant, async () => {
await createWorkerPromise({ index, variant });
await createWorkerPromise({ index, variant, cycle });
})
}
}
}

let Head = ["", ...variants];
let table: object[] = [];
const Head = ["", ...variants];
const table: Record<string, string[]>[] = [];

let strVal = 'Map {\n'
perfs.forEach((variants, name) => {
strVal += ` "${name}" => Map { `;

let obj = {};
const obj: Record<string, string[]> = {};
variants.forEach((durations, variant) => {
const [mean, std] = dmeanstdev(durations.length, 0, new Float64Array(durations), 1, new Float64Array(2), 1);

Expand All @@ -75,8 +75,8 @@ perfs.forEach((variants, name) => {
strVal += `},\n`;
})

let str = table.map((x) => {
let [key] = Object.keys(x);
const str = table.map((x) => {
const [key] = Object.keys(x);
return [key, ...x[key]]
})

Expand All @@ -87,3 +87,4 @@ console.log(strVal);




16 changes: 16 additions & 0 deletions benchmark/fixtures/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="color-scheme" content="dark light" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Benchmark</title>
</head>
<body>
<button id="structuredClone" type="button">structuredClone</button>
<button id="postMessage" type="button">postMessage</button>

<script type="module" src="./script.ts"></script>
</body>
</html>
93 changes: 93 additions & 0 deletions benchmark/fixtures/postMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { MB, add, perfs, timeFormatter } from "../utils";

import bytes from "pretty-bytes";
import { dmeanstdev } from '@stdlib/stats-base';

import { markdownTable } from 'markdown-table';

interface IIterationType {
name: string;
variant: string;
cycle: number;
i: number;
obj?: Record<string, unknown>;
}

interface ICreateWorkerIteratorOptions {
index: number;
variant: string;
cycle?: number;
}

async function createWorkerPromise({ index, cycle = 0, variant }: ICreateWorkerIteratorOptions) {
const worker = new Worker(new URL("../workers/worker.ts", import.meta.url).href, { type: "module" });

const num = Math.pow(2, index);
const sizeStr = bytes(num, { maximumFractionDigits: 3 });

const msg = { name: sizeStr, variant, cycle, i: index };
worker.postMessage(msg);

const data = await new Promise<IIterationType>(resolve => {
worker.onmessage = ({ data }: MessageEvent<IIterationType>) => {
resolve(data);
}
});

worker.terminate();
return data;
}

export default async function (e: MouseEvent) {
e.preventDefault();

const variants = [`postMessage`, `postMessage (predefined)`, `hasTransferables`, `getTransferable`, `getTransferable(s)`];
const maxSize = 1.6;
for (let cycle = 0; cycle < 5; cycle++) {
for (let variant of variants) {
for (let index = 0; index <= Math.log2(maxSize * MB); index++) {
const num = Math.pow(2, index);
const sizeStr = bytes(num, { maximumFractionDigits: 3 });

await add(sizeStr, variant, async () => {
await createWorkerPromise({ index, variant, cycle });
})
}
}
}

const Head = ["", ...variants];
const table: Record<string, string[]>[] = [];

let strVal = 'Map {\n'
perfs.forEach((variants, name) => {
strVal += ` "${name}" => Map { `;

const obj: Record<string, string[]> = {};
variants.forEach((durations, variant) => {
const [mean, std] = dmeanstdev(durations.length, 0, new Float64Array(durations), 1, new Float64Array(2), 1);

obj[name] ??= [];
obj[name].push(`${timeFormatter.format(mean, "seconds")} ± ${timeFormatter.format(std, "seconds").replace("in ", "")}`);

strVal += `"${variant}" => [${durations.map(x => timeFormatter.format(x, "seconds")).join(", ")}], `

});

table.push(obj);
strVal += `},\n`;
})

const str = table.map((x) => {
const [key] = Object.keys(x);
return [key, ...x[key]]
})

strVal += `}`;

const result = markdownTable([Head, ...str])
console.log(result);
console.log(strVal);

return result;
}
33 changes: 33 additions & 0 deletions benchmark/fixtures/script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import postMessageBenchmark from "./postMessage";
import structuredCloneBenchmark from "./structuredClone";

import { micromark } from 'micromark';
import { gfm, gfmHtml } from 'micromark-extension-gfm';

const structuredCloneBtn: HTMLButtonElement = document.querySelector("#structuredClone");
const postMessageBtn: HTMLButtonElement = document.querySelector("#postMessage");

const structuredCloneResultEl: HTMLDivElement = document.createElement("div");
const postMessageResultEl: HTMLDivElement = document.querySelector("div");

structuredCloneBtn?.addEventListener('click', async function (e: MouseEvent) {
const result = await structuredCloneBenchmark(e);
structuredCloneResultEl.id = "structuredCloneResult";
structuredCloneResultEl.dataset.value = result;
structuredCloneResultEl.innerHTML = micromark(result, {
extensions: [gfm()],
htmlExtensions: [gfmHtml()]
});
document.body.appendChild(structuredCloneResultEl);
})

postMessageBtn?.addEventListener('click', async function (e: MouseEvent) {
const result = await postMessageBenchmark(e);
postMessageResultEl.id = "postMessageResult";
postMessageResultEl.dataset.value = result;
postMessageResultEl.innerHTML = micromark(result, {
extensions: [gfm()],
htmlExtensions: [gfmHtml()]
});
document.body.appendChild(postMessageResultEl);
})

0 comments on commit 8649746

Please sign in to comment.