├── react-ssr-bench ├── vercel-edition │ ├── .gitignore │ ├── vercel.json │ ├── package.json │ ├── api │ │ └── bench.mjs │ └── ComplexComponent.mjs └── cf-edition │ ├── package.json │ ├── wrangler.jsonc │ ├── worker.js │ └── ComplexComponent.mjs ├── sveltekit-bench ├── cf-edition │ ├── .npmrc │ ├── src │ │ ├── app.css │ │ ├── lib │ │ │ ├── index.ts │ │ │ ├── assets │ │ │ │ └── favicon.svg │ │ │ └── complex.ts │ │ ├── routes │ │ │ ├── +layout.svelte │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ │ ├── app.d.ts │ │ ├── app.html │ │ └── hooks.server.ts │ ├── static │ │ └── robots.txt │ ├── .prettierignore │ ├── vite.config.ts │ ├── .gitignore │ ├── .prettierrc │ ├── wrangler.jsonc │ ├── svelte.config.js │ ├── tsconfig.json │ ├── README.md │ └── package.json └── vercel-edition │ ├── .npmrc │ ├── src │ ├── app.css │ ├── lib │ │ ├── index.ts │ │ ├── assets │ │ │ └── favicon.svg │ │ └── complex.ts │ ├── app.d.ts │ ├── routes │ │ ├── +layout.svelte │ │ ├── +page.server.ts │ │ └── +page.svelte │ └── app.html │ ├── static │ └── robots.txt │ ├── vercel.json │ ├── .prettierignore │ ├── vite.config.ts │ ├── .gitignore │ ├── svelte.config.js │ ├── .prettierrc │ ├── tsconfig.json │ ├── README.md │ └── package.json ├── vanilla-bench ├── vercel-edition │ ├── .gitignore │ ├── .vercel │ │ ├── project.json │ │ └── README.txt │ ├── package.json │ ├── vercel.json │ ├── README.md │ └── api │ │ ├── shitty-sine-bench.js │ │ ├── realistic-math-bench.js │ │ ├── bench.js │ │ └── slower-bench.js └── cf-edition │ ├── .gitignore │ ├── package.json │ ├── README.md │ └── wrangler.jsonc ├── next-bench ├── cf-edition │ ├── postcss.config.mjs │ ├── src │ │ └── app │ │ │ ├── favicon.ico │ │ │ ├── bench │ │ │ ├── page.tsx │ │ │ └── complex-component.tsx │ │ │ ├── globals.css │ │ │ ├── page.tsx │ │ │ └── layout.tsx │ ├── public │ │ ├── vercel.svg │ │ ├── _headers │ │ ├── file.svg │ │ ├── window.svg │ │ ├── globe.svg │ │ └── next.svg │ ├── next.config.ts │ ├── open-next.config.ts │ ├── eslint.config.mjs │ ├── .gitignore │ ├── tsconfig.json │ ├── package.json │ ├── README.md │ └── wrangler.jsonc └── vercel-edition │ ├── postcss.config.mjs │ ├── src │ └── app │ │ ├── favicon.ico │ │ ├── bench │ │ ├── page.tsx │ │ └── complex-component.tsx │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── page.tsx │ ├── public │ ├── vercel.svg │ ├── window.svg │ ├── file.svg │ ├── globe.svg │ └── next.svg │ ├── next.config.ts │ ├── eslint.config.mjs │ ├── .gitignore │ ├── package.json │ ├── tsconfig.json │ └── README.md ├── LICENSE ├── .gitignore ├── results ├── results-2025-10-03T08-20-57-628Z.json ├── results-2025-10-03T07-45-47-919Z.json ├── results-2025-10-03T07-47-28-876Z.json ├── results-2025-10-03T08-27-23-461Z.json ├── results-2025-10-03T08-33-14-536Z.json ├── results-2025-10-03T04-45-09-593Z.json ├── results-2025-10-03T03-52-47-398Z.json ├── results-2025-10-03T03-55-30-909Z.json ├── results-2025-10-03T04-51-03-927Z.json ├── results-2025-10-03T05-10-20-674Z.json ├── results-2025-10-03T06-10-12-995Z.json ├── results-2025-10-12T23-10-03-088Z.txt ├── results-2025-10-12T23-28-24-318Z.txt ├── results-2025-10-12T23-31-26-819Z.txt ├── results-2025-10-12T22-57-41-902Z.txt ├── results-2025-10-03T05-33-05-236Z.json └── results-2025-10-03T08-30-11-879Z.json ├── .claude └── settings.local.json ├── readme.md └── runner.js /react-ssr-bench/vercel-edition/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vercel 3 | -------------------------------------------------------------------------------- /vanilla-bench/cf-edition/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .wrangler 3 | .dev.vars 4 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/app.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | body { 4 | background-color: #101828; 5 | } 6 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // place files you want to import through the `$lib` alias in this folder. 2 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/static/robots.txt: -------------------------------------------------------------------------------- 1 | # allow crawling everything by default 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/src/app.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | body { 4 | background-color: #101828; 5 | } 6 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/static/robots.txt: -------------------------------------------------------------------------------- 1 | # allow crawling everything by default 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // place files you want to import through the `$lib` alias in this folder. 2 | -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/.vercel/project.json: -------------------------------------------------------------------------------- 1 | {"orgId":"team_p47uU3yRCjVcJwzUB0IGqX4M","projectId":"prj_MzfsfS1soANYo6N2SYiXpcCBvaFz"} -------------------------------------------------------------------------------- /next-bench/cf-edition/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /next-bench/cf-edition/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t3dotgg/cf-vs-vercel-bench/HEAD/next-bench/cf-edition/src/app/favicon.ico -------------------------------------------------------------------------------- /next-bench/vercel-edition/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://openapi.vercel.sh/vercel.json", 3 | "buildCommand": "vite build" 4 | } 5 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t3dotgg/cf-vs-vercel-bench/HEAD/next-bench/vercel-edition/src/app/favicon.ico -------------------------------------------------------------------------------- /next-bench/cf-edition/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react-ssr-bench/vercel-edition/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": { 3 | "api/bench.mjs": { 4 | "memory": 3008, 5 | "maxDuration": 30 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /next-bench/cf-edition/public/_headers: -------------------------------------------------------------------------------- 1 | # https://developers.cloudflare.com/workers/static-assets/headers 2 | /_next/static/* 3 | Cache-Control: public,max-age=31536000,immutable 4 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/.prettierignore: -------------------------------------------------------------------------------- 1 | # Package Managers 2 | package-lock.json 3 | pnpm-lock.yaml 4 | yarn.lock 5 | bun.lock 6 | bun.lockb 7 | 8 | # Miscellaneous 9 | /static/ 10 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/.prettierignore: -------------------------------------------------------------------------------- 1 | # Package Managers 2 | package-lock.json 3 | pnpm-lock.yaml 4 | yarn.lock 5 | bun.lock 6 | bun.lockb 7 | 8 | # Miscellaneous 9 | /static/ 10 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/vite.config.ts: -------------------------------------------------------------------------------- 1 | import tailwindcss from '@tailwindcss/vite'; 2 | import { sveltekit } from '@sveltejs/kit/vite'; 3 | import { defineConfig } from 'vite'; 4 | 5 | export default defineConfig({ 6 | plugins: [tailwindcss(), sveltekit()] 7 | }); 8 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/vite.config.ts: -------------------------------------------------------------------------------- 1 | import tailwindcss from '@tailwindcss/vite'; 2 | import { sveltekit } from '@sveltejs/kit/vite'; 3 | import { defineConfig } from 'vite'; 4 | 5 | export default defineConfig({ 6 | plugins: [tailwindcss(), sveltekit()] 7 | }); 8 | -------------------------------------------------------------------------------- /vanilla-bench/cf-edition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vanilla-cf-edition", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "wrangler dev", 7 | "deploy": "wrangler deploy" 8 | }, 9 | "devDependencies": { 10 | "wrangler": "^4.42.2" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vanilla-vercel-edition", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vercel dev", 7 | "deploy": "vercel deploy --prod" 8 | }, 9 | "devDependencies": { 10 | "vercel": "^37.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/lib/assets/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Output 4 | .output 5 | .vercel 6 | .netlify 7 | .wrangler 8 | /.svelte-kit 9 | /build 10 | 11 | # OS 12 | .DS_Store 13 | Thumbs.db 14 | 15 | # Env 16 | .env 17 | .env.* 18 | !.env.example 19 | !.env.test 20 | 21 | # Vite 22 | vite.config.js.timestamp-* 23 | vite.config.ts.timestamp-* 24 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Output 4 | .output 5 | .vercel 6 | .netlify 7 | .wrangler 8 | /.svelte-kit 9 | /build 10 | 11 | # OS 12 | .DS_Store 13 | Thumbs.db 14 | 15 | # Env 16 | .env 17 | .env.* 18 | !.env.example 19 | !.env.test 20 | 21 | # Vite 22 | vite.config.js.timestamp-* 23 | vite.config.ts.timestamp-* 24 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | cf sveltekit bench 10 | 11 | 12 | 13 | {@render children?.()} 14 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/src/lib/assets/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://svelte.dev/docs/kit/types#app.d.ts 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %sveltekit.head% 7 | 8 | 9 |
%sveltekit.body%
10 | 11 | 12 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://svelte.dev/docs/kit/types#app.d.ts 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | vercel sveltekit bench 10 | 11 | 12 | 13 | {@render children?.()} 14 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-vercel'; 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | preprocess: vitePreprocess(), 7 | 8 | kit: { 9 | adapter: adapter() 10 | } 11 | }; 12 | 13 | export default config; 14 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %sveltekit.head% 7 | 8 | 9 |
%sveltekit.body%
10 | 11 | 12 | -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": { 3 | "api/bench.js": { 4 | "memory": 3008, 5 | "maxDuration": 800 6 | }, 7 | "api/slower-bench.js": { 8 | "memory": 3008, 9 | "maxDuration": 800 10 | }, 11 | "api/shitty-sine-bench.js": { 12 | "memory": 3008, 13 | "maxDuration": 800 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import type { Handle } from '@sveltejs/kit'; 2 | 3 | export const handle: Handle = async ({ event, resolve }) => { 4 | const response = await resolve(event); 5 | 6 | response.headers.set('Cache-Control', 'no-store'); 7 | response.headers.set('Pragma', 'no-cache'); 8 | response.headers.set('Expires', '0'); 9 | 10 | return response; 11 | }; 12 | -------------------------------------------------------------------------------- /next-bench/cf-edition/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | 9 | // added by create cloudflare to enable calling `getCloudflareContext()` in `next dev` 10 | import { initOpenNextCloudflareForDev } from '@opennextjs/cloudflare'; 11 | initOpenNextCloudflareForDev(); 12 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], 7 | "overrides": [ 8 | { 9 | "files": "*.svelte", 10 | "options": { 11 | "parser": "svelte" 12 | } 13 | } 14 | ], 15 | "tailwindStylesheet": "./src/app.css" 16 | } 17 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], 7 | "overrides": [ 8 | { 9 | "files": "*.svelte", 10 | "options": { 11 | "parser": "svelte" 12 | } 13 | } 14 | ], 15 | "tailwindStylesheet": "./src/app.css" 16 | } 17 | -------------------------------------------------------------------------------- /next-bench/cf-edition/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next-bench/cf-edition/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react-ssr-bench/vercel-edition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-ssr-vercel-edition", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vercel dev", 8 | "deploy": "vercel" 9 | }, 10 | "dependencies": { 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0" 13 | }, 14 | "devDependencies": { 15 | "vercel": "^37.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react-ssr-bench/cf-edition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-ssr-cf-edition", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "wrangler dev", 8 | "deploy": "wrangler deploy" 9 | }, 10 | "dependencies": { 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0" 13 | }, 14 | "devDependencies": { 15 | "wrangler": "^4.42.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /next-bench/cf-edition/open-next.config.ts: -------------------------------------------------------------------------------- 1 | import { defineCloudflareConfig } from "@opennextjs/cloudflare"; 2 | 3 | export default defineCloudflareConfig({ 4 | // Uncomment to enable R2 cache, 5 | // It should be imported as: 6 | // `import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";` 7 | // See https://opennext.js.org/cloudflare/caching for more details 8 | // incrementalCache: r2IncrementalCache, 9 | }); 10 | -------------------------------------------------------------------------------- /next-bench/cf-edition/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | ]; 15 | 16 | export default eslintConfig; 17 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/wrangler.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/wrangler/config-schema.json", 3 | "name": "cf-sveltekit-bench", 4 | "main": ".svelte-kit/cloudflare/_worker.js", 5 | "assets": { 6 | "binding": "ASSETS", 7 | "directory": ".svelte-kit/cloudflare" 8 | }, 9 | "compatibility_flags": ["nodejs_compat"], 10 | "compatibility_date": "2025-10-01", 11 | "minify": true, 12 | "observability": { 13 | "enabled": true 14 | }, 15 | "account_id": "2a5bb09ba3a450e93ceec4918a6bd96f" 16 | } 17 | -------------------------------------------------------------------------------- /vanilla-bench/cf-edition/README.md: -------------------------------------------------------------------------------- 1 | # Vanilla SSR - CloudFlare Edition 2 | 3 | A simple server-side rendering demo built with vanilla JavaScript for CloudFlare Workers. 4 | 5 | ## Setup 6 | 7 | ```bash 8 | npm install 9 | ``` 10 | 11 | ## Development 12 | 13 | ```bash 14 | npm run dev 15 | ``` 16 | 17 | ## Deploy 18 | 19 | ```bash 20 | npm run deploy 21 | ``` 22 | 23 | ## Endpoint 24 | 25 | The benchmark is available at `/bench` which performs expensive server-side computations (prime numbers, fibonacci sequences, etc.) and renders them as HTML. 26 | -------------------------------------------------------------------------------- /vanilla-bench/cf-edition/wrangler.jsonc: -------------------------------------------------------------------------------- 1 | /** 2 | * Wrangler configuration for vanilla SSR CloudFlare Worker 3 | * For more details: https://developers.cloudflare.com/workers/wrangler/configuration/ 4 | */ 5 | { 6 | "$schema": "node_modules/wrangler/config-schema.json", 7 | "name": "vanilla-ssr-cf", 8 | "main": "worker.js", 9 | "compatibility_date": "2025-10-01", 10 | "compatibility_flags": ["nodejs_compat"], 11 | "minify": true, 12 | "observability": { 13 | "enabled": true, 14 | }, 15 | "account_id": "2a5bb09ba3a450e93ceec4918a6bd96f", 16 | } 17 | -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/README.md: -------------------------------------------------------------------------------- 1 | # Vanilla SSR - Vercel Edition 2 | 3 | A simple server-side rendering demo built with vanilla JavaScript for Vercel Serverless Functions. 4 | 5 | ## Setup 6 | 7 | ```bash 8 | npm install 9 | ``` 10 | 11 | ## Development 12 | 13 | ```bash 14 | npm run dev 15 | ``` 16 | 17 | ## Deploy 18 | 19 | ```bash 20 | npm run deploy 21 | ``` 22 | 23 | ## Endpoint 24 | 25 | The benchmark is available at `/api/bench` which performs expensive server-side computations (prime numbers, fibonacci sequences, etc.) and renders them as HTML. 26 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/routes/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { getComplexData } from '$lib/complex'; 2 | 3 | export const load = () => { 4 | const { data, totalPrimes, averageFib } = getComplexData(); 5 | 6 | const computations = Array.from({ length: 300 }, (_, i) => { 7 | const n = i + 1; 8 | const factorial = Array.from({ length: Math.min(n, 20) }, (_, j) => j + 1).reduce( 9 | (acc, val) => acc * val, 10 | 1 11 | ); 12 | return { n, factorial }; 13 | }); 14 | 15 | return { 16 | data, 17 | totalPrimes, 18 | averageFib, 19 | computations 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-cloudflare'; 2 | 3 | /** @type {import('@sveltejs/kit').Config} */ 4 | const config = { 5 | kit: { 6 | adapter: adapter({ 7 | // See below for an explanation of these options 8 | config: undefined, 9 | platformProxy: { 10 | configPath: undefined, 11 | environment: undefined, 12 | persist: undefined 13 | }, 14 | fallback: 'plaintext', 15 | routes: { 16 | include: ['/*'], 17 | exclude: [''] 18 | } 19 | }) 20 | } 21 | }; 22 | 23 | export default config; 24 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/src/routes/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { getComplexData } from '$lib/complex'; 2 | 3 | export const load = () => { 4 | const { data, totalPrimes, averageFib } = getComplexData(); 5 | 6 | const computations = Array.from({ length: 300 }, (_, i) => { 7 | const n = i + 1; 8 | const factorial = Array.from({ length: Math.min(n, 20) }, (_, j) => j + 1).reduce( 9 | (acc, val) => acc * val, 10 | 1 11 | ); 12 | return { n, factorial }; 13 | }); 14 | 15 | return { 16 | data, 17 | totalPrimes, 18 | averageFib, 19 | computations 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/.vercel/README.txt: -------------------------------------------------------------------------------- 1 | > Why do I have a folder named ".vercel" in my project? 2 | The ".vercel" folder is created when you link a directory to a Vercel project. 3 | 4 | > What does the "project.json" file contain? 5 | The "project.json" file contains: 6 | - The ID of the Vercel project that you linked ("projectId") 7 | - The ID of the user or team your Vercel project is owned by ("orgId") 8 | 9 | > Should I commit the ".vercel" folder? 10 | No, you should not share the ".vercel" folder with anyone. 11 | Upon creation, it will be automatically added to your ".gitignore" file. 12 | -------------------------------------------------------------------------------- /next-bench/cf-edition/src/app/bench/page.tsx: -------------------------------------------------------------------------------- 1 | import ComplexComponent from "./complex-component"; 2 | 3 | export const dynamic = "force-dynamic"; 4 | 5 | export default async function ComplexPage() { 6 | console.log("rendering", Date.now()); 7 | 8 | const currentTime = new Date().toLocaleString(); 9 | return ( 10 |
11 |

Last rendered at:

12 |

{currentTime}

13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/src/app/bench/page.tsx: -------------------------------------------------------------------------------- 1 | import ComplexComponent from "./complex-component"; 2 | 3 | export const dynamic = "force-dynamic"; 4 | 5 | export default async function ComplexPage() { 6 | console.log("rendering", Date.now()); 7 | 8 | const currentTime = new Date().toLocaleString(); 9 | return ( 10 |
11 |

Last rendered at:

12 |

{currentTime}

13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /next-bench/cf-edition/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | :root { 4 | --background: #ffffff; 5 | --foreground: #171717; 6 | } 7 | 8 | @theme inline { 9 | --color-background: var(--background); 10 | --color-foreground: var(--foreground); 11 | --font-sans: var(--font-geist-sans); 12 | --font-mono: var(--font-geist-mono); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | :root { 17 | --background: #0a0a0a; 18 | --foreground: #ededed; 19 | } 20 | } 21 | 22 | body { 23 | background: var(--background); 24 | color: var(--foreground); 25 | font-family: Arial, Helvetica, sans-serif; 26 | } 27 | -------------------------------------------------------------------------------- /react-ssr-bench/cf-edition/wrangler.jsonc: -------------------------------------------------------------------------------- 1 | /** 2 | * Wrangler configuration for React SSR CloudFlare Worker 3 | * For more details: https://developers.cloudflare.com/workers/wrangler/configuration/ 4 | */ 5 | { 6 | "$schema": "node_modules/wrangler/config-schema.json", 7 | "name": "react-ssr-cf", 8 | "main": "worker.js", 9 | "compatibility_date": "2025-10-01", 10 | "compatibility_flags": ["nodejs_compat"], 11 | "minify": true, 12 | "define": { 13 | "process.env.NODE_ENV": "'production'", 14 | }, 15 | "observability": { 16 | "enabled": true, 17 | }, 18 | "account_id": "2a5bb09ba3a450e93ceec4918a6bd96f", 19 | } 20 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | :root { 4 | --background: #ffffff; 5 | --foreground: #171717; 6 | } 7 | 8 | @theme inline { 9 | --color-background: var(--background); 10 | --color-foreground: var(--foreground); 11 | --font-sans: var(--font-geist-sans); 12 | --font-mono: var(--font-geist-mono); 13 | } 14 | 15 | @media (prefers-color-scheme: dark) { 16 | :root { 17 | --background: #0a0a0a; 18 | --foreground: #ededed; 19 | } 20 | } 21 | 22 | body { 23 | background: var(--background); 24 | color: var(--foreground); 25 | font-family: Arial, Helvetica, sans-serif; 26 | } 27 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | { 15 | ignores: [ 16 | "node_modules/**", 17 | ".next/**", 18 | "out/**", 19 | "build/**", 20 | "next-env.d.ts", 21 | ], 22 | }, 23 | ]; 24 | 25 | export default eslintConfig; 26 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /next-bench/cf-edition/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # OpenNext 39 | /.open-next 40 | 41 | # wrangler files 42 | .wrangler 43 | .dev.vars* 44 | !.dev.vars.example 45 | !.env.example 46 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vercel-edition", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbopack", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "eslint" 10 | }, 11 | "dependencies": { 12 | "react": "19.1.0", 13 | "react-dom": "19.1.0", 14 | "next": "15.4.6" 15 | }, 16 | "devDependencies": { 17 | "typescript": "^5", 18 | "@types/node": "^20", 19 | "@types/react": "^19", 20 | "@types/react-dom": "^19", 21 | "@tailwindcss/postcss": "^4", 22 | "tailwindcss": "^4", 23 | "eslint": "^9", 24 | "eslint-config-next": "15.4.6", 25 | "@eslint/eslintrc": "^3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /next-bench/cf-edition/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import Image from "next/image"; 4 | 5 | export default async function Home() { 6 | const currentTime = Date.now(); 7 | return ( 8 |
9 |
10 | Next.js logo 18 | 19 |
Last rendered at: {currentTime}
20 |
21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 15 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 16 | // 17 | // To make changes to top-level options such as include and exclude, we recommend extending 18 | // the generated config; see https://svelte.dev/docs/kit/configuration#typescript 19 | } 20 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 15 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 16 | // 17 | // To make changes to top-level options such as include and exclude, we recommend extending 18 | // the generated config; see https://svelte.dev/docs/kit/configuration#typescript 19 | } 20 | -------------------------------------------------------------------------------- /next-bench/cf-edition/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | }, 24 | "types": [ 25 | "./cloudflare-env.d.ts", 26 | "node" 27 | ] 28 | }, 29 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 30 | "exclude": ["node_modules"] 31 | } 32 | -------------------------------------------------------------------------------- /next-bench/cf-edition/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const geistSans = Geist({ 6 | variable: "--font-geist-sans", 7 | subsets: ["latin"], 8 | }); 9 | 10 | const geistMono = Geist_Mono({ 11 | variable: "--font-geist-mono", 12 | subsets: ["latin"], 13 | }); 14 | 15 | export const metadata: Metadata = { 16 | title: "Create Next App", 17 | description: "Generated by create next app", 18 | }; 19 | 20 | export default function RootLayout({ 21 | children, 22 | }: Readonly<{ 23 | children: React.ReactNode; 24 | }>) { 25 | return ( 26 | 27 | 30 | {children} 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Geist, Geist_Mono } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | const geistSans = Geist({ 6 | variable: "--font-geist-sans", 7 | subsets: ["latin"], 8 | }); 9 | 10 | const geistMono = Geist_Mono({ 11 | variable: "--font-geist-mono", 12 | subsets: ["latin"], 13 | }); 14 | 15 | export const metadata: Metadata = { 16 | title: "Create Next App", 17 | description: "Generated by create next app", 18 | }; 19 | 20 | export default function RootLayout({ 21 | children, 22 | }: Readonly<{ 23 | children: React.ReactNode; 24 | }>) { 25 | return ( 26 | 27 | 30 | {children} 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /next-bench/cf-edition/public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/README.md: -------------------------------------------------------------------------------- 1 | # sv 2 | 3 | Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). 4 | 5 | ## Creating a project 6 | 7 | If you're seeing this, you've probably already done this step. Congrats! 8 | 9 | ```sh 10 | # create a new project in the current directory 11 | npx sv create 12 | 13 | # create a new project in my-app 14 | npx sv create my-app 15 | ``` 16 | 17 | ## Developing 18 | 19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 20 | 21 | ```sh 22 | npm run dev 23 | 24 | # or start the server and open the app in a new browser tab 25 | npm run dev -- --open 26 | ``` 27 | 28 | ## Building 29 | 30 | To create a production version of your app: 31 | 32 | ```sh 33 | npm run build 34 | ``` 35 | 36 | You can preview the production build with `npm run preview`. 37 | 38 | > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. 39 | -------------------------------------------------------------------------------- /next-bench/cf-edition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-cf-bench", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev --turbopack", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", 11 | "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", 12 | "cf-typegen": "wrangler types --env-interface CloudflareEnv ./cloudflare-env.d.ts" 13 | }, 14 | "dependencies": { 15 | "@opennextjs/cloudflare": "^1.10.1", 16 | "next": "15.4.6", 17 | "react": "19.1.0", 18 | "react-dom": "19.1.0" 19 | }, 20 | "devDependencies": { 21 | "@eslint/eslintrc": "^3", 22 | "@tailwindcss/postcss": "^4", 23 | "@types/node": "^20.19.19", 24 | "@types/react": "^19", 25 | "@types/react-dom": "^19", 26 | "eslint": "^9", 27 | "eslint-config-next": "15.4.6", 28 | "tailwindcss": "^4", 29 | "typescript": "^5", 30 | "wrangler": "^4.42.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/README.md: -------------------------------------------------------------------------------- 1 | # sv 2 | 3 | Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). 4 | 5 | ## Creating a project 6 | 7 | If you're seeing this, you've probably already done this step. Congrats! 8 | 9 | ```sh 10 | # create a new project in the current directory 11 | npx sv create 12 | 13 | # create a new project in my-app 14 | npx sv create my-app 15 | ``` 16 | 17 | ## Developing 18 | 19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 20 | 21 | ```sh 22 | npm run dev 23 | 24 | # or start the server and open the app in a new browser tab 25 | npm run dev -- --open 26 | ``` 27 | 28 | ## Building 29 | 30 | To create a production version of your app: 31 | 32 | ```sh 33 | npm run build 34 | ``` 35 | 36 | You can preview the production build with `npm run preview`. 37 | 38 | > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. 39 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vercel-sveltekit-bench", 3 | "private": true, 4 | "version": "0.0.2", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite dev", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "prepare": "svelte-kit sync || echo ''", 11 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 12 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 13 | "format": "prettier --write .", 14 | "lint": "prettier --check ." 15 | }, 16 | "devDependencies": { 17 | "@sveltejs/adapter-vercel": "^5.10.3", 18 | "@sveltejs/kit": "^2.22.0", 19 | "@sveltejs/vite-plugin-svelte": "^6.0.0", 20 | "@tailwindcss/vite": "^4.0.0", 21 | "prettier": "^3.4.2", 22 | "prettier-plugin-svelte": "^3.3.3", 23 | "prettier-plugin-tailwindcss": "^0.6.11", 24 | "svelte": "^5.0.0", 25 | "svelte-check": "^4.0.0", 26 | "tailwindcss": "^4.0.0", 27 | "typescript": "^5.0.0", 28 | "vite": "^7.0.4" 29 | }, 30 | "pnpm": { 31 | "onlyBuiltDependencies": [ 32 | "esbuild" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Theo Browne 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cf-sveltekit-bench", 3 | "private": true, 4 | "version": "0.0.1", 5 | "type": "module", 6 | "scripts": { 7 | "cf-typegen": "wrangler types", 8 | "deploy": "pnpm run build && wrangler deploy", 9 | "dev": "vite dev", 10 | "build": "vite build", 11 | "preview": "vite preview", 12 | "prepare": "svelte-kit sync || echo ''", 13 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 14 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 15 | "format": "prettier --write .", 16 | "lint": "prettier --check ." 17 | }, 18 | "devDependencies": { 19 | "@sveltejs/adapter-cloudflare": "^7.2.4", 20 | "@sveltejs/kit": "^2.22.0", 21 | "@sveltejs/vite-plugin-svelte": "^6.0.0", 22 | "@tailwindcss/vite": "^4.0.0", 23 | "prettier": "^3.4.2", 24 | "prettier-plugin-svelte": "^3.3.3", 25 | "prettier-plugin-tailwindcss": "^0.6.11", 26 | "svelte": "^5.0.0", 27 | "svelte-check": "^4.0.0", 28 | "tailwindcss": "^4.0.0", 29 | "typescript": "^5.0.0", 30 | "vite": "^7.0.4", 31 | "wrangler": "^4.42.2" 32 | }, 33 | "pnpm": { 34 | "onlyBuiltDependencies": [ 35 | "esbuild" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | Icon 6 | ._* 7 | .DocumentRevisions-V100 8 | .fseventsd 9 | .Spotlight-V100 10 | .TemporaryItems 11 | .Trashes 12 | .VolumeIcon.icns 13 | .com.apple.timemachine.donotpresent 14 | .AppleDB 15 | .AppleDesktop 16 | Network Trash Folder 17 | Temporary Items 18 | .apdisk 19 | 20 | # Node.js / JavaScript 21 | node_modules/ 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | pnpm-debug.log* 26 | lerna-debug.log* 27 | .pnpm-store/ 28 | 29 | # Next.js 30 | .next/ 31 | out/ 32 | dist/ 33 | build/ 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | 37 | # Environment variables 38 | .env 39 | .env*.local 40 | .env.local 41 | .env.development.local 42 | .env.test.local 43 | .env.production.local 44 | 45 | # IDE / Editors 46 | .vscode/ 47 | .idea/ 48 | *.swp 49 | *.swo 50 | *~ 51 | .project 52 | .classpath 53 | .settings/ 54 | *.sublime-project 55 | *.sublime-workspace 56 | 57 | # Testing 58 | coverage/ 59 | *.lcov 60 | .nyc_output 61 | 62 | # Misc 63 | *.log 64 | *.pid 65 | *.seed 66 | *.pid.lock 67 | .cache/ 68 | .parcel-cache/ 69 | .turbo/ 70 | 71 | # OS generated files 72 | Thumbs.db 73 | ehthumbs.db 74 | 75 | # Cloudflare Workers 76 | .wrangler/ 77 | .dev.vars 78 | 79 | -------------------------------------------------------------------------------- /results/results-2025-10-03T08-20-57-628Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T08:20:57.628Z", 3 | "iterations": 15, 4 | "concurrency": 10, 5 | "tests": [ 6 | { 7 | "name": "shitty-sine-bench", 8 | "urls": { 9 | "cloudflare": "https://vanilla-ssr-cf.pinglabs.workers.dev/shitty-sine-bench", 10 | "vercel": "https://vanilla-bench-v2.vercel.app/api/shitty-sine-bench" 11 | }, 12 | "results": { 13 | "cloudflare": { 14 | "min": 8442.669417, 15 | "max": 53676.585167, 16 | "mean": 23001.73130006667, 17 | "successful": 15, 18 | "failed": 0, 19 | "failureRate": 0, 20 | "statusCodes": { 21 | "200": 15 22 | }, 23 | "times": [ 24 | 8442.669417, 25 | 10154.116375000001, 26 | 11277.270666999999, 27 | 13564.961667000001, 28 | 17115.160583, 29 | 21028.439749999998, 30 | 21883.011333, 31 | 11000.390875, 32 | 17430.556708999997, 33 | 27081.461542, 34 | 21229.02475, 35 | 17112.949833, 36 | 40464.244957999996, 37 | 53565.125875, 38 | 53676.585167 39 | ] 40 | }, 41 | "vercel": null 42 | } 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /next-bench/cf-edition/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.claude/settings.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "allow": [ 4 | "Bash(mkdir:*)", 5 | "Bash(git add:*)", 6 | "Bash(git commit:*)", 7 | "Bash(git push:*)", 8 | "Bash(gh pr create --title \"Add vanilla SSR benchmark\" --body \"$(cat <<''EOF''\n## Summary\n\n- Adds vanilla JavaScript SSR implementations for both CloudFlare Workers and Vercel Serverless Functions\n- Includes two benchmark routes: `/bench` (standard) and `/slower-bench` (heavier computation)\n- No framework overhead - pure string concatenation for HTML rendering\n- Uses same computational logic as Next.js benchmark (primes, fibonacci, complex data generation)\n\n## Implementation Details\n\n**CloudFlare Worker:**\n- `/bench` - Standard benchmark\n- `/slower-bench` - 5x more primes (500k), 3x more sections (150), 9x more items\n\n**Vercel Serverless:**\n- `/api/bench` - Standard benchmark\n- `/api/slower-bench` - Same heavy computation with 30s timeout\n\n## Test Plan\n\n- [ ] Deploy CloudFlare worker and verify `/bench` endpoint works\n- [ ] Deploy CloudFlare worker and verify `/slower-bench` endpoint works\n- [ ] Deploy Vercel function and verify `/api/bench` endpoint works\n- [ ] Deploy Vercel function and verify `/api/slower-bench` endpoint works\n- [ ] Run benchmark comparison between vanilla and Next.js implementations\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\nEOF\n)\")", 9 | "WebFetch(domain:github.com)" 10 | ], 11 | "deny": [], 12 | "ask": [] 13 | } 14 | } -------------------------------------------------------------------------------- /next-bench/cf-edition/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. 37 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. 37 | -------------------------------------------------------------------------------- /results/results-2025-10-03T07-45-47-919Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T07:45:47.919Z", 3 | "iterations": 10, 4 | "concurrency": 5, 5 | "tests": [ 6 | { 7 | "name": "next-js", 8 | "urls": { 9 | "cloudflare": "https://next-cf-bench.pinglabs.workers.dev/bench", 10 | "vercel": "https://vercel-ssr-bench-v2-hidden.vercel.app/bench" 11 | }, 12 | "results": { 13 | "cloudflare": { 14 | "min": 1651.1492910000002, 15 | "max": 3265.330084, 16 | "mean": 2255.794025, 17 | "successful": 10, 18 | "failed": 0, 19 | "failureRate": 0, 20 | "statusCodes": { 21 | "200": 10 22 | }, 23 | "times": [ 24 | 1906.983125, 25 | 1956.583875, 26 | 3088.2165, 27 | 2932.8678750000004, 28 | 1655.828, 29 | 1729.4208749999998, 30 | 3265.330084, 31 | 1651.1492910000002, 32 | 2123.213958, 33 | 2248.3466670000003 34 | ] 35 | }, 36 | "vercel": { 37 | "min": 395.87125000000015, 38 | "max": 1218.5598339999997, 39 | "mean": 621.1144167, 40 | "successful": 10, 41 | "failed": 0, 42 | "failureRate": 0, 43 | "statusCodes": { 44 | "200": 10 45 | }, 46 | "times": [ 47 | 658.5728749999998, 48 | 665.3004579999997, 49 | 645.3757909999995, 50 | 676.5545830000001, 51 | 1218.5598339999997, 52 | 463.62924999999996, 53 | 478.94066699999985, 54 | 491.048417, 55 | 395.87125000000015, 56 | 517.2910420000007 57 | ] 58 | } 59 | } 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /results/results-2025-10-03T07-47-28-876Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T07:47:28.876Z", 3 | "iterations": 10, 4 | "concurrency": 5, 5 | "tests": [ 6 | { 7 | "name": "next-js", 8 | "urls": { 9 | "cloudflare": "https://next-cf-bench.pinglabs.workers.dev/bench", 10 | "vercel": "https://vercel-ssr-bench-v2-hidden.vercel.app/bench" 11 | }, 12 | "results": { 13 | "cloudflare": { 14 | "min": 1413.401625, 15 | "max": 3396.980833, 16 | "mean": 2171.3084167, 17 | "successful": 10, 18 | "failed": 0, 19 | "failureRate": 0, 20 | "statusCodes": { 21 | "200": 10 22 | }, 23 | "times": [ 24 | 1822.4244999999999, 25 | 2231.907291, 26 | 2258.4694999999997, 27 | 2745.2886670000003, 28 | 1413.401625, 29 | 3396.980833, 30 | 2215.515917, 31 | 2188.552292, 32 | 1440.0625840000002, 33 | 2000.4809579999996 34 | ] 35 | }, 36 | "vercel": { 37 | "min": 365.64995799999997, 38 | "max": 549.9920830000001, 39 | "mean": 419.9091501, 40 | "successful": 10, 41 | "failed": 0, 42 | "failureRate": 0, 43 | "statusCodes": { 44 | "200": 10 45 | }, 46 | "times": [ 47 | 365.64995799999997, 48 | 395.2654589999993, 49 | 430.62737500000003, 50 | 405.36595900000066, 51 | 549.9920830000001, 52 | 395.5237079999997, 53 | 381.6696670000001, 54 | 476.4333749999996, 55 | 404.3092919999999, 56 | 394.2546250000005 57 | ] 58 | } 59 | } 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /next-bench/cf-edition/wrangler.jsonc: -------------------------------------------------------------------------------- 1 | /** 2 | * For more details on how to configure Wrangler, refer to: 3 | * https://developers.cloudflare.com/workers/wrangler/configuration/ 4 | */ 5 | { 6 | "$schema": "node_modules/wrangler/config-schema.json", 7 | "name": "next-cf-bench", 8 | "main": ".open-next/worker.js", 9 | "compatibility_date": "2025-10-01", 10 | "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"], 11 | "minify": true, 12 | "assets": { 13 | "binding": "ASSETS", 14 | "directory": ".open-next/assets", 15 | }, 16 | "observability": { 17 | "enabled": true, 18 | }, 19 | "account_id": "2a5bb09ba3a450e93ceec4918a6bd96f", 20 | /** 21 | * Smart Placement 22 | * Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement 23 | */ 24 | // "placement": { "mode": "smart" } 25 | /** 26 | * Bindings 27 | * Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including 28 | * databases, object storage, AI inference, real-time communication and more. 29 | * https://developers.cloudflare.com/workers/runtime-apis/bindings/ 30 | */ 31 | /** 32 | * Environment Variables 33 | * https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables 34 | */ 35 | // "vars": { "MY_VARIABLE": "production_value" } 36 | /** 37 | * Note: Use secrets to store sensitive data. 38 | * https://developers.cloudflare.com/workers/configuration/secrets/ 39 | */ 40 | /** 41 | * Static Assets 42 | * https://developers.cloudflare.com/workers/static-assets/binding/ 43 | */ 44 | // "assets": { "directory": "./public/", "binding": "ASSETS" } 45 | /** 46 | * Service Bindings (communicate between multiple Workers) 47 | * https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings 48 | */ 49 | // "services": [{ "binding": "MY_SERVICE", "service": "my-service" }] 50 | } 51 | -------------------------------------------------------------------------------- /react-ssr-bench/vercel-edition/api/bench.mjs: -------------------------------------------------------------------------------- 1 | // Vanilla React SSR implementation for Vercel 2 | // Uses ReactDOMServer.renderToString to render React components to HTML 3 | 4 | import React from 'react'; 5 | import { renderToString } from 'react-dom/server'; 6 | import ComplexComponent from '../ComplexComponent.mjs'; 7 | 8 | export default function handler(req, res) { 9 | console.log("rendering", Date.now()); 10 | 11 | const currentTime = new Date().toLocaleString(); 12 | 13 | // Render the React component to HTML string 14 | const componentHtml = renderToString( 15 | React.createElement('main', { 16 | style: { 17 | display: 'flex', 18 | flexDirection: 'column', 19 | alignItems: 'center', 20 | justifyContent: 'center', 21 | minHeight: '100vh' 22 | } 23 | }, 24 | React.createElement('h1', { 25 | style: { 26 | fontSize: '24px', 27 | fontWeight: 'bold', 28 | marginBottom: '16px' 29 | } 30 | }, 'Last rendered at:'), 31 | React.createElement('p', { 32 | style: { 33 | fontSize: '18px', 34 | fontFamily: 'monospace', 35 | padding: '16px', 36 | borderRadius: '4px' 37 | } 38 | }, currentTime), 39 | React.createElement(ComplexComponent) 40 | ) 41 | ); 42 | 43 | const html = ` 44 | 45 | 46 | 47 | 48 | 49 | React SSR Benchmark - Vercel 50 | 57 | 58 | 59 | ${componentHtml} 60 | 61 | 62 | `; 63 | 64 | res.setHeader('Content-Type', 'text/html; charset=utf-8'); 65 | res.status(200).send(html); 66 | } 67 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/lib/complex.ts: -------------------------------------------------------------------------------- 1 | function isPrime(num: number) { 2 | if (num <= 1) return false; 3 | if (num <= 3) return true; 4 | if (num % 2 === 0 || num % 3 === 0) return false; 5 | for (let i = 5; i * i <= num; i += 6) { 6 | if (num % i === 0 || num % (i + 2) === 0) return false; 7 | } 8 | return true; 9 | } 10 | 11 | function calculatePrimes(limit: number) { 12 | const primes: number[] = []; 13 | for (let i = 2; i <= limit; i++) { 14 | if (isPrime(i)) { 15 | primes.push(i); 16 | } 17 | } 18 | return primes; 19 | } 20 | 21 | function fibonacci(n: number): number { 22 | if (n <= 1) return n; 23 | let a = 0, 24 | b = 1; 25 | for (let i = 2; i <= n; i++) { 26 | const temp = a + b; 27 | a = b; 28 | b = temp; 29 | } 30 | return b; 31 | } 32 | 33 | function generateComplexData() { 34 | // Calculate first 10,000 primes 35 | const primes = calculatePrimes(100000); 36 | 37 | // Calculate fibonacci numbers 38 | const fibs = Array.from({ length: 100 }, (_, i) => fibonacci(i)); 39 | 40 | // Generate complex nested data 41 | const complexData = Array.from({ length: 50 }, (_, i) => ({ 42 | id: i, 43 | title: `Section ${i + 1}`, 44 | primes: primes.slice(i * 100, (i + 1) * 100), 45 | fibonacci: fibs, 46 | items: Array.from({ length: 20 }, (_, j) => ({ 47 | id: j, 48 | value: Math.sqrt(i * 1000 + j), 49 | description: `Item ${j} in section ${i}`, 50 | metadata: { 51 | timestamp: Date.now(), 52 | hash: (i * j * 12345).toString(36), 53 | complexity: Math.sin(i) * Math.cos(j) 54 | } 55 | })) 56 | })); 57 | 58 | return complexData; 59 | } 60 | 61 | export function getComplexData() { 62 | // Expensive server-side computation 63 | const data = generateComplexData(); 64 | 65 | // Additional expensive operations 66 | const totalPrimes = data.reduce((sum, section) => sum + section.primes.length, 0); 67 | const averageFib = data[0].fibonacci.reduce((a, b) => a + b, 0) / data[0].fibonacci.length; 68 | 69 | return { 70 | data, 71 | totalPrimes, 72 | averageFib 73 | }; 74 | } 75 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/src/lib/complex.ts: -------------------------------------------------------------------------------- 1 | function isPrime(num: number) { 2 | if (num <= 1) return false; 3 | if (num <= 3) return true; 4 | if (num % 2 === 0 || num % 3 === 0) return false; 5 | for (let i = 5; i * i <= num; i += 6) { 6 | if (num % i === 0 || num % (i + 2) === 0) return false; 7 | } 8 | return true; 9 | } 10 | 11 | function calculatePrimes(limit: number) { 12 | const primes: number[] = []; 13 | for (let i = 2; i <= limit; i++) { 14 | if (isPrime(i)) { 15 | primes.push(i); 16 | } 17 | } 18 | return primes; 19 | } 20 | 21 | function fibonacci(n: number): number { 22 | if (n <= 1) return n; 23 | let a = 0, 24 | b = 1; 25 | for (let i = 2; i <= n; i++) { 26 | const temp = a + b; 27 | a = b; 28 | b = temp; 29 | } 30 | return b; 31 | } 32 | 33 | function generateComplexData() { 34 | // Calculate first 10,000 primes 35 | const primes = calculatePrimes(100000); 36 | 37 | // Calculate fibonacci numbers 38 | const fibs = Array.from({ length: 100 }, (_, i) => fibonacci(i)); 39 | 40 | // Generate complex nested data 41 | const complexData = Array.from({ length: 50 }, (_, i) => ({ 42 | id: i, 43 | title: `Section ${i + 1}`, 44 | primes: primes.slice(i * 100, (i + 1) * 100), 45 | fibonacci: fibs, 46 | items: Array.from({ length: 20 }, (_, j) => ({ 47 | id: j, 48 | value: Math.sqrt(i * 1000 + j), 49 | description: `Item ${j} in section ${i}`, 50 | metadata: { 51 | timestamp: Date.now(), 52 | hash: (i * j * 12345).toString(36), 53 | complexity: Math.sin(i) * Math.cos(j) 54 | } 55 | })) 56 | })); 57 | 58 | return complexData; 59 | } 60 | 61 | export function getComplexData() { 62 | // Expensive server-side computation 63 | const data = generateComplexData(); 64 | 65 | // Additional expensive operations 66 | const totalPrimes = data.reduce((sum, section) => sum + section.primes.length, 0); 67 | const averageFib = data[0].fibonacci.reduce((a, b) => a + b, 0) / data[0].fibonacci.length; 68 | 69 | return { 70 | data, 71 | totalPrimes, 72 | averageFib 73 | }; 74 | } 75 | -------------------------------------------------------------------------------- /results/results-2025-10-03T08-27-23-461Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T08:27:23.461Z", 3 | "iterations": 15, 4 | "concurrency": 10, 5 | "tests": [ 6 | { 7 | "name": "shitty-sine-bench", 8 | "urls": { 9 | "cloudflare": "https://vanilla-ssr-cf.pinglabs.workers.dev/shitty-sine-bench", 10 | "vercel": "https://vanilla-bench-v2.vercel.app/api/shitty-sine-bench" 11 | }, 12 | "results": { 13 | "cloudflare": { 14 | "min": 8475.976292000001, 15 | "max": 30460.939625, 16 | "mean": 17203.91736673333, 17 | "successful": 15, 18 | "failed": 0, 19 | "failureRate": 0, 20 | "statusCodes": { 21 | "200": 15 22 | }, 23 | "times": [ 24 | 8475.976292000001, 25 | 9289.245625, 26 | 10188.364625, 27 | 10188.387499999999, 28 | 10191.878334, 29 | 16632.606666, 30 | 18430.454375, 31 | 9771.348957999999, 32 | 20220.10175, 33 | 11039.441542, 34 | 16401.152125, 35 | 28028.450708999997, 36 | 30460.939625, 37 | 28493.658000000003, 38 | 30246.754374999997 39 | ] 40 | }, 41 | "vercel": { 42 | "min": 37332.182, 43 | "max": 38050.737541999995, 44 | "mean": 37703.9106888, 45 | "successful": 15, 46 | "failed": 0, 47 | "failureRate": 0, 48 | "statusCodes": { 49 | "200": 15 50 | }, 51 | "times": [ 52 | 37671.882833, 53 | 37762.425958, 54 | 37769.308625, 55 | 37788.560332999994, 56 | 37788.640875, 57 | 37834.09325, 58 | 37942.362542, 59 | 37971.669208, 60 | 37973.280750000005, 61 | 38050.737541999995, 62 | 37332.182, 63 | 37451.88562500001, 64 | 37351.546333000006, 65 | 37431.262833, 66 | 37438.82162500001 67 | ] 68 | } 69 | } 70 | } 71 | ] 72 | } -------------------------------------------------------------------------------- /results/results-2025-10-03T08-33-14-536Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T08:33:14.536Z", 3 | "iterations": 15, 4 | "concurrency": 10, 5 | "tests": [ 6 | { 7 | "name": "realistic-math-bench", 8 | "urls": { 9 | "cloudflare": "https://vanilla-ssr-cf.pinglabs.workers.dev/realistic-math-bench", 10 | "vercel": "https://vanilla-bench-v2.vercel.app/api/realistic-math-bench" 11 | }, 12 | "results": { 13 | "cloudflare": { 14 | "min": 735.302334, 15 | "max": 3240.084708, 16 | "mean": 1655.963944533333, 17 | "successful": 15, 18 | "failed": 0, 19 | "failureRate": 0, 20 | "statusCodes": { 21 | "200": 15 22 | }, 23 | "times": [ 24 | 836.798584, 25 | 1111.394125, 26 | 1136.34475, 27 | 1164.199834, 28 | 1389.221333, 29 | 1901.307583, 30 | 1917.802, 31 | 832.7620830000001, 32 | 1956.107375, 33 | 735.302334, 34 | 1707.056042, 35 | 1627.5651249999999, 36 | 2825.303958, 37 | 3240.084708, 38 | 2458.209334 39 | ] 40 | }, 41 | "vercel": { 42 | "min": 624.993708, 43 | "max": 1259.1623750000003, 44 | "mean": 1027.2968916666666, 45 | "successful": 15, 46 | "failed": 0, 47 | "failureRate": 0, 48 | "statusCodes": { 49 | "200": 15 50 | }, 51 | "times": [ 52 | 843.6147079999996, 53 | 1103.3008750000004, 54 | 1211.6844579999997, 55 | 1234.1493330000003, 56 | 1239.7498329999994, 57 | 1247.3990420000005, 58 | 1254.6262920000004, 59 | 1254.637417, 60 | 1256.9531670000001, 61 | 1259.1623750000003, 62 | 624.993708, 63 | 713.8372920000002, 64 | 727.9096250000002, 65 | 715.9102499999999, 66 | 721.5249999999996 67 | ] 68 | } 69 | } 70 | } 71 | ] 72 | } -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/api/shitty-sine-bench.js: -------------------------------------------------------------------------------- 1 | // Shitty Sine Bench - CPU-bound floating-point math benchmark 2 | // Based on https://github.com/Buzut/serverless-benchmark 3 | 4 | export default function handler(req, res) { 5 | console.log("rendering shitty sine bench", Date.now()); 6 | 7 | // CPU-bound floating-point math benchmark 8 | let sum = 0; 9 | for (let i = 0; i < 100_000_000; i++) { 10 | const result = Math.sin(Math.PI * i) * Math.cos(Math.PI * i); 11 | sum += result; 12 | } 13 | 14 | const currentTime = new Date().toLocaleString(); 15 | 16 | const html = ` 17 | 18 | 19 | 20 | 21 | 22 | Shitty Sine Benchmark - Vercel 23 | 30 | 31 | 32 |
33 |

Shitty Sine Bench - Last rendered at:

34 |

${currentTime}

35 |
36 |

CPU-Bound Floating-Point Math Test

37 |

Computed 100 million sine/cosine operations

38 |
39 |

Result Sum: ${sum.toFixed(6)}

40 |
41 |
42 |
43 | 44 | 45 | `; 46 | 47 | res.setHeader('Content-Type', 'text/html; charset=utf-8'); 48 | res.status(200).send(html); 49 | } 50 | -------------------------------------------------------------------------------- /react-ssr-bench/cf-edition/worker.js: -------------------------------------------------------------------------------- 1 | // React SSR CloudFlare Worker implementation 2 | // Uses ReactDOMServer.renderToString to render React components to HTML 3 | 4 | import React from 'react'; 5 | import { renderToString } from 'react-dom/server'; 6 | import ComplexComponent from './ComplexComponent.mjs'; 7 | 8 | export default { 9 | async fetch(request, env, ctx) { 10 | const url = new URL(request.url); 11 | 12 | if (url.pathname === '/bench') { 13 | console.log("rendering", Date.now()); 14 | 15 | const currentTime = new Date().toLocaleString(); 16 | 17 | // Render the React component to HTML string 18 | const componentHtml = renderToString( 19 | React.createElement('main', { 20 | style: { 21 | display: 'flex', 22 | flexDirection: 'column', 23 | alignItems: 'center', 24 | justifyContent: 'center', 25 | minHeight: '100vh' 26 | } 27 | }, 28 | React.createElement('h1', { 29 | style: { 30 | fontSize: '24px', 31 | fontWeight: 'bold', 32 | marginBottom: '16px' 33 | } 34 | }, 'Last rendered at:'), 35 | React.createElement('p', { 36 | style: { 37 | fontSize: '18px', 38 | fontFamily: 'monospace', 39 | padding: '16px', 40 | borderRadius: '4px' 41 | } 42 | }, currentTime), 43 | React.createElement(ComplexComponent) 44 | ) 45 | ); 46 | 47 | const html = ` 48 | 49 | 50 | 51 | 52 | 53 | React SSR Benchmark - CloudFlare 54 | 61 | 62 | 63 | ${componentHtml} 64 | 65 | 66 | `; 67 | 68 | return new Response(html, { 69 | headers: { 70 | 'Content-Type': 'text/html; charset=utf-8', 71 | }, 72 | }); 73 | } 74 | 75 | return new Response('Not Found', { status: 404 }); 76 | }, 77 | }; 78 | -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/api/realistic-math-bench.js: -------------------------------------------------------------------------------- 1 | // Realistic Math Bench - CPU-bound integer and string operations benchmark 2 | 3 | export default function handler(req, res) { 4 | console.log("rendering realistic math bench", Date.now()); 5 | 6 | // CPU-bound integer and string operations benchmark 7 | let result = 0; 8 | 9 | // Integer arithmetic and bitwise operations 10 | for (let i = 0; i < 10_000_000; i++) { 11 | result += ((i * 31) ^ (i << 3)) & 0xFFFFFFFF; 12 | result = (result * 1103515245 + 12345) & 0x7FFFFFFF; // LCG 13 | } 14 | 15 | // Array sorting and manipulation 16 | const arrays = []; 17 | for (let i = 0; i < 100; i++) { 18 | const arr = Array.from({ length: 10000 }, (_, idx) => (idx * 7919) % 10007); 19 | arr.sort((a, b) => a - b); 20 | arrays.push(arr.reduce((acc, val) => acc + val, 0)); 21 | } 22 | 23 | // String operations and hashing 24 | let stringHash = 0; 25 | const baseStr = "benchmark-test-string-"; 26 | for (let i = 0; i < 1_000_000; i++) { 27 | const str = baseStr + i; 28 | for (let j = 0; j < str.length; j++) { 29 | stringHash = ((stringHash << 5) - stringHash + str.charCodeAt(j)) | 0; 30 | } 31 | } 32 | 33 | // Prime counting with optimized trial division 34 | let primeCount = 0; 35 | for (let n = 2; n < 100000; n++) { 36 | let isPrime = n > 1; 37 | if (n > 2 && n % 2 === 0) isPrime = false; 38 | else { 39 | for (let i = 3; i * i <= n; i += 2) { 40 | if (n % i === 0) { 41 | isPrime = false; 42 | break; 43 | } 44 | } 45 | } 46 | if (isPrime) primeCount++; 47 | } 48 | 49 | const currentTime = new Date().toLocaleString(); 50 | 51 | const html = ` 52 | 53 | 54 | 55 | 56 | 57 | Realistic Math Benchmark - Vercel 58 | 65 | 66 | 67 |
68 |

Realistic Math Bench - Last rendered at:

69 |

${currentTime}

70 |
71 |

CPU-Bound Integer & String Operations Test

72 |

Mixed workload: integer arithmetic, array sorting, string hashing, prime counting

73 |
74 |

Integer result: ${result}

75 |

Array ops: ${arrays.length} sorts completed

76 |

String hash: ${stringHash}

77 |

Primes found: ${primeCount}

78 |
79 |
80 |
81 | 82 | 83 | `; 84 | 85 | res.setHeader('Content-Type', 'text/html; charset=utf-8'); 86 | res.status(200).send(html); 87 | } 88 | -------------------------------------------------------------------------------- /results/results-2025-10-03T04-45-09-593Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T04:45:09.593Z", 3 | "iterations": 50, 4 | "concurrency": 5, 5 | "urls": { 6 | "cloudflare": "https://my-next-app.pinglabs.workers.dev/bench", 7 | "vercel": "https://vercel-ssr-bench-v2-hidden.vercel.app/bench" 8 | }, 9 | "results": { 10 | "cloudflare": { 11 | "min": 1191.0224170000001, 12 | "max": 5731.469125, 13 | "mean": 2926.3491508800003, 14 | "successful": 50, 15 | "times": [ 16 | 2416.6785, 17 | 2532.912792, 18 | 3483.8985, 19 | 3770.285542, 20 | 1960.850417, 21 | 2103.9466249999996, 22 | 1918.5752500000003, 23 | 5424.755375000001, 24 | 1992.9818749999995, 25 | 2826.2097920000006, 26 | 3556.0495829999995, 27 | 1949.1778749999994, 28 | 1997.4820420000005, 29 | 3903.0897909999994, 30 | 3442.215792000001, 31 | 2376.548666999999, 32 | 3811.156417, 33 | 4835.6044999999995, 34 | 2039.7617499999997, 35 | 3759.0227080000004, 36 | 2841.207625000001, 37 | 1461.7577090000013, 38 | 3794.061458, 39 | 2039.9826670000002, 40 | 2979.867542, 41 | 2646.056208, 42 | 1935.9829580000005, 43 | 2115.913083999998, 44 | 4191.619208, 45 | 2162.5242909999997, 46 | 1191.0224170000001, 47 | 2067.2831250000017, 48 | 2323.9017499999973, 49 | 2377.8662079999995, 50 | 4527.1445840000015, 51 | 1908.5372080000016, 52 | 2011.9865830000017, 53 | 1931.6635419999984, 54 | 2836.633208000003, 55 | 5731.469125, 56 | 2047.926374999999, 57 | 4552.332624999999, 58 | 2860.495708000002, 59 | 3242.655458999998, 60 | 2042.888583, 61 | 3883.269375, 62 | 2867.9140420000003, 63 | 2127.337374999999, 64 | 4697.199542000002, 65 | 4817.754167000003 66 | ] 67 | }, 68 | "vercel": { 69 | "min": 658.2812090000007, 70 | "max": 1803.8688329999968, 71 | "mean": 888.7253875599996, 72 | "successful": 50, 73 | "times": [ 74 | 1761.4887919999965, 75 | 1775.168458, 76 | 1779.3300420000014, 77 | 1785.3202920000003, 78 | 1803.8688329999968, 79 | 865.8353339999958, 80 | 911.3348750000005, 81 | 959.6032089999935, 82 | 994.8877500000017, 83 | 1006.0829159999994, 84 | 862.5794170000008, 85 | 921.4702079999988, 86 | 835.5172920000041, 87 | 862.2155000000057, 88 | 1016.3819169999988, 89 | 828.5377920000028, 90 | 773.2083330000023, 91 | 811.3582499999975, 92 | 915.6860420000012, 93 | 826.7379999999976, 94 | 774.4939999999988, 95 | 803.4029999999984, 96 | 776.9205000000002, 97 | 817.4384580000042, 98 | 822.8365419999973, 99 | 803.2920830000003, 100 | 764.7289999999994, 101 | 746.4018330000035, 102 | 731.9774170000019, 103 | 877.6742919999961, 104 | 735.0408749999988, 105 | 746.4495000000024, 106 | 818.1470409999965, 107 | 736.4802090000012, 108 | 769.7497919999951, 109 | 693.2742500000022, 110 | 682.5449999999983, 111 | 693.9571250000008, 112 | 725.4955419999969, 113 | 703.9449160000004, 114 | 679.9225420000002, 115 | 677.6500419999938, 116 | 671.6403750000027, 117 | 658.2812090000007, 118 | 692.0867500000022, 119 | 724.9252500000002, 120 | 662.711374999999, 121 | 737.1427079999994, 122 | 699.3359169999967, 123 | 711.7085829999996 124 | ] 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /results/results-2025-10-03T03-52-47-398Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T03:52:47.398Z", 3 | "iterations": 50, 4 | "concurrency": 5, 5 | "urls": { 6 | "cloudflare": "https://my-next-app.pinglabs.workers.dev/bench", 7 | "vercel": "https://vercel-ssr-bench-v2.vercel.app/bench" 8 | }, 9 | "results": { 10 | "cloudflare": { 11 | "min": 1529.843417, 12 | "max": 5098.460417000002, 13 | "mean": 3073.550775759999, 14 | "successful": 50, 15 | "times": [ 16 | 2680.013666, 17 | 2689.2094580000003, 18 | 2692.236458, 19 | 2720.258666, 20 | 3502.510708, 21 | 2552.5455, 22 | 2590.514625, 23 | 2593.02675, 24 | 2631.099459, 25 | 2837.3067920000003, 26 | 2665.324375, 27 | 2664.971708, 28 | 2666.499583, 29 | 2652.8957499999997, 30 | 3165.5436250000002, 31 | 2509.7545410000002, 32 | 2481.9326249999995, 33 | 2514.4390409999996, 34 | 3486.209417000001, 35 | 3149.261875, 36 | 3146.6831249999996, 37 | 3148.0747919999994, 38 | 4330.262458000001, 39 | 2950.340458999999, 40 | 2513.0032920000012, 41 | 2525.1031669999993, 42 | 4692.887624999999, 43 | 3357.8024579999983, 44 | 2649.457916000001, 45 | 4092.7290829999984, 46 | 4113.402291999999, 47 | 5075.59425, 48 | 3133.7867910000023, 49 | 2323.6752080000006, 50 | 2338.8881669999973, 51 | 4431.955041999998, 52 | 3217.923791000001, 53 | 3059.235625000001, 54 | 4067.6617909999986, 55 | 4078.4597079999985, 56 | 5098.460417000002, 57 | 3302.3940000000002, 58 | 3329.315666999999, 59 | 2556.999499999998, 60 | 1529.843417, 61 | 2569.2884999999987, 62 | 2717.226834000001, 63 | 2718.967208000002, 64 | 2375.0465000000004, 65 | 2787.515083000002 66 | ] 67 | }, 68 | "vercel": { 69 | "min": 1017.2759170000063, 70 | "max": 1834.1857079999972, 71 | "mean": 1154.93814744, 72 | "successful": 50, 73 | "times": [ 74 | 1600.272874999995, 75 | 1659.5866659999992, 76 | 1689.5220829999998, 77 | 1694.7606250000026, 78 | 1834.1857079999972, 79 | 1151.4442909999998, 80 | 1097.485415999996, 81 | 1145.0686250000072, 82 | 1226.2390000000014, 83 | 1118.911874999998, 84 | 1020.1500000000015, 85 | 1035.5258329999997, 86 | 1107.536417000003, 87 | 1052.9203329999946, 88 | 1045.0685409999933, 89 | 1029.4395000000004, 90 | 1137.8589999999967, 91 | 1089.865291000002, 92 | 1095.1130419999972, 93 | 1044.4334159999999, 94 | 1042.6650410000002, 95 | 1061.1520000000019, 96 | 1040.703125, 97 | 1098.2588749999995, 98 | 1063.7742920000019, 99 | 1032.9456250000003, 100 | 1140.701000000001, 101 | 1017.2759170000063, 102 | 1060.6033749999988, 103 | 1088.330332999998, 104 | 1071.4924580000006, 105 | 1045.044624999995, 106 | 1055.484666999997, 107 | 1105.025542000003, 108 | 1182.5550829999993, 109 | 1071.0461250000008, 110 | 1036.7266669999954, 111 | 1028.5291670000006, 112 | 1092.1376670000027, 113 | 1067.0612920000058, 114 | 1048.019917000005, 115 | 1053.922166999997, 116 | 1099.7347080000036, 117 | 1241.427791000002, 118 | 1324.5220409999965, 119 | 1053.9372920000023, 120 | 1117.8135419999962, 121 | 1432.0643340000024, 122 | 1056.2891670000026, 123 | 1042.2750000000015 124 | ] 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /results/results-2025-10-03T03-55-30-909Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T03:55:30.909Z", 3 | "iterations": 50, 4 | "concurrency": 5, 5 | "urls": { 6 | "cloudflare": "https://my-next-app.pinglabs.workers.dev/bench", 7 | "vercel": "https://vercel-ssr-bench-v2.vercel.app/bench" 8 | }, 9 | "results": { 10 | "cloudflare": { 11 | "min": 1469.036208999998, 12 | "max": 6009.688167, 13 | "mean": 2964.749892580001, 14 | "successful": 50, 15 | "times": [ 16 | 2600.0234170000003, 17 | 2660.965875, 18 | 3309.5207920000003, 19 | 3899.004667, 20 | 4665.364541, 21 | 2113.5173330000002, 22 | 2529.328375, 23 | 2454.311333, 24 | 1991.1632920000002, 25 | 2492.5689999999995, 26 | 4398.706958000001, 27 | 1952.2844169999998, 28 | 2430.368708000001, 29 | 5056.432999999999, 30 | 2307.5509170000005, 31 | 2126.947083000001, 32 | 2406.181292000001, 33 | 3502.381542, 34 | 2032.991583000001, 35 | 3278.1051669999997, 36 | 1832.3902089999992, 37 | 3513.33425, 38 | 3808.8647500000006, 39 | 1541.7013750000006, 40 | 2549.3819999999996, 41 | 3452.5482500000016, 42 | 3455.181917, 43 | 2117.1423749999994, 44 | 2651.584334000001, 45 | 2653.120332999999, 46 | 2107.9009159999987, 47 | 3380.3639999999978, 48 | 2751.464250000001, 49 | 5074.648042000001, 50 | 3961.7469579999997, 51 | 2236.769042, 52 | 2692.591833000002, 53 | 1469.036208999998, 54 | 2128.560957999998, 55 | 4537.619957999999, 56 | 2890.156167000001, 57 | 2890.782792000002, 58 | 2254.2036249999983, 59 | 6009.688167, 60 | 2016.5168330000015, 61 | 2812.0474590000013, 62 | 2808.1024590000015, 63 | 4749.536416999999, 64 | 2217.5597500000003, 65 | 3465.2297089999993 66 | ] 67 | }, 68 | "vercel": { 69 | "min": 649.6226249999963, 70 | "max": 2077.2860000000037, 71 | "mean": 1000.2148366599997, 72 | "successful": 50, 73 | "times": [ 74 | 1479.3055419999982, 75 | 1912.7394999999997, 76 | 1954.391166999998, 77 | 1966.4752919999992, 78 | 2077.2860000000037, 79 | 1015.8450829999965, 80 | 839.1032919999998, 81 | 894.0211659999986, 82 | 855.2976660000058, 83 | 1054.4644580000022, 84 | 781.9564580000006, 85 | 777.6327919999967, 86 | 750.1561250000013, 87 | 811.7690000000002, 88 | 816.6354999999967, 89 | 809.1237920000058, 90 | 722.6495839999989, 91 | 737.109000000004, 92 | 727.5599169999987, 93 | 769.9549169999955, 94 | 783.6190420000057, 95 | 746.3792079999985, 96 | 751.2203339999978, 97 | 770.3558750000011, 98 | 737.401165999996, 99 | 725.1772500000006, 100 | 649.6226249999963, 101 | 697.440125000001, 102 | 1608.4069170000002, 103 | 1635.4746659999946, 104 | 1624.3851250000007, 105 | 1578.702750000004, 106 | 1582.6971670000057, 107 | 914.9417919999978, 108 | 901.0813749999943, 109 | 880.8691249999974, 110 | 896.4162920000017, 111 | 911.3408329999947, 112 | 826.7830000000031, 113 | 1017.2667920000022, 114 | 802.4482910000006, 115 | 845.7063750000016, 116 | 823.6277499999997, 117 | 775.5879159999968, 118 | 769.3536249999961, 119 | 850.9795000000013, 120 | 814.1082499999975, 121 | 792.1808749999982, 122 | 769.4852079999982, 123 | 774.2063330000019 124 | ] 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /results/results-2025-10-03T04-51-03-927Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T04:51:03.927Z", 3 | "iterations": 50, 4 | "concurrency": 5, 5 | "urls": { 6 | "cloudflare": "https://my-next-app.pinglabs.workers.dev/bench", 7 | "vercel": "https://vercel-ssr-bench-v2-hidden.vercel.app/bench" 8 | }, 9 | "results": { 10 | "cloudflare": { 11 | "min": 2174.1056670000035, 12 | "max": 12769.699750000002, 13 | "mean": 6831.345566659999, 14 | "successful": 50, 15 | "times": [ 16 | 3106.868292, 17 | 2623.4717080000005, 18 | 2548.1958750000003, 19 | 2607.7096249999995, 20 | 12746.8635, 21 | 12755.557708, 22 | 12763.004458000001, 23 | 12769.699750000002, 24 | 2522.5182080000013, 25 | 2911.3413329999985, 26 | 3017.015333000001, 27 | 2792.8748339999984, 28 | 11861.141583, 29 | 11893.516916999999, 30 | 11885.183916999998, 31 | 11900.150541, 32 | 2661.9932499999995, 33 | 2352.436249999999, 34 | 2506.3724999999977, 35 | 2764.7297499999986, 36 | 2663.827125, 37 | 11712.732583, 38 | 11708.189416999998, 39 | 11743.561375000001, 40 | 11733.476374999998, 41 | 2572.014167000001, 42 | 2414.0185420000053, 43 | 2410.997457999998, 44 | 2372.736499999999, 45 | 2551.3747909999947, 46 | 11072.365417, 47 | 11060.767334000004, 48 | 11093.805499999995, 49 | 11077.800167000001, 50 | 2788.6100000000006, 51 | 2218.2336250000008, 52 | 2433.4477499999994, 53 | 2477.3573340000003, 54 | 11017.995708000002, 55 | 11014.507667000005, 56 | 11025.545749999997, 57 | 11021.235207999998, 58 | 2362.3642499999987, 59 | 2275.2391659999994, 60 | 2174.1056670000035, 61 | 2386.5040420000005, 62 | 10290.810374999994, 63 | 10305.994583000007, 64 | 10293.423292, 65 | 10303.591832999999 66 | ] 67 | }, 68 | "vercel": { 69 | "min": 584.2391249999928, 70 | "max": 1699.2433339999989, 71 | "mean": 786.0017282999988, 72 | "successful": 50, 73 | "times": [ 74 | 899.9474159999954, 75 | 937.7375840000022, 76 | 1486.885249999992, 77 | 743.6471249999886, 78 | 723.6787500000064, 79 | 1666.6731250000012, 80 | 1699.2433339999989, 81 | 808.1710000000021, 82 | 710.4271670000016, 83 | 751.0892909999966, 84 | 906.0221250000031, 85 | 940.4157500000001, 86 | 747.1038750000007, 87 | 704.3796250000014, 88 | 740.314750000005, 89 | 855.6075830000045, 90 | 873.010042000009, 91 | 696.2795829999959, 92 | 702.1401249999908, 93 | 723.8925829999935, 94 | 778.768499999991, 95 | 811.5913750000036, 96 | 659.6451669999951, 97 | 732.0931669999991, 98 | 651.1567910000012, 99 | 778.2459579999995, 100 | 629.4941669999971, 101 | 703.1476249999978, 102 | 803.5022920000047, 103 | 677.2412080000067, 104 | 584.2391249999928, 105 | 749.0574169999891, 106 | 688.4723329999979, 107 | 719.6705840000068, 108 | 728.005124999996, 109 | 628.115082999997, 110 | 761.5362499999901, 111 | 688.3618749999878, 112 | 708.9204579999932, 113 | 681.3666249999951, 114 | 629.8521670000046, 115 | 676.2933749999938, 116 | 703.28216599999, 117 | 683.3783330000006, 118 | 656.6045000000013, 119 | 639.2427910000115, 120 | 676.0120840000018, 121 | 696.9724160000042, 122 | 719.885042000009, 123 | 739.2663329999923 124 | ] 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # CloudFlare vs Vercel benchmark 2 | 3 | This repo is meant to benchmark SSR performance between CloudFlare and Vercel. There's a lot of misinformation going around and I'm annoyed. So I wrote a really annoying component and server rendered it in a bunch of different ways. 4 | 5 | ## Results 6 | 7 | From my findings, **Vercel is 1.2x to 5x faster than CloudFlare for server rendering**. 8 | 9 | I also noticed some CF runs would perform TERRIBLY on Next and SvelteKit. Like 1/5th of attempts taking 10+ seconds (mean is closer to 1.2s). Horrible variability. 10 | 11 | Do these numbers really matter? Meh. Not really. Most "slowness" for web apps is APIs and queries being slow. CPU is rarely the bottleneck. 12 | 13 | Regardless, here's the results laid out (you can see individual runs in the `results` directory). 14 | 15 | I ran 100 iterations with every framework/provider combo. The results will shock you! 16 | 17 | All Vercel tests were run with "performance" function environments (2 vcpu and 4gb ram). I ran multiple runs on "Standard" as well (included in `/results`), difference wasn't as large as expected (~10% hit overall). 18 | 19 | ## next-js 20 | 21 | | Platform | Mean | Min | Max | Variability | 22 | | ---------- | ------ | ------ | ------ | ----------- | 23 | | Cloudflare | 1.895s | 0.800s | 3.971s | 3.171s | 24 | | Vercel | 0.534s | 0.343s | 1.442s | 1.098s | 25 | 26 | **Winner:** Vercel (3.55x faster) 27 | 28 | ## react-ssr-bench 29 | 30 | | Platform | Mean | Min | Max | Variability | 31 | | ---------- | ------ | ------ | ------ | ----------- | 32 | | Cloudflare | 0.476s | 0.227s | 1.383s | 1.156s | 33 | | Vercel | 0.138s | 0.059s | 0.635s | 0.576s | 34 | 35 | **Winner:** Vercel (3.45x faster) 36 | 37 | ## sveltekit 38 | 39 | | Platform | Mean | Min | Max | Variability | 40 | | ---------- | ------ | ------ | ------ | ----------- | 41 | | Cloudflare | 0.292s | 0.078s | 1.038s | 0.960s | 42 | | Vercel | 0.113s | 0.058s | 0.552s | 0.494s | 43 | 44 | **Winner:** Vercel (2.59x faster) 45 | 46 | ## shitty-sine-bench 47 | 48 | | Platform | Mean | Min | Max | Variability | 49 | | ---------- | ------- | ------- | ------- | ----------- | 50 | | Cloudflare | 23.401s | 8.122s | 46.326s | 38.204s | 51 | | Vercel | 36.025s | 30.376s | 38.156s | 7.781s | 52 | 53 | **Winner:** Cloudflare (1.54x faster) 54 | 55 | ## realistic-math-bench 56 | 57 | | Platform | Mean | Min | Max | Variability | 58 | | ---------- | ------ | ------ | ------ | ----------- | 59 | | Cloudflare | 1.469s | 0.751s | 3.387s | 2.636s | 60 | | Vercel | 0.702s | 0.463s | 1.136s | 0.673s | 61 | 62 | **Winner:** Vercel (2.09x faster) 63 | 64 | ## vanilla-slower 65 | 66 | | Platform | Mean | Min | Max | Variability | 67 | | ---------- | ------ | ------ | ------ | ----------- | 68 | | Cloudflare | 0.220s | 0.104s | 0.620s | 0.516s | 69 | | Vercel | 0.208s | 0.119s | 0.743s | 0.624s | 70 | 71 | **Winner:** Vercel (1.06x faster) 72 | 73 | ## FAQ 74 | 75 | ### Why do this? 76 | 77 | Someone was lying on the internet and it went viral. I was annoyed. This should show why. 78 | 79 | ### Isn't this measuring round trip time? Why not measure actual compute time? 80 | 81 | Fun fact - you \_can't use `performance.now()` on CloudFlare. It's [frozen due to spectre side channeling](https://developers.cloudflare.com/workers/runtime-apis/performance/#:~:text=When%20Workers%20are%20deployed%20to%20Cloudflare%2C%20as%20a%20security%20measure%20to%20mitigate%20against%20Spectre%20attacks). Since CloudFlare runs your code in an isolate instead of a vm, you're sharing memory with other workers by other devs. They have to do shit like this to keep your data safe. 82 | 83 | ### What is the "shitty-sine-bench" test about? 84 | 85 | There's another youtuber who claimed CF is 3x faster than Vercel. He's mostly wrong. 86 | 87 | CF is ~2x faster at computing floating point numbers and sine/cosine operations. In more realistic math, Vercel is faster (see `realistic-math-bench`). 88 | -------------------------------------------------------------------------------- /sveltekit-bench/cf-edition/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
10 |

11 | Complex Server-Rendered Component 12 |

13 | 14 |
17 |

Statistics

18 |

Total Prime Numbers: {totalPrimes}

19 |

20 | Average Fibonacci Value: {averageFib.toFixed(2)} 21 |

22 |

Total Sections: {data.length}

23 |
24 | 25 | {#each data as section} 26 |
29 |

30 | {section.title} 31 |

32 | 33 |
34 |

35 | Prime Numbers (100 samples) 36 |

37 |
38 | {#each section.primes as prime} 39 |
42 | {prime} 43 |
44 | {/each} 45 |
46 |
47 | 48 |
49 |

50 | Fibonacci Sequence 51 |

52 |
53 | {#each section.fibonacci as fib} 54 |
57 | {fib} 58 |
59 | {/each} 60 |
61 |
62 | 63 |
64 |

65 | Items ({section.items.length}) 66 |

67 |
68 | {#each section.items as item} 69 |
72 |

73 | Item {item.id} 74 |

75 |

76 | {item.description} 77 |

78 |

79 | Value: {item.value.toFixed(4)} 80 |

81 |
82 |

Hash: {item.metadata.hash}

83 |

Complexity: {item.metadata.complexity.toFixed(6)}

84 |

Timestamp: {item.metadata.timestamp}

85 |
86 |
87 | {/each} 88 |
89 |
90 |
91 | {/each} 92 | 93 |
96 |

97 | Additional Computations 98 |

99 |
100 | {#each computations as { n, factorial }} 101 |
104 |

105 | n={n}, f={factorial.toExponential(2)} 106 |

107 |
108 | {/each} 109 |
110 |
111 |
112 | -------------------------------------------------------------------------------- /sveltekit-bench/vercel-edition/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
10 |

11 | Complex Server-Rendered Component 12 |

13 | 14 |
17 |

Statistics

18 |

Total Prime Numbers: {totalPrimes}

19 |

20 | Average Fibonacci Value: {averageFib.toFixed(2)} 21 |

22 |

Total Sections: {data.length}

23 |
24 | 25 | {#each data as section} 26 |
29 |

30 | {section.title} 31 |

32 | 33 |
34 |

35 | Prime Numbers (100 samples) 36 |

37 |
38 | {#each section.primes as prime} 39 |
42 | {prime} 43 |
44 | {/each} 45 |
46 |
47 | 48 |
49 |

50 | Fibonacci Sequence 51 |

52 |
53 | {#each section.fibonacci as fib} 54 |
57 | {fib} 58 |
59 | {/each} 60 |
61 |
62 | 63 |
64 |

65 | Items ({section.items.length}) 66 |

67 |
68 | {#each section.items as item} 69 |
72 |

73 | Item {item.id} 74 |

75 |

76 | {item.description} 77 |

78 |

79 | Value: {item.value.toFixed(4)} 80 |

81 |
82 |

Hash: {item.metadata.hash}

83 |

Complexity: {item.metadata.complexity.toFixed(6)}

84 |

Timestamp: {item.metadata.timestamp}

85 |
86 |
87 | {/each} 88 |
89 |
90 |
91 | {/each} 92 | 93 |
96 |

97 | Additional Computations 98 |

99 |
100 | {#each computations as { n, factorial }} 101 |
104 |

105 | n={n}, f={factorial.toExponential(2)} 106 |

107 |
108 | {/each} 109 |
110 |
111 |
112 | -------------------------------------------------------------------------------- /results/results-2025-10-03T05-10-20-674Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T05:10:20.674Z", 3 | "iterations": 50, 4 | "concurrency": 5, 5 | "tests": [ 6 | { 7 | "name": "vanilla-slower", 8 | "urls": { 9 | "cloudflare": "https://vanilla-ssr-cf.pinglabs.workers.dev/slower-bench", 10 | "vercel": "https://vanilla-bench-v2.vercel.app/api/slower-bench" 11 | }, 12 | "results": { 13 | "cloudflare": { 14 | "min": 291.8374590000001, 15 | "max": 1324.8145, 16 | "mean": 687.4629942200002, 17 | "successful": 50, 18 | "times": [ 19 | 644.409, 20 | 671.926834, 21 | 924.0964590000001, 22 | 389.77612499999987, 23 | 1079.545584, 24 | 1324.8145, 25 | 291.8374590000001, 26 | 955.2311669999999, 27 | 338.8775830000002, 28 | 829.7432500000001, 29 | 847.382083, 30 | 431.7842919999998, 31 | 793.2221669999999, 32 | 831.4960420000002, 33 | 385.3800839999999, 34 | 863.528125, 35 | 360.94399999999996, 36 | 820.6214169999998, 37 | 1047.6610000000003, 38 | 723.7598749999997, 39 | 390.175917, 40 | 771.5470829999999, 41 | 589.1775000000002, 42 | 339.1861249999997, 43 | 788.3087500000001, 44 | 370.517875, 45 | 777.3325, 46 | 800.0969580000001, 47 | 365.5950840000005, 48 | 798.4767919999995, 49 | 825.6122909999995, 50 | 360.23741599999994, 51 | 785.3057919999997, 52 | 789.758167, 53 | 428.1715829999994, 54 | 793.9465829999999, 55 | 828.819375, 56 | 368.5630420000007, 57 | 853.416166, 58 | 361.31237500000043, 59 | 983.6282080000001, 60 | 1175.784834, 61 | 327.88770900000054, 62 | 968.9114170000003, 63 | 368.7660829999995, 64 | 1024.7928329999995, 65 | 660.6681660000004, 66 | 330.6675000000005, 67 | 839.1995000000006, 68 | 751.249041 69 | ] 70 | }, 71 | "vercel": { 72 | "min": 417.16399999999885, 73 | "max": 1868.6770000000015, 74 | "mean": 750.9606665799994, 75 | "successful": 50, 76 | "times": [ 77 | 685.5335840000007, 78 | 1238.7282920000007, 79 | 570.1138329999994, 80 | 1266.8601659999995, 81 | 1393.5335829999995, 82 | 1525.9900829999997, 83 | 730.7174169999998, 84 | 742.644209, 85 | 824.5097499999993, 86 | 901.2189159999998, 87 | 495.24012500000026, 88 | 673.8733329999995, 89 | 757.6413329999996, 90 | 1653.145625000001, 91 | 685.230916999999, 92 | 675.9297079999997, 93 | 650.0225829999999, 94 | 716.2131250000002, 95 | 666.1337500000009, 96 | 673.8456670000014, 97 | 471.7798330000005, 98 | 812.7110830000001, 99 | 695.1819999999989, 100 | 563.4992500000008, 101 | 498.9078329999993, 102 | 541.7232089999998, 103 | 594.6832909999994, 104 | 695.2964999999986, 105 | 613.2730419999989, 106 | 637.8142079999998, 107 | 528.7985419999986, 108 | 530.5334999999995, 109 | 686.0293329999986, 110 | 704.490667, 111 | 482.1331659999996, 112 | 932.6870000000017, 113 | 792.3377079999991, 114 | 470.686040999999, 115 | 675.5405419999988, 116 | 667.4423330000009, 117 | 552.2041659999995, 118 | 1868.6770000000015, 119 | 553.7102919999998, 120 | 745.6499159999985, 121 | 560.9583330000005, 122 | 610.8134169999994, 123 | 715.0324579999997, 124 | 417.16399999999885, 125 | 862.8724999999995, 126 | 538.276167 127 | ] 128 | } 129 | } 130 | } 131 | ] 132 | } -------------------------------------------------------------------------------- /results/results-2025-10-03T06-10-12-995Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T06:10:12.995Z", 3 | "iterations": 50, 4 | "concurrency": 5, 5 | "tests": [ 6 | { 7 | "name": "react-ssr-bench", 8 | "urls": { 9 | "cloudflare": "https://react-ssr-cf.pinglabs.workers.dev/bench", 10 | "vercel": "https://react-ssr-bench-v2.vercel.app/api/bench" 11 | }, 12 | "results": { 13 | "cloudflare": { 14 | "min": 533.3488749999997, 15 | "max": 1747.198625, 16 | "mean": 906.06939748, 17 | "successful": 50, 18 | "times": [ 19 | 903.796167, 20 | 917.90525, 21 | 1412.9009999999998, 22 | 1416.408458, 23 | 1747.198625, 24 | 863.196417, 25 | 1258.1682919999998, 26 | 833.7267080000001, 27 | 1106.161, 28 | 904.462792, 29 | 1221.4470419999998, 30 | 766.8407500000003, 31 | 1141.020666, 32 | 647.332292, 33 | 577.6899579999999, 34 | 1132.950667, 35 | 563.6366250000001, 36 | 991.6308750000003, 37 | 597.0323750000002, 38 | 970.8500409999997, 39 | 582.8151670000002, 40 | 991.6099580000005, 41 | 571.3832080000002, 42 | 992.4291670000002, 43 | 625.7342499999995, 44 | 990.9577920000002, 45 | 674.6867499999998, 46 | 979.04475, 47 | 673.7225829999998, 48 | 1035.0526250000003, 49 | 696.6116250000005, 50 | 1044.8060829999995, 51 | 745.4599590000007, 52 | 1080.320208, 53 | 736.398083, 54 | 1010.634, 55 | 730.1162080000004, 56 | 986.0870420000001, 57 | 787.7637919999997, 58 | 1022.1967919999997, 59 | 810.9342079999997, 60 | 1137.8111660000004, 61 | 638.5849999999991, 62 | 1147.7869579999997, 63 | 533.3488749999997, 64 | 1015.0788750000002, 65 | 600.065125000001, 66 | 929.2171249999992, 67 | 649.1707079999996, 68 | 909.2857920000006 69 | ] 70 | }, 71 | "vercel": { 72 | "min": 82.86420799999905, 73 | "max": 647.6684999999998, 74 | "mean": 184.51504409999995, 75 | "successful": 50, 76 | "times": [ 77 | 166.96700000000055, 78 | 285.0777079999989, 79 | 94.96787500000028, 80 | 154.25504100000035, 81 | 644.1627499999995, 82 | 645.7961669999986, 83 | 647.6684999999998, 84 | 490.6272079999999, 85 | 130.65616600000067, 86 | 275.23704200000066, 87 | 167.65129199999865, 88 | 184.0639170000013, 89 | 137.05579100000068, 90 | 160.5030000000006, 91 | 172.46291700000074, 92 | 161.6299999999992, 93 | 382.786333, 94 | 143.05587500000001, 95 | 152.54462500000045, 96 | 192.97941600000013, 97 | 168.66354199999842, 98 | 258.52495799999997, 99 | 122.82658400000037, 100 | 97.62862499999937, 101 | 137.52458399999887, 102 | 119.78720800000156, 103 | 82.86420799999905, 104 | 110.28800000000047, 105 | 126.97825000000012, 106 | 100.65604100000019, 107 | 118.35420799999883, 108 | 117.17908399999942, 109 | 112.0814160000009, 110 | 150.14829199999986, 111 | 89.8199169999989, 112 | 111.96708299999955, 113 | 160.33645799999977, 114 | 182.69666600000164, 115 | 191.1284999999989, 116 | 122.16825000000063, 117 | 96.28345799999988, 118 | 105.31562499999927, 119 | 93.01633399999992, 120 | 98.29841599999963, 121 | 105.8187080000007, 122 | 126.20024999999987, 123 | 112.257708000001, 124 | 119.04091700000026, 125 | 148.69479200000023, 126 | 149.05549999999857 127 | ] 128 | } 129 | } 130 | } 131 | ] 132 | } -------------------------------------------------------------------------------- /next-bench/vercel-edition/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | export default function Home() { 4 | return ( 5 |
6 |
7 | Next.js logo 15 |
    16 |
  1. 17 | Get started by editing{" "} 18 | 19 | src/app/page.tsx 20 | 21 | . 22 |
  2. 23 |
  3. 24 | Save and see your changes instantly. 25 |
  4. 26 |
27 | 28 |
29 | 35 | Vercel logomark 42 | Deploy now 43 | 44 | 50 | Read our docs 51 | 52 |
53 |
54 | 101 |
102 | ); 103 | } 104 | -------------------------------------------------------------------------------- /results/results-2025-10-12T23-10-03-088Z.txt: -------------------------------------------------------------------------------- 1 | ============================================================ 2 | SSR Performance Benchmark: Cloudflare vs Vercel 3 | ============================================================ 4 | 5 | ------------------------------------------------------------ 6 | Test: next-js 7 | ------------------------------------------------------------ 8 | ============================================================ 9 | RESULTS (next-js) 10 | ============================================================ 11 | 12 | 📊 Cloudflare Results: 13 | Successful requests: 400/400 14 | Min: 0.598s 15 | Max: 2.741s 16 | Mean: 1.096s 17 | 18 | 📊 Vercel Results: 19 | Successful requests: 400/400 20 | Min: 0.373s 21 | Max: 1.615s 22 | Mean: 0.553s 23 | 24 | 📈 Comparison: 25 | Vercel is 1.98x faster than Cloudflare (by mean) 26 | 27 | ------------------------------------------------------------ 28 | Test: react-ssr-bench 29 | ------------------------------------------------------------ 30 | ============================================================ 31 | RESULTS (react-ssr-bench) 32 | ============================================================ 33 | 34 | 📊 Cloudflare Results: 35 | Successful requests: 400/400 36 | Min: 0.084s 37 | Max: 0.717s 38 | Mean: 0.159s 39 | 40 | 📊 Vercel Results: 41 | Successful requests: 400/400 42 | Min: 0.066s 43 | Max: 0.674s 44 | Mean: 0.127s 45 | 46 | 📈 Comparison: 47 | Vercel is 1.26x faster than Cloudflare (by mean) 48 | 49 | ------------------------------------------------------------ 50 | Test: sveltekit 51 | ------------------------------------------------------------ 52 | ============================================================ 53 | RESULTS (sveltekit) 54 | ============================================================ 55 | 56 | 📊 Cloudflare Results: 57 | Successful requests: 400/400 58 | Min: 0.071s 59 | Max: 0.692s 60 | Mean: 0.238s 61 | 62 | 📊 Vercel Results: 63 | Successful requests: 400/400 64 | Min: 0.143s 65 | Max: 0.903s 66 | Mean: 0.197s 67 | 68 | 📈 Comparison: 69 | Vercel is 1.21x faster than Cloudflare (by mean) 70 | 71 | ------------------------------------------------------------ 72 | Test: realistic-math-bench 73 | ------------------------------------------------------------ 74 | ============================================================ 75 | RESULTS (realistic-math-bench) 76 | ============================================================ 77 | 78 | 📊 Cloudflare Results: 79 | Successful requests: 400/400 80 | Min: 0.474s 81 | Max: 1.785s 82 | Mean: 0.868s 83 | 84 | 📊 Vercel Results: 85 | Successful requests: 400/400 86 | Min: 0.619s 87 | Max: 1.211s 88 | Mean: 0.728s 89 | 90 | 📈 Comparison: 91 | Vercel is 1.19x faster than Cloudflare (by mean) 92 | 93 | ------------------------------------------------------------ 94 | Test: vanilla-slower 95 | ------------------------------------------------------------ 96 | ============================================================ 97 | RESULTS (vanilla-slower) 98 | ============================================================ 99 | 100 | 📊 Cloudflare Results: 101 | Successful requests: 400/400 102 | Min: 0.125s 103 | Max: 0.542s 104 | Mean: 0.228s 105 | 106 | 📊 Vercel Results: 107 | Successful requests: 400/400 108 | Min: 0.144s 109 | Max: 0.629s 110 | Mean: 0.222s 111 | 112 | 📈 Comparison: 113 | Vercel is 1.03x faster than Cloudflare (by mean) 114 | 115 | ============================================================ 116 | 117 | 118 | ============================================================ 119 | FINAL RESULTS SUMMARY 120 | ============================================================ 121 | 122 | ## next-js 123 | 124 | | Platform | Mean | Min | Max | Variability | 125 | |------------|------|-----|-----|-------------| 126 | | Cloudflare | 1.096s | 0.598s | 2.741s | 2.143s | 127 | | Vercel | 0.553s | 0.373s | 1.615s | 1.242s | 128 | 129 | **Winner:** Vercel (1.98x faster) 130 | 131 | ## react-ssr-bench 132 | 133 | | Platform | Mean | Min | Max | Variability | 134 | |------------|------|-----|-----|-------------| 135 | | Cloudflare | 0.159s | 0.084s | 0.717s | 0.633s | 136 | | Vercel | 0.127s | 0.066s | 0.674s | 0.608s | 137 | 138 | **Winner:** Vercel (1.26x faster) 139 | 140 | ## sveltekit 141 | 142 | | Platform | Mean | Min | Max | Variability | 143 | |------------|------|-----|-----|-------------| 144 | | Cloudflare | 0.238s | 0.071s | 0.692s | 0.621s | 145 | | Vercel | 0.197s | 0.143s | 0.903s | 0.760s | 146 | 147 | **Winner:** Vercel (1.21x faster) 148 | 149 | ## realistic-math-bench 150 | 151 | | Platform | Mean | Min | Max | Variability | 152 | |------------|------|-----|-----|-------------| 153 | | Cloudflare | 0.868s | 0.474s | 1.785s | 1.311s | 154 | | Vercel | 0.728s | 0.619s | 1.211s | 0.592s | 155 | 156 | **Winner:** Vercel (1.19x faster) 157 | 158 | ## vanilla-slower 159 | 160 | | Platform | Mean | Min | Max | Variability | 161 | |------------|------|-----|-----|-------------| 162 | | Cloudflare | 0.228s | 0.125s | 0.542s | 0.417s | 163 | | Vercel | 0.222s | 0.144s | 0.629s | 0.485s | 164 | 165 | **Winner:** Vercel (1.03x faster) 166 | 167 | --- 168 | 169 | *Benchmark run: 2025-10-12 • 400 iterations • Concurrency: 20* 170 | 171 | ============================================================ 172 | -------------------------------------------------------------------------------- /results/results-2025-10-12T23-28-24-318Z.txt: -------------------------------------------------------------------------------- 1 | ============================================================ 2 | SSR Performance Benchmark: Cloudflare vs Vercel 3 | ============================================================ 4 | 5 | ------------------------------------------------------------ 6 | Test: next-js 7 | ------------------------------------------------------------ 8 | ============================================================ 9 | RESULTS (next-js) 10 | ============================================================ 11 | 12 | 📊 Cloudflare Results: 13 | Successful requests: 400/400 14 | Min: 0.604s 15 | Max: 2.600s 16 | Mean: 1.040s 17 | 18 | 📊 Vercel Results: 19 | Successful requests: 400/400 20 | Min: 0.357s 21 | Max: 1.392s 22 | Mean: 0.534s 23 | 24 | 📈 Comparison: 25 | Vercel is 1.95x faster than Cloudflare (by mean) 26 | 27 | ------------------------------------------------------------ 28 | Test: react-ssr-bench 29 | ------------------------------------------------------------ 30 | ============================================================ 31 | RESULTS (react-ssr-bench) 32 | ============================================================ 33 | 34 | 📊 Cloudflare Results: 35 | Successful requests: 400/400 36 | Min: 0.087s 37 | Max: 0.444s 38 | Mean: 0.155s 39 | 40 | 📊 Vercel Results: 41 | Successful requests: 400/400 42 | Min: 0.061s 43 | Max: 0.666s 44 | Mean: 0.122s 45 | 46 | 📈 Comparison: 47 | Vercel is 1.27x faster than Cloudflare (by mean) 48 | 49 | ------------------------------------------------------------ 50 | Test: sveltekit 51 | ------------------------------------------------------------ 52 | ============================================================ 53 | RESULTS (sveltekit) 54 | ============================================================ 55 | 56 | 📊 Cloudflare Results: 57 | Successful requests: 400/400 58 | Min: 0.069s 59 | Max: 0.761s 60 | Mean: 0.197s 61 | 62 | 📊 Vercel Results: 63 | Successful requests: 400/400 64 | Min: 0.122s 65 | Max: 0.751s 66 | Mean: 0.177s 67 | 68 | 📈 Comparison: 69 | Vercel is 1.11x faster than Cloudflare (by mean) 70 | 71 | ------------------------------------------------------------ 72 | Test: realistic-math-bench 73 | ------------------------------------------------------------ 74 | ============================================================ 75 | RESULTS (realistic-math-bench) 76 | ============================================================ 77 | 78 | 📊 Cloudflare Results: 79 | Successful requests: 400/400 80 | Min: 0.399s 81 | Max: 1.696s 82 | Mean: 0.809s 83 | 84 | 📊 Vercel Results: 85 | Successful requests: 400/400 86 | Min: 0.626s 87 | Max: 1.250s 88 | Mean: 0.704s 89 | 90 | 📈 Comparison: 91 | Vercel is 1.15x faster than Cloudflare (by mean) 92 | 93 | ------------------------------------------------------------ 94 | Test: vanilla-slower 95 | ------------------------------------------------------------ 96 | ============================================================ 97 | RESULTS (vanilla-slower) 98 | ============================================================ 99 | 100 | 📊 Cloudflare Results: 101 | Successful requests: 400/400 102 | Min: 0.110s 103 | Max: 0.813s 104 | Mean: 0.220s 105 | 106 | 📊 Vercel Results: 107 | Successful requests: 400/400 108 | Min: 0.132s 109 | Max: 0.656s 110 | Mean: 0.186s 111 | 112 | 📈 Comparison: 113 | Vercel is 1.18x faster than Cloudflare (by mean) 114 | 115 | ============================================================ 116 | 117 | 118 | ============================================================ 119 | FINAL RESULTS SUMMARY 120 | ============================================================ 121 | 122 | ## next-js 123 | 124 | | Platform | Mean | Min | Max | Variability | 125 | |------------|------|-----|-----|-------------| 126 | | Cloudflare | 1.040s | 0.604s | 2.600s | 1.997s | 127 | | Vercel | 0.534s | 0.357s | 1.392s | 1.035s | 128 | 129 | **Winner:** Vercel (1.95x faster) 130 | 131 | ## react-ssr-bench 132 | 133 | | Platform | Mean | Min | Max | Variability | 134 | |------------|------|-----|-----|-------------| 135 | | Cloudflare | 0.155s | 0.087s | 0.444s | 0.357s | 136 | | Vercel | 0.122s | 0.061s | 0.666s | 0.605s | 137 | 138 | **Winner:** Vercel (1.27x faster) 139 | 140 | ## sveltekit 141 | 142 | | Platform | Mean | Min | Max | Variability | 143 | |------------|------|-----|-----|-------------| 144 | | Cloudflare | 0.197s | 0.069s | 0.761s | 0.691s | 145 | | Vercel | 0.177s | 0.122s | 0.751s | 0.629s | 146 | 147 | **Winner:** Vercel (1.11x faster) 148 | 149 | ## realistic-math-bench 150 | 151 | | Platform | Mean | Min | Max | Variability | 152 | |------------|------|-----|-----|-------------| 153 | | Cloudflare | 0.809s | 0.399s | 1.696s | 1.296s | 154 | | Vercel | 0.704s | 0.626s | 1.250s | 0.624s | 155 | 156 | **Winner:** Vercel (1.15x faster) 157 | 158 | ## vanilla-slower 159 | 160 | | Platform | Mean | Min | Max | Variability | 161 | |------------|------|-----|-----|-------------| 162 | | Cloudflare | 0.220s | 0.110s | 0.813s | 0.703s | 163 | | Vercel | 0.186s | 0.132s | 0.656s | 0.524s | 164 | 165 | **Winner:** Vercel (1.18x faster) 166 | 167 | --- 168 | 169 | *Benchmark run: 2025-10-12 • 400 iterations • Concurrency: 20* 170 | 171 | ============================================================ 172 | -------------------------------------------------------------------------------- /results/results-2025-10-12T23-31-26-819Z.txt: -------------------------------------------------------------------------------- 1 | ============================================================ 2 | SSR Performance Benchmark: Cloudflare vs Vercel 3 | ============================================================ 4 | 5 | ------------------------------------------------------------ 6 | Test: next-js 7 | ------------------------------------------------------------ 8 | ============================================================ 9 | RESULTS (next-js) 10 | ============================================================ 11 | 12 | 📊 Cloudflare Results: 13 | Successful requests: 100/100 14 | Min: 0.616s 15 | Max: 1.910s 16 | Mean: 1.015s 17 | 18 | 📊 Vercel Results: 19 | Successful requests: 100/100 20 | Min: 0.401s 21 | Max: 0.736s 22 | Mean: 0.493s 23 | 24 | 📈 Comparison: 25 | Vercel is 2.06x faster than Cloudflare (by mean) 26 | 27 | ------------------------------------------------------------ 28 | Test: react-ssr-bench 29 | ------------------------------------------------------------ 30 | ============================================================ 31 | RESULTS (react-ssr-bench) 32 | ============================================================ 33 | 34 | 📊 Cloudflare Results: 35 | Successful requests: 100/100 36 | Min: 0.085s 37 | Max: 0.360s 38 | Mean: 0.158s 39 | 40 | 📊 Vercel Results: 41 | Successful requests: 100/100 42 | Min: 0.065s 43 | Max: 0.317s 44 | Mean: 0.107s 45 | 46 | 📈 Comparison: 47 | Vercel is 1.47x faster than Cloudflare (by mean) 48 | 49 | ------------------------------------------------------------ 50 | Test: sveltekit 51 | ------------------------------------------------------------ 52 | ============================================================ 53 | RESULTS (sveltekit) 54 | ============================================================ 55 | 56 | 📊 Cloudflare Results: 57 | Successful requests: 100/100 58 | Min: 0.056s 59 | Max: 0.271s 60 | Mean: 0.101s 61 | 62 | 📊 Vercel Results: 63 | Successful requests: 100/100 64 | Min: 0.118s 65 | Max: 0.228s 66 | Mean: 0.146s 67 | 68 | 📈 Comparison: 69 | Cloudflare is 1.44x faster than Vercel (by mean) 70 | 71 | ------------------------------------------------------------ 72 | Test: realistic-math-bench 73 | ------------------------------------------------------------ 74 | ============================================================ 75 | RESULTS (realistic-math-bench) 76 | ============================================================ 77 | 78 | 📊 Cloudflare Results: 79 | Successful requests: 100/100 80 | Min: 0.399s 81 | Max: 1.164s 82 | Mean: 0.673s 83 | 84 | 📊 Vercel Results: 85 | Successful requests: 100/100 86 | Min: 0.639s 87 | Max: 0.779s 88 | Mean: 0.667s 89 | 90 | 📈 Comparison: 91 | Vercel is 1.01x faster than Cloudflare (by mean) 92 | 93 | ------------------------------------------------------------ 94 | Test: vanilla-slower 95 | ------------------------------------------------------------ 96 | ============================================================ 97 | RESULTS (vanilla-slower) 98 | ============================================================ 99 | 100 | 📊 Cloudflare Results: 101 | Successful requests: 100/100 102 | Min: 0.088s 103 | Max: 0.397s 104 | Mean: 0.156s 105 | 106 | 📊 Vercel Results: 107 | Successful requests: 100/100 108 | Min: 0.133s 109 | Max: 0.306s 110 | Mean: 0.164s 111 | 112 | 📈 Comparison: 113 | Cloudflare is 1.05x faster than Vercel (by mean) 114 | 115 | ============================================================ 116 | 117 | 118 | ============================================================ 119 | FINAL RESULTS SUMMARY 120 | ============================================================ 121 | 122 | ## next-js 123 | 124 | | Platform | Mean | Min | Max | Variability | 125 | |------------|------|-----|-----|-------------| 126 | | Cloudflare | 1.015s | 0.616s | 1.910s | 1.293s | 127 | | Vercel | 0.493s | 0.401s | 0.736s | 0.335s | 128 | 129 | **Winner:** Vercel (2.06x faster) 130 | 131 | ## react-ssr-bench 132 | 133 | | Platform | Mean | Min | Max | Variability | 134 | |------------|------|-----|-----|-------------| 135 | | Cloudflare | 0.158s | 0.085s | 0.360s | 0.276s | 136 | | Vercel | 0.107s | 0.065s | 0.317s | 0.251s | 137 | 138 | **Winner:** Vercel (1.47x faster) 139 | 140 | ## sveltekit 141 | 142 | | Platform | Mean | Min | Max | Variability | 143 | |------------|------|-----|-----|-------------| 144 | | Cloudflare | 0.101s | 0.056s | 0.271s | 0.215s | 145 | | Vercel | 0.146s | 0.118s | 0.228s | 0.110s | 146 | 147 | **Winner:** Cloudflare (1.44x faster) 148 | 149 | ## realistic-math-bench 150 | 151 | | Platform | Mean | Min | Max | Variability | 152 | |------------|------|-----|-----|-------------| 153 | | Cloudflare | 0.673s | 0.399s | 1.164s | 0.766s | 154 | | Vercel | 0.667s | 0.639s | 0.779s | 0.140s | 155 | 156 | **Winner:** Vercel (1.01x faster) 157 | 158 | ## vanilla-slower 159 | 160 | | Platform | Mean | Min | Max | Variability | 161 | |------------|------|-----|-----|-------------| 162 | | Cloudflare | 0.156s | 0.088s | 0.397s | 0.309s | 163 | | Vercel | 0.164s | 0.133s | 0.306s | 0.173s | 164 | 165 | **Winner:** Cloudflare (1.05x faster) 166 | 167 | --- 168 | 169 | *Benchmark run: 2025-10-12 • 100 iterations • Concurrency: 10* 170 | 171 | ============================================================ 172 | -------------------------------------------------------------------------------- /results/results-2025-10-12T22-57-41-902Z.txt: -------------------------------------------------------------------------------- 1 | ============================================================ 2 | SSR Performance Benchmark: Cloudflare vs Vercel 3 | ============================================================ 4 | 5 | ------------------------------------------------------------ 6 | Test: next-js 7 | ------------------------------------------------------------ 8 | ============================================================ 9 | RESULTS (next-js) 10 | ============================================================ 11 | 12 | 📊 Cloudflare Results: 13 | Successful requests: 100/100 14 | Min: 0.618s 15 | Max: 2.324s 16 | Mean: 1.105s 17 | 18 | 📊 Vercel Results: 19 | Successful requests: 100/100 20 | Min: 0.376s 21 | Max: 0.770s 22 | Mean: 0.484s 23 | 24 | 📈 Comparison: 25 | Vercel is 2.28x faster than Cloudflare (by mean) 26 | 27 | ------------------------------------------------------------ 28 | Test: react-ssr-bench 29 | ------------------------------------------------------------ 30 | ============================================================ 31 | RESULTS (react-ssr-bench) 32 | ============================================================ 33 | 34 | 📊 Cloudflare Results: 35 | Successful requests: 100/100 36 | Min: 0.088s 37 | Max: 0.467s 38 | Mean: 0.168s 39 | 40 | 📊 Vercel Results: 41 | Successful requests: 100/100 42 | Min: 0.067s 43 | Max: 0.656s 44 | Mean: 0.116s 45 | 46 | 📈 Comparison: 47 | Vercel is 1.45x faster than Cloudflare (by mean) 48 | 49 | ------------------------------------------------------------ 50 | Test: sveltekit 51 | ------------------------------------------------------------ 52 | ============================================================ 53 | RESULTS (sveltekit) 54 | ============================================================ 55 | 56 | 📊 Cloudflare Results: 57 | Successful requests: 100/100 58 | Min: 0.053s 59 | Max: 0.415s 60 | Mean: 0.121s 61 | 62 | 📊 Vercel Results: 63 | Successful requests: 100/100 64 | Min: 0.130s 65 | Max: 0.240s 66 | Mean: 0.154s 67 | 68 | 📈 Comparison: 69 | Cloudflare is 1.27x faster than Vercel (by mean) 70 | 71 | ------------------------------------------------------------ 72 | Test: realistic-math-bench 73 | ------------------------------------------------------------ 74 | ============================================================ 75 | RESULTS (realistic-math-bench) 76 | ============================================================ 77 | 78 | 📊 Cloudflare Results: 79 | Successful requests: 100/100 80 | Min: 0.396s 81 | Max: 1.084s 82 | Mean: 0.698s 83 | 84 | 📊 Vercel Results: 85 | Successful requests: 100/100 86 | Min: 0.576s 87 | Max: 1.188s 88 | Mean: 0.731s 89 | 90 | 📈 Comparison: 91 | Cloudflare is 1.05x faster than Vercel (by mean) 92 | 93 | ------------------------------------------------------------ 94 | Test: vanilla-slower 95 | ------------------------------------------------------------ 96 | ============================================================ 97 | RESULTS (vanilla-slower) 98 | ============================================================ 99 | 100 | 📊 Cloudflare Results: 101 | Successful requests: 100/100 102 | Min: 0.096s 103 | Max: 0.375s 104 | Mean: 0.169s 105 | 106 | 📊 Vercel Results: 107 | Successful requests: 100/100 108 | Min: 0.146s 109 | Max: 0.656s 110 | Mean: 0.200s 111 | 112 | 📈 Comparison: 113 | Cloudflare is 1.18x faster than Vercel (by mean) 114 | 115 | ============================================================ 116 | 117 | 118 | ============================================================ 119 | FINAL RESULTS SUMMARY 120 | ============================================================ 121 | 122 | ## next-js 123 | 124 | | Platform | Mean | Min | Max | Variability | 125 | |------------|------|-----|-----|-------------| 126 | | Cloudflare | 1.105s | 0.618s | 2.324s | 1.706s | 127 | | Vercel | 0.484s | 0.376s | 0.770s | 0.394s | 128 | 129 | **Winner:** Vercel (2.28x faster) 130 | 131 | ## react-ssr-bench 132 | 133 | | Platform | Mean | Min | Max | Variability | 134 | |------------|------|-----|-----|-------------| 135 | | Cloudflare | 0.168s | 0.088s | 0.467s | 0.379s | 136 | | Vercel | 0.116s | 0.067s | 0.656s | 0.589s | 137 | 138 | **Winner:** Vercel (1.45x faster) 139 | 140 | ## sveltekit 141 | 142 | | Platform | Mean | Min | Max | Variability | 143 | |------------|------|-----|-----|-------------| 144 | | Cloudflare | 0.121s | 0.053s | 0.415s | 0.363s | 145 | | Vercel | 0.154s | 0.130s | 0.240s | 0.110s | 146 | 147 | **Winner:** Cloudflare (1.27x faster) 148 | 149 | ## realistic-math-bench 150 | 151 | | Platform | Mean | Min | Max | Variability | 152 | |------------|------|-----|-----|-------------| 153 | | Cloudflare | 0.698s | 0.396s | 1.084s | 0.689s | 154 | | Vercel | 0.731s | 0.576s | 1.188s | 0.611s | 155 | 156 | **Winner:** Cloudflare (1.05x faster) 157 | 158 | ## vanilla-slower 159 | 160 | | Platform | Mean | Min | Max | Variability | 161 | |------------|------|-----|-----|-------------| 162 | | Cloudflare | 0.169s | 0.096s | 0.375s | 0.279s | 163 | | Vercel | 0.200s | 0.146s | 0.656s | 0.510s | 164 | 165 | **Winner:** Cloudflare (1.18x faster) 166 | 167 | --- 168 | 169 | *Benchmark run: 2025-10-12 • 100 iterations • Concurrency: 10* 170 | 171 | ============================================================ 172 | -------------------------------------------------------------------------------- /next-bench/cf-edition/src/app/bench/complex-component.tsx: -------------------------------------------------------------------------------- 1 | // Server component with computationally expensive rendering 2 | // This component intentionally takes several seconds to render 3 | 4 | function isPrime(num: number) { 5 | if (num <= 1) return false; 6 | if (num <= 3) return true; 7 | if (num % 2 === 0 || num % 3 === 0) return false; 8 | for (let i = 5; i * i <= num; i += 6) { 9 | if (num % i === 0 || num % (i + 2) === 0) return false; 10 | } 11 | return true; 12 | } 13 | 14 | function calculatePrimes(limit: number) { 15 | const primes: number[] = []; 16 | for (let i = 2; i <= limit; i++) { 17 | if (isPrime(i)) { 18 | primes.push(i); 19 | } 20 | } 21 | return primes; 22 | } 23 | 24 | function fibonacci(n: number): number { 25 | if (n <= 1) return n; 26 | let a = 0, 27 | b = 1; 28 | for (let i = 2; i <= n; i++) { 29 | const temp = a + b; 30 | a = b; 31 | b = temp; 32 | } 33 | return b; 34 | } 35 | 36 | function generateComplexData() { 37 | // Calculate first 10,000 primes 38 | const primes = calculatePrimes(100000); 39 | 40 | // Calculate fibonacci numbers 41 | const fibs = Array.from({ length: 100 }, (_, i) => fibonacci(i)); 42 | 43 | // Generate complex nested data 44 | const complexData = Array.from({ length: 50 }, (_, i) => ({ 45 | id: i, 46 | title: `Section ${i + 1}`, 47 | primes: primes.slice(i * 100, (i + 1) * 100), 48 | fibonacci: fibs, 49 | items: Array.from({ length: 20 }, (_, j) => ({ 50 | id: j, 51 | value: Math.sqrt(i * 1000 + j), 52 | description: `Item ${j} in section ${i}`, 53 | metadata: { 54 | timestamp: Date.now(), 55 | hash: (i * j * 12345).toString(36), 56 | complexity: Math.sin(i) * Math.cos(j), 57 | }, 58 | })), 59 | })); 60 | 61 | return complexData; 62 | } 63 | 64 | export default function ComplexComponent() { 65 | // Expensive server-side computation 66 | const data = generateComplexData(); 67 | 68 | // Additional expensive operations 69 | const totalPrimes = data.reduce( 70 | (sum, section) => sum + section.primes.length, 71 | 0 72 | ); 73 | const averageFib = 74 | data[0].fibonacci.reduce((a, b) => a + b, 0) / data[0].fibonacci.length; 75 | 76 | return ( 77 |
78 |

79 | Complex Server-Rendered Component 80 |

81 | 82 |
83 |

84 | Statistics 85 |

86 |

Total Prime Numbers: {totalPrimes}

87 |

88 | Average Fibonacci Value: {averageFib.toFixed(2)} 89 |

90 |

Total Sections: {data.length}

91 |
92 | 93 | {data.map((section) => ( 94 |
98 |

99 | {section.title} 100 |

101 | 102 |
103 |

104 | Prime Numbers (100 samples) 105 |

106 |
107 | {section.primes.map((prime, idx) => ( 108 |
112 | {prime} 113 |
114 | ))} 115 |
116 |
117 | 118 |
119 |

120 | Fibonacci Sequence 121 |

122 |
123 | {section.fibonacci.map((fib, idx) => ( 124 |
128 | {fib} 129 |
130 | ))} 131 |
132 |
133 | 134 |
135 |

136 | Items ({section.items.length}) 137 |

138 |
139 | {section.items.map((item) => ( 140 |
144 |

145 | Item {item.id} 146 |

147 |

148 | {item.description} 149 |

150 |

151 | Value: {item.value.toFixed(4)} 152 |

153 |
154 |

Hash: {item.metadata.hash}

155 |

Complexity: {item.metadata.complexity.toFixed(6)}

156 |

Timestamp: {item.metadata.timestamp}

157 |
158 |
159 | ))} 160 |
161 |
162 |
163 | ))} 164 | 165 |
166 |

167 | Additional Computations 168 |

169 |
170 | {Array.from({ length: 300 }, (_, i) => { 171 | const n = i + 1; 172 | const factorial = Array.from( 173 | { length: Math.min(n, 20) }, 174 | (_, j) => j + 1 175 | ).reduce((acc, val) => acc * val, 1); 176 | return ( 177 |
181 |

182 | n={n}, f={factorial.toExponential(2)} 183 |

184 |
185 | ); 186 | })} 187 |
188 |
189 |
190 | ); 191 | } 192 | -------------------------------------------------------------------------------- /next-bench/vercel-edition/src/app/bench/complex-component.tsx: -------------------------------------------------------------------------------- 1 | // Server component with computationally expensive rendering 2 | // This component intentionally takes several seconds to render 3 | 4 | function isPrime(num: number) { 5 | if (num <= 1) return false; 6 | if (num <= 3) return true; 7 | if (num % 2 === 0 || num % 3 === 0) return false; 8 | for (let i = 5; i * i <= num; i += 6) { 9 | if (num % i === 0 || num % (i + 2) === 0) return false; 10 | } 11 | return true; 12 | } 13 | 14 | function calculatePrimes(limit: number) { 15 | const primes: number[] = []; 16 | for (let i = 2; i <= limit; i++) { 17 | if (isPrime(i)) { 18 | primes.push(i); 19 | } 20 | } 21 | return primes; 22 | } 23 | 24 | function fibonacci(n: number): number { 25 | if (n <= 1) return n; 26 | let a = 0, 27 | b = 1; 28 | for (let i = 2; i <= n; i++) { 29 | const temp = a + b; 30 | a = b; 31 | b = temp; 32 | } 33 | return b; 34 | } 35 | 36 | function generateComplexData() { 37 | // Calculate first 10,000 primes 38 | const primes = calculatePrimes(100000); 39 | 40 | // Calculate fibonacci numbers 41 | const fibs = Array.from({ length: 100 }, (_, i) => fibonacci(i)); 42 | 43 | // Generate complex nested data 44 | const complexData = Array.from({ length: 50 }, (_, i) => ({ 45 | id: i, 46 | title: `Section ${i + 1}`, 47 | primes: primes.slice(i * 100, (i + 1) * 100), 48 | fibonacci: fibs, 49 | items: Array.from({ length: 20 }, (_, j) => ({ 50 | id: j, 51 | value: Math.sqrt(i * 1000 + j), 52 | description: `Item ${j} in section ${i}`, 53 | metadata: { 54 | timestamp: Date.now(), 55 | hash: (i * j * 12345).toString(36), 56 | complexity: Math.sin(i) * Math.cos(j), 57 | }, 58 | })), 59 | })); 60 | 61 | return complexData; 62 | } 63 | 64 | export default function ComplexComponent() { 65 | // Expensive server-side computation 66 | const data = generateComplexData(); 67 | 68 | // Additional expensive operations 69 | const totalPrimes = data.reduce( 70 | (sum, section) => sum + section.primes.length, 71 | 0 72 | ); 73 | const averageFib = 74 | data[0].fibonacci.reduce((a, b) => a + b, 0) / data[0].fibonacci.length; 75 | 76 | return ( 77 |
78 |

79 | Complex Server-Rendered Component 80 |

81 | 82 |
83 |

84 | Statistics 85 |

86 |

Total Prime Numbers: {totalPrimes}

87 |

88 | Average Fibonacci Value: {averageFib.toFixed(2)} 89 |

90 |

Total Sections: {data.length}

91 |
92 | 93 | {data.map((section) => ( 94 |
98 |

99 | {section.title} 100 |

101 | 102 |
103 |

104 | Prime Numbers (100 samples) 105 |

106 |
107 | {section.primes.map((prime, idx) => ( 108 |
112 | {prime} 113 |
114 | ))} 115 |
116 |
117 | 118 |
119 |

120 | Fibonacci Sequence 121 |

122 |
123 | {section.fibonacci.map((fib, idx) => ( 124 |
128 | {fib} 129 |
130 | ))} 131 |
132 |
133 | 134 |
135 |

136 | Items ({section.items.length}) 137 |

138 |
139 | {section.items.map((item) => ( 140 |
144 |

145 | Item {item.id} 146 |

147 |

148 | {item.description} 149 |

150 |

151 | Value: {item.value.toFixed(4)} 152 |

153 |
154 |

Hash: {item.metadata.hash}

155 |

Complexity: {item.metadata.complexity.toFixed(6)}

156 |

Timestamp: {item.metadata.timestamp}

157 |
158 |
159 | ))} 160 |
161 |
162 |
163 | ))} 164 | 165 |
166 |

167 | Additional Computations 168 |

169 |
170 | {Array.from({ length: 300 }, (_, i) => { 171 | const n = i + 1; 172 | const factorial = Array.from( 173 | { length: Math.min(n, 20) }, 174 | (_, j) => j + 1 175 | ).reduce((acc, val) => acc * val, 1); 176 | return ( 177 |
181 |

182 | n={n}, f={factorial.toExponential(2)} 183 |

184 |
185 | ); 186 | })} 187 |
188 |
189 |
190 | ); 191 | } 192 | -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/api/bench.js: -------------------------------------------------------------------------------- 1 | // Vanilla Vercel Serverless Function SSR implementation 2 | // This is a simple SSR demo without any framework 3 | 4 | function isPrime(num) { 5 | if (num <= 1) return false; 6 | if (num <= 3) return true; 7 | if (num % 2 === 0 || num % 3 === 0) return false; 8 | for (let i = 5; i * i <= num; i += 6) { 9 | if (num % i === 0 || num % (i + 2) === 0) return false; 10 | } 11 | return true; 12 | } 13 | 14 | function calculatePrimes(limit) { 15 | const primes = []; 16 | for (let i = 2; i <= limit; i++) { 17 | if (isPrime(i)) { 18 | primes.push(i); 19 | } 20 | } 21 | return primes; 22 | } 23 | 24 | function fibonacci(n) { 25 | if (n <= 1) return n; 26 | let a = 0, b = 1; 27 | for (let i = 2; i <= n; i++) { 28 | const temp = a + b; 29 | a = b; 30 | b = temp; 31 | } 32 | return b; 33 | } 34 | 35 | function generateComplexData() { 36 | // Calculate first 10,000 primes 37 | const primes = calculatePrimes(100000); 38 | 39 | // Calculate fibonacci numbers 40 | const fibs = Array.from({ length: 100 }, (_, i) => fibonacci(i)); 41 | 42 | // Generate complex nested data 43 | const complexData = Array.from({ length: 50 }, (_, i) => ({ 44 | id: i, 45 | title: `Section ${i + 1}`, 46 | primes: primes.slice(i * 100, (i + 1) * 100), 47 | fibonacci: fibs, 48 | items: Array.from({ length: 20 }, (_, j) => ({ 49 | id: j, 50 | value: Math.sqrt(i * 1000 + j), 51 | description: `Item ${j} in section ${i}`, 52 | metadata: { 53 | timestamp: Date.now(), 54 | hash: (i * j * 12345).toString(36), 55 | complexity: Math.sin(i) * Math.cos(j), 56 | }, 57 | })), 58 | })); 59 | 60 | return complexData; 61 | } 62 | 63 | function renderComplexComponent(data) { 64 | const totalPrimes = data.reduce((sum, section) => sum + section.primes.length, 0); 65 | const averageFib = data[0].fibonacci.reduce((a, b) => a + b, 0) / data[0].fibonacci.length; 66 | 67 | const sectionsHtml = data.map(section => { 68 | const primesHtml = section.primes.map(prime => 69 | `
${prime}
` 70 | ).join(''); 71 | 72 | const fibsHtml = section.fibonacci.map(fib => 73 | `
${fib}
` 74 | ).join(''); 75 | 76 | const itemsHtml = section.items.map(item => ` 77 |
78 |

Item ${item.id}

79 |

${item.description}

80 |

Value: ${item.value.toFixed(4)}

81 |
82 |

Hash: ${item.metadata.hash}

83 |

Complexity: ${item.metadata.complexity.toFixed(6)}

84 |

Timestamp: ${item.metadata.timestamp}

85 |
86 |
87 | `).join(''); 88 | 89 | return ` 90 |
91 |

${section.title}

92 | 93 |
94 |

Prime Numbers (100 samples)

95 |
${primesHtml}
96 |
97 | 98 |
99 |

Fibonacci Sequence

100 |
${fibsHtml}
101 |
102 | 103 |
104 |

Items (${section.items.length})

105 |
${itemsHtml}
106 |
107 |
108 | `; 109 | }).join(''); 110 | 111 | const additionalComputations = Array.from({ length: 300 }, (_, i) => { 112 | const n = i + 1; 113 | const factorial = Array.from({ length: Math.min(n, 20) }, (_, j) => j + 1) 114 | .reduce((acc, val) => acc * val, 1); 115 | return ` 116 |
117 |

n=${n}, f=${factorial.toExponential(2)}

118 |
119 | `; 120 | }).join(''); 121 | 122 | return ` 123 |
124 |

Complex Server-Rendered Component (Vanilla)

125 | 126 |
127 |

Statistics

128 |

Total Prime Numbers: ${totalPrimes}

129 |

Average Fibonacci Value: ${averageFib.toFixed(2)}

130 |

Total Sections: ${data.length}

131 |
132 | 133 | ${sectionsHtml} 134 | 135 |
136 |

Additional Computations

137 |
${additionalComputations}
138 |
139 |
140 | `; 141 | } 142 | 143 | export default function handler(req, res) { 144 | console.log("rendering", Date.now()); 145 | 146 | // Expensive server-side computation 147 | const data = generateComplexData(); 148 | const currentTime = new Date().toLocaleString(); 149 | 150 | const componentHtml = renderComplexComponent(data); 151 | 152 | const html = ` 153 | 154 | 155 | 156 | 157 | 158 | Vanilla SSR Benchmark - Vercel 159 | 166 | 167 | 168 |
169 |

Last rendered at:

170 |

${currentTime}

171 | ${componentHtml} 172 |
173 | 174 | 175 | `; 176 | 177 | res.setHeader('Content-Type', 'text/html; charset=utf-8'); 178 | res.status(200).send(html); 179 | } 180 | -------------------------------------------------------------------------------- /vanilla-bench/vercel-edition/api/slower-bench.js: -------------------------------------------------------------------------------- 1 | // Vanilla Vercel Serverless Function SSR implementation - SLOWER VERSION 2 | // This is a simple SSR demo without any framework, with much heavier computations 3 | 4 | function isPrime(num) { 5 | if (num <= 1) return false; 6 | if (num <= 3) return true; 7 | if (num % 2 === 0 || num % 3 === 0) return false; 8 | for (let i = 5; i * i <= num; i += 6) { 9 | if (num % i === 0 || num % (i + 2) === 0) return false; 10 | } 11 | return true; 12 | } 13 | 14 | function calculatePrimes(limit) { 15 | const primes = []; 16 | for (let i = 2; i <= limit; i++) { 17 | if (isPrime(i)) { 18 | primes.push(i); 19 | } 20 | } 21 | return primes; 22 | } 23 | 24 | function fibonacci(n) { 25 | if (n <= 1) return n; 26 | let a = 0, b = 1; 27 | for (let i = 2; i <= n; i++) { 28 | const temp = a + b; 29 | a = b; 30 | b = temp; 31 | } 32 | return b; 33 | } 34 | 35 | function generateSlowerComplexData() { 36 | // Calculate way more primes (5x more) 37 | const primes = calculatePrimes(500000); 38 | 39 | // Calculate more fibonacci numbers 40 | const fibs = Array.from({ length: 200 }, (_, i) => fibonacci(i)); 41 | 42 | // Generate much more complex nested data (3x sections, 3x items) 43 | const complexData = Array.from({ length: 150 }, (_, i) => ({ 44 | id: i, 45 | title: `Section ${i + 1}`, 46 | primes: primes.slice(i * 200, (i + 1) * 200), 47 | fibonacci: fibs, 48 | items: Array.from({ length: 60 }, (_, j) => ({ 49 | id: j, 50 | value: Math.sqrt(i * 1000 + j), 51 | description: `Item ${j} in section ${i}`, 52 | metadata: { 53 | timestamp: Date.now(), 54 | hash: (i * j * 12345).toString(36), 55 | complexity: Math.sin(i) * Math.cos(j), 56 | extraComputation: Math.pow(i + j, 2) * Math.log(i + j + 1), 57 | }, 58 | })), 59 | })); 60 | 61 | return complexData; 62 | } 63 | 64 | function renderComplexComponent(data) { 65 | const totalPrimes = data.reduce((sum, section) => sum + section.primes.length, 0); 66 | const averageFib = data[0].fibonacci.reduce((a, b) => a + b, 0) / data[0].fibonacci.length; 67 | 68 | const sectionsHtml = data.map(section => { 69 | const primesHtml = section.primes.map(prime => 70 | `
${prime}
` 71 | ).join(''); 72 | 73 | const fibsHtml = section.fibonacci.map(fib => 74 | `
${fib}
` 75 | ).join(''); 76 | 77 | const itemsHtml = section.items.map(item => ` 78 |
79 |

Item ${item.id}

80 |

${item.description}

81 |

Value: ${item.value.toFixed(4)}

82 |
83 |

Hash: ${item.metadata.hash}

84 |

Complexity: ${item.metadata.complexity.toFixed(6)}

85 | ${item.metadata.extraComputation ? `

Extra: ${item.metadata.extraComputation.toFixed(6)}

` : ''} 86 |

Timestamp: ${item.metadata.timestamp}

87 |
88 |
89 | `).join(''); 90 | 91 | return ` 92 |
93 |

${section.title}

94 | 95 |
96 |

Prime Numbers (200 samples)

97 |
${primesHtml}
98 |
99 | 100 |
101 |

Fibonacci Sequence

102 |
${fibsHtml}
103 |
104 | 105 |
106 |

Items (${section.items.length})

107 |
${itemsHtml}
108 |
109 |
110 | `; 111 | }).join(''); 112 | 113 | const additionalComputations = Array.from({ length: 300 }, (_, i) => { 114 | const n = i + 1; 115 | const factorial = Array.from({ length: Math.min(n, 20) }, (_, j) => j + 1) 116 | .reduce((acc, val) => acc * val, 1); 117 | return ` 118 |
119 |

n=${n}, f=${factorial.toExponential(2)}

120 |
121 | `; 122 | }).join(''); 123 | 124 | return ` 125 |
126 |

Complex Server-Rendered Component (Vanilla - SLOWER)

127 | 128 |
129 |

Statistics

130 |

Total Prime Numbers: ${totalPrimes}

131 |

Average Fibonacci Value: ${averageFib.toFixed(2)}

132 |

Total Sections: ${data.length}

133 |
134 | 135 | ${sectionsHtml} 136 | 137 |
138 |

Additional Computations

139 |
${additionalComputations}
140 |
141 |
142 | `; 143 | } 144 | 145 | export default function handler(req, res) { 146 | console.log("rendering slower bench", Date.now()); 147 | 148 | // Much more expensive server-side computation 149 | const data = generateSlowerComplexData(); 150 | const currentTime = new Date().toLocaleString(); 151 | 152 | const componentHtml = renderComplexComponent(data); 153 | 154 | const html = ` 155 | 156 | 157 | 158 | 159 | 160 | Vanilla SSR Slower Benchmark - Vercel 161 | 168 | 169 | 170 |
171 |

Slower Bench - Last rendered at:

172 |

${currentTime}

173 | ${componentHtml} 174 |
175 | 176 | 177 | `; 178 | 179 | res.setHeader('Content-Type', 'text/html; charset=utf-8'); 180 | res.status(200).send(html); 181 | } 182 | -------------------------------------------------------------------------------- /react-ssr-bench/vercel-edition/ComplexComponent.mjs: -------------------------------------------------------------------------------- 1 | // Server component with computationally expensive rendering 2 | // This component intentionally takes several seconds to render 3 | // Uses React.createElement instead of JSX to avoid compilation 4 | 5 | import React from 'react'; 6 | 7 | function isPrime(num) { 8 | if (num <= 1) return false; 9 | if (num <= 3) return true; 10 | if (num % 2 === 0 || num % 3 === 0) return false; 11 | for (let i = 5; i * i <= num; i += 6) { 12 | if (num % i === 0 || num % (i + 2) === 0) return false; 13 | } 14 | return true; 15 | } 16 | 17 | function calculatePrimes(limit) { 18 | const primes = []; 19 | for (let i = 2; i <= limit; i++) { 20 | if (isPrime(i)) { 21 | primes.push(i); 22 | } 23 | } 24 | return primes; 25 | } 26 | 27 | function fibonacci(n) { 28 | if (n <= 1) return n; 29 | let a = 0, 30 | b = 1; 31 | for (let i = 2; i <= n; i++) { 32 | const temp = a + b; 33 | a = b; 34 | b = temp; 35 | } 36 | return b; 37 | } 38 | 39 | function generateComplexData() { 40 | // Calculate first 10,000 primes 41 | const primes = calculatePrimes(100000); 42 | 43 | // Calculate fibonacci numbers 44 | const fibs = Array.from({ length: 100 }, (_, i) => fibonacci(i)); 45 | 46 | // Generate complex nested data 47 | const complexData = Array.from({ length: 50 }, (_, i) => ({ 48 | id: i, 49 | title: `Section ${i + 1}`, 50 | primes: primes.slice(i * 100, (i + 1) * 100), 51 | fibonacci: fibs, 52 | items: Array.from({ length: 20 }, (_, j) => ({ 53 | id: j, 54 | value: Math.sqrt(i * 1000 + j), 55 | description: `Item ${j} in section ${i}`, 56 | metadata: { 57 | timestamp: Date.now(), 58 | hash: (i * j * 12345).toString(36), 59 | complexity: Math.sin(i) * Math.cos(j), 60 | }, 61 | })), 62 | })); 63 | 64 | return complexData; 65 | } 66 | 67 | export default function ComplexComponent() { 68 | // Expensive server-side computation 69 | const data = generateComplexData(); 70 | 71 | // Additional expensive operations 72 | const totalPrimes = data.reduce( 73 | (sum, section) => sum + section.primes.length, 74 | 0 75 | ); 76 | const averageFib = 77 | data[0].fibonacci.reduce((a, b) => a + b, 0) / data[0].fibonacci.length; 78 | 79 | return React.createElement('div', { 80 | style: { padding: '32px', maxWidth: '1280px', margin: '0 auto', backgroundColor: 'white', color: '#111827' } 81 | }, 82 | React.createElement('h1', { 83 | style: { fontSize: '36px', fontWeight: 'bold', marginBottom: '24px', color: '#111827' } 84 | }, 'Complex Server-Rendered Component (React SSR)'), 85 | 86 | React.createElement('div', { 87 | style: { marginBottom: '32px', padding: '16px', borderRadius: '8px', backgroundColor: '#f3f4f6', border: '1px solid #e5e7eb', boxShadow: '0 1px 3px rgba(0,0,0,0.1)' } 88 | }, 89 | React.createElement('h2', { 90 | style: { fontSize: '24px', fontWeight: '600', marginBottom: '8px', color: '#1f2937' } 91 | }, 'Statistics'), 92 | React.createElement('p', { style: { fontSize: '18px' } }, `Total Prime Numbers: ${totalPrimes}`), 93 | React.createElement('p', { style: { fontSize: '18px' } }, `Average Fibonacci Value: ${averageFib.toFixed(2)}`), 94 | React.createElement('p', { style: { fontSize: '18px' } }, `Total Sections: ${data.length}`) 95 | ), 96 | 97 | ...data.map((section) => 98 | React.createElement('div', { 99 | key: section.id, 100 | style: { marginBottom: '32px', border: '1px solid #e5e7eb', borderRadius: '8px', padding: '24px', backgroundColor: 'white', boxShadow: '0 1px 3px rgba(0,0,0,0.1)' } 101 | }, 102 | React.createElement('h2', { 103 | style: { fontSize: '24px', fontWeight: 'bold', marginBottom: '16px', color: '#111827' } 104 | }, section.title), 105 | 106 | React.createElement('div', { style: { marginBottom: '16px' } }, 107 | React.createElement('h3', { 108 | style: { fontSize: '20px', fontWeight: '600', marginBottom: '8px', color: '#6b21a8' } 109 | }, 'Prime Numbers (100 samples)'), 110 | React.createElement('div', { 111 | style: { display: 'grid', gridTemplateColumns: 'repeat(10, 1fr)', gap: '8px' } 112 | }, 113 | ...section.primes.map((prime, idx) => 114 | React.createElement('div', { 115 | key: idx, 116 | style: { backgroundColor: '#e9d5ff', padding: '8px', textAlign: 'center', borderRadius: '4px', fontSize: '14px', color: '#581c87', border: '1px solid #d8b4fe' } 117 | }, prime) 118 | ) 119 | ) 120 | ), 121 | 122 | React.createElement('div', { style: { marginBottom: '16px' } }, 123 | React.createElement('h3', { 124 | style: { fontSize: '20px', fontWeight: '600', marginBottom: '8px', color: '#166534' } 125 | }, 'Fibonacci Sequence'), 126 | React.createElement('div', { 127 | style: { display: 'flex', flexWrap: 'wrap', gap: '8px' } 128 | }, 129 | ...section.fibonacci.map((fib, idx) => 130 | React.createElement('div', { 131 | key: idx, 132 | style: { backgroundColor: '#dcfce7', padding: '4px 12px', borderRadius: '4px', fontSize: '14px', color: '#14532d', border: '1px solid #bbf7d0' } 133 | }, fib) 134 | ) 135 | ) 136 | ), 137 | 138 | React.createElement('div', {}, 139 | React.createElement('h3', { 140 | style: { fontSize: '20px', fontWeight: '600', marginBottom: '8px', color: '#1e40af' } 141 | }, `Items (${section.items.length})`), 142 | React.createElement('div', { 143 | style: { display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '16px' } 144 | }, 145 | ...section.items.map((item) => 146 | React.createElement('div', { 147 | key: item.id, 148 | style: { backgroundColor: '#f9fafb', padding: '16px', borderRadius: '4px', border: '1px solid #d1d5db', boxShadow: '0 1px 2px rgba(0,0,0,0.05)' } 149 | }, 150 | React.createElement('h4', { 151 | style: { fontWeight: '600', color: '#111827' } 152 | }, `Item ${item.id}`), 153 | React.createElement('p', { 154 | style: { fontSize: '14px', color: '#4b5563' } 155 | }, item.description), 156 | React.createElement('p', { 157 | style: { fontSize: '14px', color: '#1f2937' } 158 | }, `Value: ${item.value.toFixed(4)}`), 159 | React.createElement('div', { 160 | style: { marginTop: '8px', fontSize: '12px', color: '#6b7280' } 161 | }, 162 | React.createElement('p', {}, `Hash: ${item.metadata.hash}`), 163 | React.createElement('p', {}, `Complexity: ${item.metadata.complexity.toFixed(6)}`), 164 | React.createElement('p', {}, `Timestamp: ${item.metadata.timestamp}`) 165 | ) 166 | ) 167 | ) 168 | ) 169 | ) 170 | ) 171 | ), 172 | 173 | React.createElement('div', { 174 | style: { marginTop: '32px', padding: '24px', backgroundColor: '#f3f4f6', borderRadius: '8px', border: '1px solid #e5e7eb', boxShadow: '0 1px 3px rgba(0,0,0,0.1)' } 175 | }, 176 | React.createElement('h2', { 177 | style: { fontSize: '24px', fontWeight: 'bold', marginBottom: '16px', color: '#111827' } 178 | }, 'Additional Computations'), 179 | React.createElement('div', { 180 | style: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px' } 181 | }, 182 | ...Array.from({ length: 300 }, (_, i) => { 183 | const n = i + 1; 184 | const factorial = Array.from( 185 | { length: Math.min(n, 20) }, 186 | (_, j) => j + 1 187 | ).reduce((acc, val) => acc * val, 1); 188 | return React.createElement('div', { 189 | key: i, 190 | style: { backgroundColor: 'white', padding: '12px', borderRadius: '4px', boxShadow: '0 1px 2px rgba(0,0,0,0.05)', border: '1px solid #e5e7eb' } 191 | }, 192 | React.createElement('p', { 193 | style: { fontFamily: 'monospace', fontSize: '14px', color: '#1f2937' } 194 | }, `n=${n}, f=${factorial.toExponential(2)}`) 195 | ); 196 | }) 197 | ) 198 | ) 199 | ); 200 | } 201 | -------------------------------------------------------------------------------- /react-ssr-bench/cf-edition/ComplexComponent.mjs: -------------------------------------------------------------------------------- 1 | // Server component with computationally expensive rendering 2 | // This component intentionally takes several seconds to render 3 | // Uses React.createElement instead of JSX to avoid compilation 4 | 5 | import React from 'react'; 6 | 7 | function isPrime(num) { 8 | if (num <= 1) return false; 9 | if (num <= 3) return true; 10 | if (num % 2 === 0 || num % 3 === 0) return false; 11 | for (let i = 5; i * i <= num; i += 6) { 12 | if (num % i === 0 || num % (i + 2) === 0) return false; 13 | } 14 | return true; 15 | } 16 | 17 | function calculatePrimes(limit) { 18 | const primes = []; 19 | for (let i = 2; i <= limit; i++) { 20 | if (isPrime(i)) { 21 | primes.push(i); 22 | } 23 | } 24 | return primes; 25 | } 26 | 27 | function fibonacci(n) { 28 | if (n <= 1) return n; 29 | let a = 0, 30 | b = 1; 31 | for (let i = 2; i <= n; i++) { 32 | const temp = a + b; 33 | a = b; 34 | b = temp; 35 | } 36 | return b; 37 | } 38 | 39 | function generateComplexData() { 40 | // Calculate first 10,000 primes 41 | const primes = calculatePrimes(100000); 42 | 43 | // Calculate fibonacci numbers 44 | const fibs = Array.from({ length: 100 }, (_, i) => fibonacci(i)); 45 | 46 | // Generate complex nested data 47 | const complexData = Array.from({ length: 50 }, (_, i) => ({ 48 | id: i, 49 | title: `Section ${i + 1}`, 50 | primes: primes.slice(i * 100, (i + 1) * 100), 51 | fibonacci: fibs, 52 | items: Array.from({ length: 20 }, (_, j) => ({ 53 | id: j, 54 | value: Math.sqrt(i * 1000 + j), 55 | description: `Item ${j} in section ${i}`, 56 | metadata: { 57 | timestamp: Date.now(), 58 | hash: (i * j * 12345).toString(36), 59 | complexity: Math.sin(i) * Math.cos(j), 60 | }, 61 | })), 62 | })); 63 | 64 | return complexData; 65 | } 66 | 67 | export default function ComplexComponent() { 68 | // Expensive server-side computation 69 | const data = generateComplexData(); 70 | 71 | // Additional expensive operations 72 | const totalPrimes = data.reduce( 73 | (sum, section) => sum + section.primes.length, 74 | 0 75 | ); 76 | const averageFib = 77 | data[0].fibonacci.reduce((a, b) => a + b, 0) / data[0].fibonacci.length; 78 | 79 | return React.createElement('div', { 80 | style: { padding: '32px', maxWidth: '1280px', margin: '0 auto', backgroundColor: 'white', color: '#111827' } 81 | }, 82 | React.createElement('h1', { 83 | style: { fontSize: '36px', fontWeight: 'bold', marginBottom: '24px', color: '#111827' } 84 | }, 'Complex Server-Rendered Component (React SSR - CloudFlare)'), 85 | 86 | React.createElement('div', { 87 | style: { marginBottom: '32px', padding: '16px', borderRadius: '8px', backgroundColor: '#f3f4f6', border: '1px solid #e5e7eb', boxShadow: '0 1px 3px rgba(0,0,0,0.1)' } 88 | }, 89 | React.createElement('h2', { 90 | style: { fontSize: '24px', fontWeight: '600', marginBottom: '8px', color: '#1f2937' } 91 | }, 'Statistics'), 92 | React.createElement('p', { style: { fontSize: '18px' } }, `Total Prime Numbers: ${totalPrimes}`), 93 | React.createElement('p', { style: { fontSize: '18px' } }, `Average Fibonacci Value: ${averageFib.toFixed(2)}`), 94 | React.createElement('p', { style: { fontSize: '18px' } }, `Total Sections: ${data.length}`) 95 | ), 96 | 97 | ...data.map((section) => 98 | React.createElement('div', { 99 | key: section.id, 100 | style: { marginBottom: '32px', border: '1px solid #e5e7eb', borderRadius: '8px', padding: '24px', backgroundColor: 'white', boxShadow: '0 1px 3px rgba(0,0,0,0.1)' } 101 | }, 102 | React.createElement('h2', { 103 | style: { fontSize: '24px', fontWeight: 'bold', marginBottom: '16px', color: '#111827' } 104 | }, section.title), 105 | 106 | React.createElement('div', { style: { marginBottom: '16px' } }, 107 | React.createElement('h3', { 108 | style: { fontSize: '20px', fontWeight: '600', marginBottom: '8px', color: '#6b21a8' } 109 | }, 'Prime Numbers (100 samples)'), 110 | React.createElement('div', { 111 | style: { display: 'grid', gridTemplateColumns: 'repeat(10, 1fr)', gap: '8px' } 112 | }, 113 | ...section.primes.map((prime, idx) => 114 | React.createElement('div', { 115 | key: idx, 116 | style: { backgroundColor: '#e9d5ff', padding: '8px', textAlign: 'center', borderRadius: '4px', fontSize: '14px', color: '#581c87', border: '1px solid #d8b4fe' } 117 | }, prime) 118 | ) 119 | ) 120 | ), 121 | 122 | React.createElement('div', { style: { marginBottom: '16px' } }, 123 | React.createElement('h3', { 124 | style: { fontSize: '20px', fontWeight: '600', marginBottom: '8px', color: '#166534' } 125 | }, 'Fibonacci Sequence'), 126 | React.createElement('div', { 127 | style: { display: 'flex', flexWrap: 'wrap', gap: '8px' } 128 | }, 129 | ...section.fibonacci.map((fib, idx) => 130 | React.createElement('div', { 131 | key: idx, 132 | style: { backgroundColor: '#dcfce7', padding: '4px 12px', borderRadius: '4px', fontSize: '14px', color: '#14532d', border: '1px solid #bbf7d0' } 133 | }, fib) 134 | ) 135 | ) 136 | ), 137 | 138 | React.createElement('div', {}, 139 | React.createElement('h3', { 140 | style: { fontSize: '20px', fontWeight: '600', marginBottom: '8px', color: '#1e40af' } 141 | }, `Items (${section.items.length})`), 142 | React.createElement('div', { 143 | style: { display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '16px' } 144 | }, 145 | ...section.items.map((item) => 146 | React.createElement('div', { 147 | key: item.id, 148 | style: { backgroundColor: '#f9fafb', padding: '16px', borderRadius: '4px', border: '1px solid #d1d5db', boxShadow: '0 1px 2px rgba(0,0,0,0.05)' } 149 | }, 150 | React.createElement('h4', { 151 | style: { fontWeight: '600', color: '#111827' } 152 | }, `Item ${item.id}`), 153 | React.createElement('p', { 154 | style: { fontSize: '14px', color: '#4b5563' } 155 | }, item.description), 156 | React.createElement('p', { 157 | style: { fontSize: '14px', color: '#1f2937' } 158 | }, `Value: ${item.value.toFixed(4)}`), 159 | React.createElement('div', { 160 | style: { marginTop: '8px', fontSize: '12px', color: '#6b7280' } 161 | }, 162 | React.createElement('p', {}, `Hash: ${item.metadata.hash}`), 163 | React.createElement('p', {}, `Complexity: ${item.metadata.complexity.toFixed(6)}`), 164 | React.createElement('p', {}, `Timestamp: ${item.metadata.timestamp}`) 165 | ) 166 | ) 167 | ) 168 | ) 169 | ) 170 | ) 171 | ), 172 | 173 | React.createElement('div', { 174 | style: { marginTop: '32px', padding: '24px', backgroundColor: '#f3f4f6', borderRadius: '8px', border: '1px solid #e5e7eb', boxShadow: '0 1px 3px rgba(0,0,0,0.1)' } 175 | }, 176 | React.createElement('h2', { 177 | style: { fontSize: '24px', fontWeight: 'bold', marginBottom: '16px', color: '#111827' } 178 | }, 'Additional Computations'), 179 | React.createElement('div', { 180 | style: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px' } 181 | }, 182 | ...Array.from({ length: 300 }, (_, i) => { 183 | const n = i + 1; 184 | const factorial = Array.from( 185 | { length: Math.min(n, 20) }, 186 | (_, j) => j + 1 187 | ).reduce((acc, val) => acc * val, 1); 188 | return React.createElement('div', { 189 | key: i, 190 | style: { backgroundColor: 'white', padding: '12px', borderRadius: '4px', boxShadow: '0 1px 2px rgba(0,0,0,0.05)', border: '1px solid #e5e7eb' } 191 | }, 192 | React.createElement('p', { 193 | style: { fontFamily: 'monospace', fontSize: '14px', color: '#1f2937' } 194 | }, `n=${n}, f=${factorial.toExponential(2)}`) 195 | ); 196 | }) 197 | ) 198 | ) 199 | ); 200 | } 201 | -------------------------------------------------------------------------------- /results/results-2025-10-03T05-33-05-236Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T05:33:05.236Z", 3 | "iterations": 50, 4 | "concurrency": 5, 5 | "tests": [ 6 | { 7 | "name": "next-js", 8 | "urls": { 9 | "cloudflare": "https://my-next-app.pinglabs.workers.dev/bench", 10 | "vercel": "https://vercel-ssr-bench-v2-hidden.vercel.app/bench" 11 | }, 12 | "results": { 13 | "cloudflare": { 14 | "min": 4367.879332999999, 15 | "max": 8845.661124999999, 16 | "mean": 6556.93746412, 17 | "successful": 50, 18 | "times": [ 19 | 5252.117332999999, 20 | 5261.576708, 21 | 8145.608292, 22 | 8151.319708, 23 | 8157.960333, 24 | 4992.655333000001, 25 | 5600.779917, 26 | 4969.279208, 27 | 4367.879332999999, 28 | 7624.23825, 29 | 7644.185374999999, 30 | 7645.275707999999, 31 | 4772.777250000001, 32 | 4788.401041000001, 33 | 7908.031249999998, 34 | 7922.462666000001, 35 | 7916.244749999998, 36 | 4757.729667, 37 | 4772.240166, 38 | 5479.8714169999985, 39 | 5481.168792, 40 | 7787.430082999999, 41 | 7785.309666999998, 42 | 7807.947541000001, 43 | 5145.229458999998, 44 | 5160.832208000003, 45 | 7846.0169590000005, 46 | 7846.377166999999, 47 | 7845.080125000004, 48 | 5116.994583, 49 | 5117.544000000002, 50 | 4908.139540999997, 51 | 4920.889959, 52 | 7085.924333999996, 53 | 7105.055249999998, 54 | 7109.407874999997, 55 | 5412.686792, 56 | 5415.680791999999, 57 | 8541.272833000003, 58 | 8566.007874999996, 59 | 8565.934457999996, 60 | 5682.908750000002, 61 | 5696.898541000002, 62 | 5794.903958000003, 63 | 5794.570958000004, 64 | 8823.257166999996, 65 | 8845.661124999999, 66 | 8843.400624999995, 67 | 5829.244584, 68 | 5834.463500000005 69 | ] 70 | }, 71 | "vercel": { 72 | "min": 610.271041, 73 | "max": 1789.8701250000013, 74 | "mean": 855.6368973400007, 75 | "successful": 50, 76 | "times": [ 77 | 1610.3296249999985, 78 | 1710.8002909999923, 79 | 1737.3105829999986, 80 | 1755.0686660000065, 81 | 1789.8701250000013, 82 | 825.7679170000047, 83 | 826.9814999999944, 84 | 872.3141250000044, 85 | 887.4102080000011, 86 | 1076.385541999989, 87 | 723.7056660000089, 88 | 799.1290420000005, 89 | 864.8548330000049, 90 | 836.6179160000029, 91 | 836.5914580000099, 92 | 728.9958330000081, 93 | 778.3421249999956, 94 | 816.2889589999977, 95 | 872.5991670000076, 96 | 801.6442909999896, 97 | 682.0690409999952, 98 | 779.1764159999875, 99 | 799.9129580000008, 100 | 819.774207999988, 101 | 670.8152919999993, 102 | 808.5156660000066, 103 | 769.3882920000033, 104 | 734.9033749999944, 105 | 623.1886669999949, 106 | 716.2332080000051, 107 | 733.302125000002, 108 | 719.5394579999993, 109 | 719.6797920000099, 110 | 704.512584000011, 111 | 775.1627080000035, 112 | 937.7558750000026, 113 | 783.3159169999999, 114 | 696.7983329999988, 115 | 720.3038749999978, 116 | 727.6132500000094, 117 | 660.492333000002, 118 | 712.6975000000093, 119 | 629.1521659999999, 120 | 671.8022499999934, 121 | 692.9393330000021, 122 | 711.7773749999906, 123 | 610.271041, 124 | 673.730791000009, 125 | 673.6387079999986, 126 | 672.3744580000057 127 | ] 128 | } 129 | } 130 | }, 131 | { 132 | "name": "vanilla-slower", 133 | "urls": { 134 | "cloudflare": "https://vanilla-ssr-cf.pinglabs.workers.dev/slower-bench", 135 | "vercel": "https://vanilla-bench-v2.vercel.app/api/slower-bench" 136 | }, 137 | "results": { 138 | "cloudflare": { 139 | "min": 276.9269159999967, 140 | "max": 945.705958999999, 141 | "mean": 413.9898600399995, 142 | "successful": 50, 143 | "times": [ 144 | 557.9534589999967, 145 | 568.289208000002, 146 | 578.2092499999999, 147 | 792.1983749999927, 148 | 852.5352909999929, 149 | 329.22387499999604, 150 | 414.2633329999953, 151 | 368.2087080000056, 152 | 276.9269159999967, 153 | 393.04933300000266, 154 | 350.20370900000853, 155 | 316.54354199999943, 156 | 945.705958999999, 157 | 384.21566699999676, 158 | 339.0816249999916, 159 | 345.54279099999985, 160 | 318.2130829999951, 161 | 416.12158399999316, 162 | 438.5535419999942, 163 | 844.3608329999988, 164 | 297.1552079999965, 165 | 290.06466699999874, 166 | 322.1909580000065, 167 | 358.02920900000026, 168 | 301.36216600000625, 169 | 336.83295799999905, 170 | 328.482124999995, 171 | 524.5322920000035, 172 | 327.3304170000047, 173 | 302.5845419999969, 174 | 344.6521250000078, 175 | 342.60470799999894, 176 | 308.3421670000098, 177 | 395.7658749999973, 178 | 323.39595800000825, 179 | 324.296417000005, 180 | 443.65795799999614, 181 | 331.13150000000314, 182 | 391.33966700000747, 183 | 470.8874579999974, 184 | 439.0540000000037, 185 | 370.41966699999466, 186 | 350.9750839999906, 187 | 288.34291700000176, 188 | 321.3971249999886, 189 | 368.8653329999943, 190 | 441.5371670000022, 191 | 323.7325419999979, 192 | 443.46854200000234, 193 | 457.6621670000022 194 | ] 195 | }, 196 | "vercel": { 197 | "min": 416.27287500000966, 198 | "max": 1473.9204170000012, 199 | "mean": 727.3560283399985, 200 | "successful": 50, 201 | "times": [ 202 | 579.8939160000009, 203 | 563.1013329999987, 204 | 1182.6213339999958, 205 | 1190.9223749999946, 206 | 1209.6451669999951, 207 | 1225.0895829999936, 208 | 605.3142919999955, 209 | 635.7142089999979, 210 | 673.8467079999973, 211 | 906.8662079999922, 212 | 515.1302920000016, 213 | 646.7440419999912, 214 | 1473.9204170000012, 215 | 828.3293750000012, 216 | 675.0041250000068, 217 | 926.9547500000044, 218 | 802.0532919999969, 219 | 836.3177079999878, 220 | 469.86729199999536, 221 | 917.9320420000004, 222 | 607.0343750000029, 223 | 706.6104579999956, 224 | 523.1083749999962, 225 | 631.6292920000124, 226 | 623.2530419999966, 227 | 641.6912500000035, 228 | 439.7017499999929, 229 | 610.3403330000001, 230 | 910.9629580000037, 231 | 606.6500410000008, 232 | 736.0044169999892, 233 | 565.3603339999972, 234 | 560.546709000002, 235 | 547.802291999993, 236 | 662.3460829999967, 237 | 658.8302919999987, 238 | 655.0102499999921, 239 | 550.8556669999962, 240 | 786.1824579999957, 241 | 556.1938329999975, 242 | 681.9706659999938, 243 | 416.27287500000966, 244 | 1016.1900830000086, 245 | 748.590916000001, 246 | 622.8438329999917, 247 | 773.6767919999984, 248 | 769.4982500000042, 249 | 711.1205829999963, 250 | 434.51662500000384, 251 | 747.7381250000035 252 | ] 253 | } 254 | } 255 | } 256 | ] 257 | } -------------------------------------------------------------------------------- /results/results-2025-10-03T08-30-11-879Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestamp": "2025-10-03T08:30:11.879Z", 3 | "iterations": 15, 4 | "concurrency": 10, 5 | "tests": [ 6 | { 7 | "name": "next-js", 8 | "urls": { 9 | "cloudflare": "https://next-cf-bench.pinglabs.workers.dev/bench", 10 | "vercel": "https://vercel-ssr-bench-v2-hidden.vercel.app/bench" 11 | }, 12 | "results": { 13 | "cloudflare": { 14 | "min": 927.6707499999998, 15 | "max": 3205.217959, 16 | "mean": 2457.6251612, 17 | "successful": 15, 18 | "failed": 0, 19 | "failureRate": 0, 20 | "statusCodes": { 21 | "200": 15 22 | }, 23 | "times": [ 24 | 1835.348209, 25 | 2249.398125, 26 | 2708.077, 27 | 2851.441625, 28 | 2065.962792, 29 | 3001.4945000000002, 30 | 2695.602292, 31 | 2846.8207079999997, 32 | 3052.0703750000002, 33 | 3205.217959, 34 | 1611.6754999999998, 35 | 927.6707499999998, 36 | 2647.667125, 37 | 2448.776833, 38 | 2717.1536249999995 39 | ] 40 | }, 41 | "vercel": { 42 | "min": 523.4515000000001, 43 | "max": 1470.2933750000002, 44 | "mean": 1061.8847028666669, 45 | "successful": 15, 46 | "failed": 0, 47 | "failureRate": 0, 48 | "statusCodes": { 49 | "200": 15 50 | }, 51 | "times": [ 52 | 1221.6202079999994, 53 | 1244.6301670000003, 54 | 1237.778292, 55 | 1250.092834, 56 | 1258.488625, 57 | 1352.272583, 58 | 1337.9544580000002, 59 | 1356.5715, 60 | 1387.925542, 61 | 1470.2933750000002, 62 | 536.4674169999998, 63 | 523.4515000000001, 64 | 530.2047079999993, 65 | 556.6186250000001, 66 | 663.9007090000005 67 | ] 68 | } 69 | } 70 | }, 71 | { 72 | "name": "react-ssr-bench", 73 | "urls": { 74 | "cloudflare": "https://react-ssr-cf.pinglabs.workers.dev/bench", 75 | "vercel": "https://react-ssr-bench-v2.vercel.app/api/bench" 76 | }, 77 | "results": { 78 | "cloudflare": { 79 | "min": 291.6689170000009, 80 | "max": 1778.0732500000004, 81 | "mean": 875.5307833333333, 82 | "successful": 15, 83 | "failed": 0, 84 | "failureRate": 0, 85 | "statusCodes": { 86 | "200": 15 87 | }, 88 | "times": [ 89 | 461.26954099999966, 90 | 486.9812499999989, 91 | 511.46991700000035, 92 | 666.3261669999993, 93 | 739.9230000000007, 94 | 385.4642079999994, 95 | 792.7974999999997, 96 | 291.6689170000009, 97 | 1008.7497909999993, 98 | 540.3075419999986, 99 | 1306.4962920000007, 100 | 1641.4699999999993, 101 | 1274.2380830000002, 102 | 1778.0732500000004, 103 | 1247.7262919999994 104 | ] 105 | }, 106 | "vercel": { 107 | "min": 104.94929100000081, 108 | "max": 659.4865840000002, 109 | "mean": 423.41063319999984, 110 | "successful": 15, 111 | "failed": 0, 112 | "failureRate": 0, 113 | "statusCodes": { 114 | "200": 15 115 | }, 116 | "times": [ 117 | 242.77829099999872, 118 | 146.667958, 119 | 122.28333399999974, 120 | 567.9371250000004, 121 | 577.3238749999982, 122 | 587.3291250000002, 123 | 591.4828749999997, 124 | 616.4079999999994, 125 | 611.0224999999991, 126 | 651.0259999999998, 127 | 644.2301659999994, 128 | 659.4865840000002, 129 | 104.94929100000081, 130 | 122.22883300000103, 131 | 106.00554100000045 132 | ] 133 | } 134 | } 135 | }, 136 | { 137 | "name": "sveltekit", 138 | "urls": { 139 | "cloudflare": "https://cf-sveltekit-bench.pinglabs.workers.dev/", 140 | "vercel": "https://vercel-svelte-bench.vercel.app" 141 | }, 142 | "results": { 143 | "cloudflare": { 144 | "min": 72.55175000000054, 145 | "max": 495.9641250000004, 146 | "mean": 293.46040273333335, 147 | "successful": 15, 148 | "failed": 0, 149 | "failureRate": 0, 150 | "statusCodes": { 151 | "200": 15 152 | }, 153 | "times": [ 154 | 149.5208330000005, 155 | 72.55175000000054, 156 | 310.78420800000094, 157 | 315.95745799999895, 158 | 338.72541700000147, 159 | 373.2408749999995, 160 | 375.9627500000006, 161 | 86.83429199999955, 162 | 438.7385839999988, 163 | 432.31833299999926, 164 | 438.7613330000004, 165 | 221.18062499999905, 166 | 495.9641250000004, 167 | 139.48716699999932, 168 | 211.8782910000009 169 | ] 170 | }, 171 | "vercel": { 172 | "min": 58.80774999999994, 173 | "max": 626.8486250000005, 174 | "mean": 391.7080528000001, 175 | "successful": 15, 176 | "failed": 0, 177 | "failureRate": 0, 178 | "statusCodes": { 179 | "200": 15 180 | }, 181 | "times": [ 182 | 152.39204100000097, 183 | 213.43054200000006, 184 | 66.52825000000121, 185 | 65.62045800000124, 186 | 64.81995800000004, 187 | 557.4174999999996, 188 | 58.80774999999994, 189 | 564.6940419999992, 190 | 606.9629590000004, 191 | 428.48120799999924, 192 | 618.8760839999995, 193 | 614.4223330000004, 194 | 622.9322499999998, 195 | 626.8486250000005, 196 | 613.3867919999993 197 | ] 198 | } 199 | } 200 | }, 201 | { 202 | "name": "shitty-sine-bench", 203 | "urls": { 204 | "cloudflare": "https://vanilla-ssr-cf.pinglabs.workers.dev/shitty-sine-bench", 205 | "vercel": "https://vanilla-bench-v2.vercel.app/api/shitty-sine-bench" 206 | }, 207 | "results": { 208 | "cloudflare": { 209 | "min": 8482.695875000001, 210 | "max": 33649.093166, 211 | "mean": 20634.861377666668, 212 | "successful": 15, 213 | "failed": 0, 214 | "failureRate": 0, 215 | "statusCodes": { 216 | "200": 15 217 | }, 218 | "times": [ 219 | 8482.695875000001, 220 | 10021.330500000002, 221 | 10021.383375000001, 222 | 10896.574791000001, 223 | 16930.658584, 224 | 9931.084291, 225 | 19970.382459, 226 | 21855.974541000003, 227 | 25276.497874999997, 228 | 29681.930875, 229 | 24913.196541999998, 230 | 33649.093166, 231 | 29795.415333, 232 | 24532.652416, 233 | 33564.050042 234 | ] 235 | }, 236 | "vercel": { 237 | "min": 37322.36341599999, 238 | "max": 37694.19637500001, 239 | "mean": 37471.94869720001, 240 | "successful": 15, 241 | "failed": 0, 242 | "failureRate": 0, 243 | "statusCodes": { 244 | "200": 15 245 | }, 246 | "times": [ 247 | 37365.998167, 248 | 37367.215625, 249 | 37392.5885, 250 | 37393.986167, 251 | 37511.364292, 252 | 37563.716250000005, 253 | 37597.824667, 254 | 37621.181791999996, 255 | 37648.02308300001, 256 | 37694.19637500001, 257 | 37322.36341599999, 258 | 37334.63158300001, 259 | 37373.65325, 260 | 37399.30833299999, 261 | 37493.17895800002 262 | ] 263 | } 264 | } 265 | }, 266 | { 267 | "name": "vanilla-slower", 268 | "urls": { 269 | "cloudflare": "https://vanilla-ssr-cf.pinglabs.workers.dev/slower-bench", 270 | "vercel": "https://vanilla-bench-v2.vercel.app/api/slower-bench" 271 | }, 272 | "results": { 273 | "cloudflare": { 274 | "min": 136.70795899999212, 275 | "max": 466.8122910000093, 276 | "mean": 252.2627666666677, 277 | "successful": 15, 278 | "failed": 0, 279 | "failureRate": 0, 280 | "statusCodes": { 281 | "200": 15 282 | }, 283 | "times": [ 284 | 138.7092920000141, 285 | 140.5572080000129, 286 | 174.77537499999744, 287 | 282.94416700000875, 288 | 197.52170800001477, 289 | 330.5684170000022, 290 | 339.6414170000062, 291 | 450.2799170000071, 292 | 392.64795799998683, 293 | 180.44858299999032, 294 | 466.8122910000093, 295 | 136.70795899999212, 296 | 218.28462500000023, 297 | 174.3463749999937, 298 | 159.69620799997938 299 | ] 300 | }, 301 | "vercel": { 302 | "min": 171.38916699998663, 303 | "max": 799.1281670000171, 304 | "mean": 567.667572333328, 305 | "successful": 15, 306 | "failed": 0, 307 | "failureRate": 0, 308 | "statusCodes": { 309 | "200": 15 310 | }, 311 | "times": [ 312 | 464.831749999983, 313 | 783.7909999999974, 314 | 783.8122079999885, 315 | 789.3542920000036, 316 | 781.3627919999999, 317 | 733.8068750000093, 318 | 799.1281670000171, 319 | 791.7659160000039, 320 | 206.575708999997, 321 | 171.38916699998663, 322 | 768.498499999987, 323 | 230.2436249999737, 324 | 747.5976249999949, 325 | 230.32783399999607, 326 | 232.52812499998254 327 | ] 328 | } 329 | } 330 | } 331 | ] 332 | } -------------------------------------------------------------------------------- /runner.js: -------------------------------------------------------------------------------- 1 | // Note: This whole file is vibe coded. I do not know how any of the formatting works. Blame Claude. 2 | 3 | const tests = [ 4 | { 5 | name: "next-js", 6 | cfUrl: 7 | "https://next-cf-bench.theo-s-cool-new-test-account-10-3.workers.dev/bench", 8 | vercelUrl: "https://vercel-ssr-bench-v2-h.vercel.app/bench", 9 | }, 10 | { 11 | name: "react-ssr-bench", 12 | cfUrl: 13 | "https://react-ssr-cf.theo-s-cool-new-test-account-10-3.workers.dev/bench", 14 | vercelUrl: "https://react-ssr-bench-v2-h.vercel.app/api/bench", 15 | }, 16 | { 17 | name: "sveltekit", 18 | cfUrl: 19 | "https://cf-sveltekit-bench.theo-s-cool-new-test-account-10-3.workers.dev/", 20 | vercelUrl: "https://vercel-svelte-bench-h.vercel.app", 21 | }, 22 | // { 23 | // name: "shitty-sine-bench", 24 | // cfUrl: 25 | // "https://vanilla-ssr-cf.theo-s-cool-new-test-account-10-3.workers.dev/shitty-sine-bench", 26 | // vercelUrl: "https://vanilla-bench-v2-h.vercel.app/api/shitty-sine-bench", 27 | // }, 28 | { 29 | name: "realistic-math-bench", 30 | cfUrl: 31 | "https://vanilla-ssr-cf.theo-s-cool-new-test-account-10-3.workers.dev/realistic-math-bench", 32 | vercelUrl: "https://vanilla-bench-v2-h.vercel.app/api/realistic-math-bench", 33 | }, 34 | { 35 | name: "vanilla-slower", 36 | cfUrl: 37 | "https://vanilla-ssr-cf.theo-s-cool-new-test-account-10-3.workers.dev/slower-bench", 38 | vercelUrl: "https://vanilla-bench-v2-h.vercel.app/api/slower-bench", 39 | }, 40 | ]; 41 | 42 | const fs = require("fs"); 43 | const path = require("path"); 44 | 45 | const ITERATIONS = 400; 46 | const CONCURRENCY = 20; 47 | 48 | async function measureResponseTime(url) { 49 | const start = performance.now(); 50 | try { 51 | const response = await fetch(url); 52 | 53 | // Read the response body 54 | const content = await response.text(); 55 | const end = performance.now(); 56 | const responseTime = end - start; 57 | 58 | return { 59 | time: responseTime, 60 | status: response.status, 61 | success: response.ok, 62 | content, 63 | }; 64 | } catch (error) { 65 | return { 66 | time: null, 67 | status: null, 68 | success: false, 69 | error: error.message, 70 | }; 71 | } 72 | } 73 | 74 | async function runBenchmark(url, name, outputDir, timestamp) { 75 | console.log(`\n🏃 Running benchmark for ${name}...`); 76 | console.log(`URL: ${url}`); 77 | console.log(`Iterations: ${ITERATIONS} (concurrency: ${CONCURRENCY})\n`); 78 | 79 | const results = []; 80 | let completed = 0; 81 | let nextIndex = 0; 82 | 83 | // Spawn a fixed number of workers; each pulls the next index until done 84 | async function worker() { 85 | while (true) { 86 | const i = nextIndex++; 87 | if (i >= ITERATIONS) break; 88 | const result = await measureResponseTime(url); 89 | 90 | // Save content to output directory 91 | if (result.success && result.content) { 92 | const safeName = name.replace(/[^a-zA-Z0-9-]/g, "_"); 93 | const contentPath = path.join( 94 | outputDir, 95 | `${timestamp}-${safeName}-${i}.html` 96 | ); 97 | await fs.promises 98 | .writeFile(contentPath, result.content, "utf8") 99 | .catch((err) => { 100 | console.error(`Failed to write content file: ${err.message}`); 101 | }); 102 | } 103 | 104 | results.push(result); 105 | completed++; 106 | process.stdout.write(` Progress: ${completed}/${ITERATIONS}\r`); 107 | } 108 | } 109 | 110 | const workerCount = Math.min(CONCURRENCY, ITERATIONS); 111 | const workers = Array.from({ length: workerCount }, () => worker()); 112 | await Promise.all(workers); 113 | 114 | console.log(`\n`); 115 | 116 | // Analyze results 117 | const successful = results.filter((r) => r.success); 118 | const failed = results.filter((r) => !r.success); 119 | const times = successful.map((r) => r.time); 120 | 121 | // Count status codes 122 | const statusCodes = {}; 123 | results.forEach((r) => { 124 | if (r.status !== null) { 125 | statusCodes[r.status] = (statusCodes[r.status] || 0) + 1; 126 | } 127 | }); 128 | 129 | // Count error types 130 | const errors = {}; 131 | failed.forEach((r) => { 132 | if (r.error) { 133 | errors[r.error] = (errors[r.error] || 0) + 1; 134 | } 135 | }); 136 | 137 | const failureRate = (failed.length / results.length) * 100; 138 | 139 | if (times.length === 0) { 140 | console.log(`❌ No successful requests for ${name}`); 141 | console.log(` Failure rate: ${failureRate.toFixed(2)}%`); 142 | if (Object.keys(statusCodes).length > 0) { 143 | console.log(` Status codes:`, statusCodes); 144 | } 145 | if (Object.keys(errors).length > 0) { 146 | console.log(` Errors:`, errors); 147 | } 148 | return null; 149 | } 150 | 151 | const min = Math.min(...times); 152 | const max = Math.max(...times); 153 | const mean = times.reduce((a, b) => a + b, 0) / times.length; 154 | 155 | return { 156 | min, 157 | max, 158 | mean, 159 | successful: successful.length, 160 | failed: failed.length, 161 | failureRate, 162 | statusCodes, 163 | errors: Object.keys(errors).length > 0 ? errors : undefined, 164 | times, 165 | }; 166 | } 167 | 168 | function formatTime(ms) { 169 | return `${(ms / 1000).toFixed(3)}s`; 170 | } 171 | 172 | async function main() { 173 | // Setup output directory and timestamp 174 | const timestamp = new Date().toISOString(); 175 | const safeStamp = timestamp.replace(/[:.]/g, "-"); 176 | const outputDir = path.resolve(__dirname, "out"); 177 | await fs.promises.mkdir(outputDir, { recursive: true }); 178 | 179 | // Array to capture formatted output (only for summary) 180 | let formattedOutput = []; 181 | 182 | console.log("=".repeat(60)); 183 | console.log(" SSR Performance Benchmark: Cloudflare vs Vercel"); 184 | console.log("=".repeat(60)); 185 | 186 | const allResults = []; 187 | 188 | for (const test of tests) { 189 | const sectionHeader = `\n${"-".repeat(60)}\nTest: ${test.name}\n${"-".repeat(60)}`; 190 | console.log(sectionHeader); 191 | 192 | const cfResults = await runBenchmark( 193 | test.cfUrl, 194 | `${test.name} - Cloudflare`, 195 | outputDir, 196 | `${safeStamp}-cf` 197 | ); 198 | const vercelResults = await runBenchmark( 199 | test.vercelUrl, 200 | `${test.name} - Vercel`, 201 | outputDir, 202 | `${safeStamp}-vercel` 203 | ); 204 | 205 | const resultsHeader = `${"=".repeat(60)}\n RESULTS (${test.name})\n${"=".repeat(60)}`; 206 | console.log(resultsHeader); 207 | 208 | if (cfResults) { 209 | const cfOutput = [ 210 | "\n📊 Cloudflare Results:", 211 | ` Successful requests: ${cfResults.successful}/${ITERATIONS}`, 212 | ]; 213 | if (cfResults.failed > 0) { 214 | cfOutput.push(` Failed requests: ${cfResults.failed}/${ITERATIONS}`); 215 | cfOutput.push(` Failure rate: ${cfResults.failureRate.toFixed(2)}%`); 216 | cfOutput.push( 217 | ` Status codes: ${JSON.stringify(cfResults.statusCodes)}` 218 | ); 219 | if (cfResults.errors) { 220 | cfOutput.push(` Errors: ${JSON.stringify(cfResults.errors)}`); 221 | } 222 | } 223 | cfOutput.push(` Min: ${formatTime(cfResults.min)}`); 224 | cfOutput.push(` Max: ${formatTime(cfResults.max)}`); 225 | cfOutput.push(` Mean: ${formatTime(cfResults.mean)}`); 226 | 227 | cfOutput.forEach((line) => { 228 | console.log(line); 229 | }); 230 | } 231 | 232 | if (vercelResults) { 233 | const vercelOutput = [ 234 | "\n📊 Vercel Results:", 235 | ` Successful requests: ${vercelResults.successful}/${ITERATIONS}`, 236 | ]; 237 | if (vercelResults.failed > 0) { 238 | vercelOutput.push( 239 | ` Failed requests: ${vercelResults.failed}/${ITERATIONS}` 240 | ); 241 | vercelOutput.push( 242 | ` Failure rate: ${vercelResults.failureRate.toFixed(2)}%` 243 | ); 244 | vercelOutput.push( 245 | ` Status codes: ${JSON.stringify(vercelResults.statusCodes)}` 246 | ); 247 | if (vercelResults.errors) { 248 | vercelOutput.push( 249 | ` Errors: ${JSON.stringify(vercelResults.errors)}` 250 | ); 251 | } 252 | } 253 | vercelOutput.push(` Min: ${formatTime(vercelResults.min)}`); 254 | vercelOutput.push(` Max: ${formatTime(vercelResults.max)}`); 255 | vercelOutput.push(` Mean: ${formatTime(vercelResults.mean)}`); 256 | 257 | vercelOutput.forEach((line) => { 258 | console.log(line); 259 | }); 260 | } 261 | 262 | if (cfResults && vercelResults) { 263 | const comparisonOutput = ["\n📈 Comparison:"]; 264 | const ratio = cfResults.mean / vercelResults.mean; 265 | if (ratio > 1) { 266 | comparisonOutput.push( 267 | ` Vercel is ${ratio.toFixed(2)}x faster than Cloudflare (by mean)` 268 | ); 269 | } else { 270 | comparisonOutput.push( 271 | ` Cloudflare is ${(1 / ratio).toFixed( 272 | 2 273 | )}x faster than Vercel (by mean)` 274 | ); 275 | } 276 | 277 | comparisonOutput.forEach((line) => { 278 | console.log(line); 279 | }); 280 | } 281 | 282 | allResults.push({ 283 | name: test.name, 284 | urls: { cloudflare: test.cfUrl, vercel: test.vercelUrl }, 285 | results: { cloudflare: cfResults, vercel: vercelResults }, 286 | }); 287 | } 288 | 289 | const separator = "\n" + "=".repeat(60); 290 | console.log(separator); 291 | 292 | // Output final results summary for README 293 | const summaryHeader = [ 294 | "=".repeat(60), 295 | " FINAL RESULTS SUMMARY", 296 | "=".repeat(60) + "\n", 297 | ]; 298 | summaryHeader.forEach((line) => { 299 | console.log(line); 300 | formattedOutput.push(line); 301 | }); 302 | 303 | for (const result of allResults) { 304 | const cf = result.results.cloudflare; 305 | const vercel = result.results.vercel; 306 | 307 | const testHeader = `## ${result.name}`; 308 | console.log(testHeader); 309 | console.log(); 310 | formattedOutput.push(testHeader); 311 | formattedOutput.push(""); 312 | 313 | if (cf && vercel) { 314 | const ratio = vercel.mean / cf.mean; 315 | const winner = ratio > 1 ? "Cloudflare" : "Vercel"; 316 | const speedup = ratio > 1 ? ratio : 1 / ratio; 317 | 318 | const cfVariability = cf.max - cf.min; 319 | const vercelVariability = vercel.max - vercel.min; 320 | 321 | const tableOutput = [ 322 | `| Platform | Mean | Min | Max | Variability |`, 323 | `|------------|------|-----|-----|-------------|`, 324 | `| Cloudflare | ${formatTime(cf.mean)} | ${formatTime(cf.min)} | ${formatTime(cf.max)} | ${formatTime(cfVariability)} |`, 325 | `| Vercel | ${formatTime(vercel.mean)} | ${formatTime(vercel.min)} | ${formatTime(vercel.max)} | ${formatTime(vercelVariability)} |`, 326 | "", 327 | `**Winner:** ${winner} (${speedup.toFixed(2)}x faster)`, 328 | "", 329 | ]; 330 | 331 | tableOutput.forEach((line) => { 332 | console.log(line); 333 | formattedOutput.push(line); 334 | }); 335 | } 336 | } 337 | 338 | const footer = [ 339 | "---", 340 | `\n*Benchmark run: ${new Date().toISOString().split("T")[0]} • ${ITERATIONS} iterations • Concurrency: ${CONCURRENCY}*`, 341 | "\n" + "=".repeat(60) + "\n", 342 | ]; 343 | footer.forEach((line) => { 344 | console.log(line); 345 | formattedOutput.push(line); 346 | }); 347 | 348 | // Write formatted results to text file 349 | try { 350 | const resultsDir = path.resolve(__dirname, "results"); 351 | await fs.promises.mkdir(resultsDir, { recursive: true }); 352 | 353 | const textFilePath = path.join(resultsDir, `results-${safeStamp}.txt`); 354 | await fs.promises.writeFile( 355 | textFilePath, 356 | formattedOutput.join("\n"), 357 | "utf8" 358 | ); 359 | console.log(`📝 Formatted results written to: ${textFilePath}`); 360 | } catch (err) { 361 | console.error("Failed to write formatted results file:", err.message); 362 | } 363 | 364 | // Write consolidated results to results-(datetime).json inside results/ directory 365 | try { 366 | const resultsDir = path.resolve(__dirname, "results"); 367 | const jsonFilePath = path.join(resultsDir, `results-${safeStamp}.json`); 368 | 369 | const summary = { 370 | timestamp, 371 | iterations: ITERATIONS, 372 | concurrency: CONCURRENCY, 373 | tests: allResults, 374 | }; 375 | 376 | await fs.promises.writeFile( 377 | jsonFilePath, 378 | JSON.stringify(summary, null, 2), 379 | "utf8" 380 | ); 381 | console.log(`📝 JSON results written to: ${jsonFilePath}`); 382 | } catch (err) { 383 | console.error("Failed to write JSON results file:", err.message); 384 | } 385 | 386 | console.log(`📁 Response content written to: ${outputDir}/`); 387 | } 388 | 389 | main().catch(console.error); 390 | --------------------------------------------------------------------------------