Skip to content

Repository files navigation

Workload Cartography

CPU vs GPU workload placement experiment — determining where a data engineering pipeline runs faster by benchmarking identical workloads on both processing units in the browser.

What This Does

This experiment runs a four-stage data pipeline — filter, normalize, transform, aggregate — on both the CPU (single-threaded TypeScript) and the GPU (WebGPU via TypeGPU), then compares their performance across different input sizes. The goal is to find the crossover point where GPU parallelism overcomes the overhead of data transfer.

GPU Data Flow

Architecture

The same synthetic dataset is fed into two parallel paths. Both produce per-category aggregates (count, sum, mean, variance), which are then compared for correctness. Timing is recorded independently for each path, with GPU timings broken down into write, compute, and readback phases.

Experiment Architecture

Pipeline Stages

Stage CPU Implementation GPU Implementation
Filter Sequential scan, compacted index list Parallel flag array (1/0 per record)
Normalize Two-pass min/max, then (v - min) / range Atomic min/max via IEEE 754 bitcast trick
Transform sin(norm * PI) * exp(-norm) + log(1 + norm) in f64 Same formula in f32 (GPU has no f64)
Aggregate Direct f64 accumulation per category Fixed-point integer atomics (scale = 100)

Key Design Decisions

  • No WGSL written by hand. GPU kernels are authored as TypeScript functions with a 'use gpu' directive. The TypeGPU build plugin transpiles them to WGSL at build time.
  • Fixed-point aggregation. WGSL lacks f32 atomics, so sums are scaled by 100, truncated to u32, and accumulated with atomicAdd. This introduces quantization error (~2 decimal places).
  • Bitcast atomic trick. For non-negative floats, IEEE 754 bit patterns preserve ordering when reinterpreted as u32, enabling atomicMin/atomicMax for the normalization bounds.
  • Verification with tolerance. Counts must match exactly. Means and variances are compared with 2% relative error tolerance to account for f32 vs f64 precision and fixed-point quantization.

Sample Results

Results from an Apple M-series GPU (Metal 3) via Chrome WebGPU:

Benchmark Results

Key observations:

  • At 10K records, the CPU wins — GPU overhead (buffer creation, dispatch, readback) dominates.
  • The crossover occurs around 100K records, where GPU compute starts to offset transfer costs.
  • At 1M+ records, the GPU pulls ahead significantly (3.5-4.9x faster).
  • At 10M records, a count mismatch appears in verification — this is a known limitation of the current buffer size handling on some WebGPU implementations.

Project Structure

src/
  main.ts              Entry point — WebGPU check, UI mount
  types.ts             Shared constants, interfaces, GPU struct schemas
  generate.ts          Deterministic synthetic data generation (seeded PRNG)
  cpu/pipeline.ts      CPU benchmark path (sequential TypeScript)
  gpu/pipeline.ts      GPU benchmark path (6 WebGPU compute kernels)
  bench/runner.ts      Orchestrator — warmup, trials, timing, verification
  bench/timing.ts      Median calculation for timing arrays
  verify.ts            CPU vs GPU correctness comparison
  results.ts           JSON export / download
  ui.ts                Browser UI (vanilla DOM)

Getting Started

Prerequisites

  • Node.js 18+
  • A browser with WebGPU support (Chrome 113+, Edge 113+)

Run

npm install
npm run dev

Open the local URL shown by Vite. Select an input size and click Run Benchmark or Run All Sizes.

Build

npm run build
npm run preview

How Timing Works

  • CPU: performance.now() wraps all four pipeline stages. Median of 10 trials after 3 warmup runs.
  • GPU: Split into two segments:
    • writeMs — buffer creation + CPU-to-GPU data transfer
    • computeAndReadMs — kernel dispatch + execution + GPU-to-CPU readback
    • totalMs — sum of both
  • Data generation is not timed — the same ArrayBuffer is reused across all trials.

Tech Stack

  • TypeScript — all source code
  • TypeGPU — write WebGPU compute shaders in TypeScript
  • Vite — dev server and bundler
  • WebGPU — browser GPU compute API

About

Browser-based benchmark comparing CPU (TypeScript) vs GPU (WebGPU) performance on a data pipeline: filter → normalize → transform → aggregate. Tests identical workloads across input sizes (10K–10M records) to find the crossover where GPU parallelism beats transfer overhead. Includes correctness verification and detailed timing breakdowns.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages