666db68matmul-bench: browser GEMM benchmark (JS, WebGPU, custom C WASM, libFLAME/BLIS WASM)Jeremy Magland 1import {
2 AppBar, Container, createTheme, CssBaseline, Link, Paper, Table, TableBody,
3 TableCell, TableContainer, TableHead, TableRow, ThemeProvider, Toolbar, Typography,
4} from '@mui/material'
5import { BenchmarkRunner } from './components/BenchmarkRunner'
6import { NATIVE_REFERENCE, NATIVE_MAX_THREADS } from './data/nativeReference'
8const theme = createTheme({
9 palette: { mode: 'light', primary: { main: '#1565c0' } },
10})
12function App() {
13 return (
14 <ThemeProvider theme={theme}>
15 <CssBaseline />
16 <AppBar position="static" elevation={0}>
17 <Toolbar>
18 <Typography variant="h6" sx={{ flexGrow: 1 }}>matmul-bench</Typography>
19 <Link href="https://github.com/concept-collection/matmul-bench" target="_blank" rel="noreferrer" color="inherit" underline="hover">
20 GitHub
21 </Link>
22 </Toolbar>
23 </AppBar>
25 <Container maxWidth="md" sx={{ py: 3 }}>
26 <Typography variant="body1" sx={{ mb: 2 }}>
27 Compares n×n matrix-matrix multiply (GEMM) across implementations
28 running in the browser — plain JavaScript/TypeScript, a WebGPU
29 compute shader, hand-optimized C compiled to WebAssembly (including a
30 multi-threaded build), and libFLAME/BLIS (a real BLAS) compiled to
31 WebAssembly, single- and multi-threaded — plus a fixed reference
32 table from native LAPACK/OpenBLAS running outside the browser.
33 </Typography>
35 <BenchmarkRunner />
37 <Typography variant="h6" sx={{ mt: 4, mb: 1 }}>Native reference: OpenBLAS dgemm</Typography>
38 <Typography variant="caption" color="text.secondary" component="div" sx={{ mb: 1 }}>
39 Measured with <code>native/bench_native.c</code> outside the browser on one
40 Linux desktop — hardware-dependent, for rough comparison only. Regenerate with{' '}
41 <code>native/build.sh && native/bench_native</code>.
42 </Typography>
43 {NATIVE_REFERENCE.length > 0 ? (
44 <TableContainer component={Paper} variant="outlined" sx={{ maxWidth: 480 }}>
45 <Table size="small">
46 <TableHead>
47 <TableRow>
48 <TableCell>n</TableCell>
49 <TableCell align="right">1 thread (GFLOP/s)</TableCell>
50 <TableCell align="right">{NATIVE_MAX_THREADS} threads (GFLOP/s)</TableCell>
51 </TableRow>
52 </TableHead>
53 <TableBody>
54 {NATIVE_REFERENCE.map((r) => (
55 <TableRow key={r.n}>
56 <TableCell>{r.n}</TableCell>
57 <TableCell align="right">{r.gflops1t.toFixed(1)}</TableCell>
58 <TableCell align="right">{r.gflopsNt.toFixed(1)}</TableCell>
59 </TableRow>
60 ))}
61 </TableBody>
62 </Table>
63 </TableContainer>
64 ) : (
65 <Typography variant="body2" color="text.secondary">(not yet captured)</Typography>
66 )}
67 <Typography variant="caption" color="text.secondary" component="div" sx={{ mt: 1 }}>
68 Threading overhead dominates at small n — the {NATIVE_MAX_THREADS}-thread
69 column is slower than 1 thread below n≈512 on this machine.
70 </Typography>
72 <Typography variant="body2" color="text.secondary" sx={{ mt: 3 }}>
73 The threaded methods (custom C and libFLAME/BLIS) use WASM pthreads
74 (Web Workers + SharedArrayBuffer) and need cross-origin isolation,
75 provided here by a service worker; pick the thread count above. The
76 libFLAME/BLIS methods link a real BLAS (BLIS generic C kernels with{' '}
77 <code>-msimd128</code>) compiled to WebAssembly — see{' '}
78 <Link href="https://github.com/magland/libflame2wasm" target="_blank" rel="noreferrer">libflame2wasm</Link>{' '}
79 — and get far closer to native single-threaded OpenBLAS than the
80 hand-written C kernels.
81 </Typography>
83 <Typography variant="caption" color="text.secondary" component="div" sx={{ mt: 4 }}>
84 Part of the{' '}
85 <Link href="https://github.com/concept-collection" target="_blank" rel="noreferrer">concept-collection</Link>.
86 </Typography>
87 </Container>
88 </ThemeProvider>
89 )
90}
92export default App