refactor(benchmark): use react-router instead of just vite
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,3 +6,5 @@
|
|||||||
/pkgs/*/.turbo/
|
/pkgs/*/.turbo/
|
||||||
|
|
||||||
/pkgs/solver-wasm/target/
|
/pkgs/solver-wasm/target/
|
||||||
|
|
||||||
|
/pkgs/benchmark/.react-router/
|
||||||
|
|||||||
91
pkgs/benchmark/app/App.tsx
Normal file
91
pkgs/benchmark/app/App.tsx
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import * as powCaptcha from "@pow-captcha/pow-captcha";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import type { Route } from "./+types/App";
|
||||||
|
|
||||||
|
export async function loader({ params }: Route.LoaderArgs) {
|
||||||
|
const challenge = await powCaptcha.server.createChallengesRaw({
|
||||||
|
difficultyBits: 19,
|
||||||
|
challengeCount: 64,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { challenge };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function App({
|
||||||
|
loaderData: { challenge },
|
||||||
|
}: Route.ComponentProps) {
|
||||||
|
const [durationJs, setDurationJs] = useState<null | string>(null);
|
||||||
|
const [durationWasm, setDurationWasm] = useState<null | string>(null);
|
||||||
|
|
||||||
|
const run = useCallback(
|
||||||
|
async (
|
||||||
|
engine: "wasm" | "js",
|
||||||
|
setter: React.Dispatch<React.SetStateAction<string | null>>,
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
setter(`Running`);
|
||||||
|
|
||||||
|
const challenges = challenge.challenges.map(
|
||||||
|
(challenge) =>
|
||||||
|
[
|
||||||
|
powCaptcha.wire.deserializeArray(challenge[0]),
|
||||||
|
powCaptcha.wire.deserializeArray(challenge[1]),
|
||||||
|
] as const,
|
||||||
|
);
|
||||||
|
|
||||||
|
const start = performance.now();
|
||||||
|
await powCaptcha.solver.solveChallenges({
|
||||||
|
difficultyBits: challenge.difficultyBits,
|
||||||
|
challenges,
|
||||||
|
engine,
|
||||||
|
});
|
||||||
|
const duration = performance.now() - start;
|
||||||
|
|
||||||
|
setter(`${duration.toFixed(1)}ms`);
|
||||||
|
} catch (err) {
|
||||||
|
setter(`Error: ${err}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[challenge],
|
||||||
|
);
|
||||||
|
|
||||||
|
const runJs = useCallback(async () => {
|
||||||
|
await run("js", setDurationJs);
|
||||||
|
}, [run]);
|
||||||
|
|
||||||
|
const runWasm = useCallback(async () => {
|
||||||
|
await run("wasm", setDurationWasm);
|
||||||
|
}, [run]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>pow-captcha Benchmark</h1>
|
||||||
|
Results:
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>type</th>
|
||||||
|
<th>duration</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>wasm</td>
|
||||||
|
<td>{durationWasm}</td>
|
||||||
|
<td>
|
||||||
|
<button onClick={runWasm}>run</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>js</td>
|
||||||
|
<td>{durationJs}</td>
|
||||||
|
<td>
|
||||||
|
<button onClick={runJs}>run</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
15
pkgs/benchmark/app/root.tsx
Normal file
15
pkgs/benchmark/app/root.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Outlet, Scripts } from "react-router";
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>pow-captcha Benchmark</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<Outlet />
|
||||||
|
<Scripts />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
pkgs/benchmark/app/routes.ts
Normal file
3
pkgs/benchmark/app/routes.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { type RouteConfig, index } from "@react-router/dev/routes";
|
||||||
|
|
||||||
|
export default [index("./App.tsx")] satisfies RouteConfig;
|
||||||
@@ -3,17 +3,17 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "react-router dev",
|
||||||
"build": "tsc -b && vite build",
|
"build": "react-router build",
|
||||||
|
"check": "react-router typegen && tsc",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.1.0",
|
|
||||||
"react-dom": "^19.1.0",
|
|
||||||
"vite-plugin-top-level-await": "^1.5.0",
|
|
||||||
"vite-plugin-wasm": "^3.5.0",
|
|
||||||
"@eslint/js": "^9.30.1",
|
"@eslint/js": "^9.30.1",
|
||||||
|
"@pow-captcha/pow-captcha": "workspace:*",
|
||||||
|
"@react-router/dev": "^7.6.3",
|
||||||
|
"@react-router/node": "^7.6.3",
|
||||||
"@types/react": "^19.1.8",
|
"@types/react": "^19.1.8",
|
||||||
"@types/react-dom": "^19.1.6",
|
"@types/react-dom": "^19.1.6",
|
||||||
"@vitejs/plugin-react": "^4.6.0",
|
"@vitejs/plugin-react": "^4.6.0",
|
||||||
@@ -21,9 +21,14 @@
|
|||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
"globals": "^16.3.0",
|
"globals": "^16.3.0",
|
||||||
|
"isbot": "^5",
|
||||||
|
"react": "^19.1.0",
|
||||||
|
"react-dom": "^19.1.0",
|
||||||
|
"react-router": "^7.6.3",
|
||||||
"typescript": "~5.8.3",
|
"typescript": "~5.8.3",
|
||||||
"typescript-eslint": "^8.35.1",
|
"typescript-eslint": "^8.35.1",
|
||||||
"vite": "^7.0.4",
|
"vite": "^7.0.4",
|
||||||
"@pow-captcha/pow-captcha": "workspace:*"
|
"vite-plugin-top-level-await": "^1.5.0",
|
||||||
|
"vite-plugin-wasm": "^3.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
pkgs/benchmark/react-router.config.ts
Normal file
5
pkgs/benchmark/react-router.config.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import type { Config } from "@react-router/dev/config";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
ssr: true,
|
||||||
|
} satisfies Config;
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
import * as powCaptcha from "@pow-captcha/pow-captcha";
|
|
||||||
import { useCallback, useEffect, useState } from "react";
|
|
||||||
|
|
||||||
async function benchmark(cb: () => void | Promise<void>): Promise<number> {
|
|
||||||
const start = performance.now();
|
|
||||||
await cb();
|
|
||||||
const end = performance.now();
|
|
||||||
return end - start;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const [challenge, setChallenge] =
|
|
||||||
useState<null | powCaptcha.wire.Challenge>(null);
|
|
||||||
|
|
||||||
const [durationJs, setDurationJs] = useState<null | string>(null);
|
|
||||||
const [durationWasm, setDurationWasm] = useState<null | string>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function init() {
|
|
||||||
const challengesRaw = await powCaptcha.server.createChallengesRaw({
|
|
||||||
difficultyBits: 19,
|
|
||||||
challengeCount: 64,
|
|
||||||
});
|
|
||||||
setChallenge(challengesRaw);
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const run = useCallback(
|
|
||||||
async (
|
|
||||||
engine: "wasm" | "js",
|
|
||||||
setter: React.Dispatch<React.SetStateAction<string | null>>,
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
if (!challenge) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setter(`Running`);
|
|
||||||
|
|
||||||
const challenges = challenge.challenges.map(
|
|
||||||
(challenge) =>
|
|
||||||
[
|
|
||||||
powCaptcha.wire.deserializeArray(challenge[0]),
|
|
||||||
powCaptcha.wire.deserializeArray(challenge[1]),
|
|
||||||
] as const,
|
|
||||||
);
|
|
||||||
|
|
||||||
const duration = await benchmark(async () => {
|
|
||||||
await powCaptcha.solver.solveChallenges({
|
|
||||||
difficultyBits: challenge.difficultyBits,
|
|
||||||
challenges,
|
|
||||||
engine,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
setter(`${duration.toFixed(1)}ms`);
|
|
||||||
} catch (err) {
|
|
||||||
setter(`Error: ${err}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[challenge],
|
|
||||||
);
|
|
||||||
|
|
||||||
const runJs = useCallback(async () => {
|
|
||||||
await run("js", setDurationJs);
|
|
||||||
}, [run]);
|
|
||||||
|
|
||||||
const runWasm = useCallback(async () => {
|
|
||||||
await run("wasm", setDurationWasm);
|
|
||||||
}, [run]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>pow-captcha Benchmark</h1>
|
|
||||||
Results:
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>type</th>
|
|
||||||
<th>duration</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>wasm</td>
|
|
||||||
<td>{durationWasm}</td>
|
|
||||||
<td>
|
|
||||||
<button onClick={runWasm}>run</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>js</td>
|
|
||||||
<td>{durationJs}</td>
|
|
||||||
<td>
|
|
||||||
<button onClick={runJs}>run</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { StrictMode } from "react";
|
|
||||||
import { createRoot } from "react-dom/client";
|
|
||||||
import App from "./App.tsx";
|
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
|
||||||
<StrictMode>
|
|
||||||
<App />
|
|
||||||
</StrictMode>,
|
|
||||||
);
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"types": ["@react-router/node", "vite/client"],
|
||||||
|
"rootDirs": [".", "./.react-router/types"],
|
||||||
|
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
@@ -23,5 +26,5 @@
|
|||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUncheckedSideEffectImports": true
|
"noUncheckedSideEffectImports": true
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["app", ".react-router/types/**/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
import { defineConfig, type PluginOption } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import { reactRouter } from "@react-router/dev/vite";
|
||||||
import wasm from "vite-plugin-wasm";
|
import wasm from "vite-plugin-wasm";
|
||||||
import topLevelAwait from "vite-plugin-top-level-await";
|
import topLevelAwait from "vite-plugin-top-level-await";
|
||||||
|
|
||||||
const plugins: () => Array<PluginOption> = () => [
|
|
||||||
react(),
|
|
||||||
wasm(),
|
|
||||||
topLevelAwait(),
|
|
||||||
];
|
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: plugins(),
|
plugins: [reactRouter(), wasm(), topLevelAwait()],
|
||||||
worker: {
|
worker: {
|
||||||
plugins,
|
plugins: () => [wasm(), topLevelAwait()],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
602
pnpm-lock.yaml
generated
602
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user