Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cellmark

A stress test and benchmark suite for the PlayStation 3.

cellmark started as a way to verify Cell BE / XDR overclock stability against memtest86+ style patterns and a deterministic FMA accumulator. It has since grown into a fuller hardware characterisation suite covering the PPE core, all 6 SPEs, the EIB ring fabric, the XDR memory subsystem, and the disk path.

Both retail CFW and DECR-1000A development-kit build variants are supported. The DECR build links libperf for direct CBE Performance Monitor access on dev hardware.

A preview of cellmark


What's in this repo

ppu/main.c                entry point

engine/                   bench-agnostic framework + shared services
  cellmark_engine.{c,h}   bench module dispatch
  bench_modules.c         all bench_module_t entries
  bench_registry.c        the canonical NULL-terminated module array
  spu.{c,h}, gcm.{c,h}    SPU thread group lifecycle, GCM display
  pmu.{c,h}, cell_pmu.{c,h}  libperf wrapper + cell-side sampling
  sysmon.{c,h}, render.{c,h} status string + render helpers
  spu_shared/             SPU code used by multiple benches

benches/<name>/           one self-contained directory per benchmark
  _template/              copy-source for new benches (see adding_a_benchmark.md)
  cell/    ppe/    disk/    dma/    eib/    atomic/    mbox/    branch/
  pi/      fft/    nbody/   workload/   burn/   mandelbrot/

include/                  public headers
  bench.h                 bench_module_t interface
  stress_common.h         PPU/SPE shared types

tools/
  build.py                build driver (replaces build.bat)
  new_bench.py            scaffold a new bench from _template/

docs/
  cell_tuning.md          optimization bible + measurement bible
  architecture.md         how cellmark is structured internally
  adding_a_benchmark.md   guide for extending cellmark
  autobench.md            headless scripted runs + stock baseline
  ppe_tuning.md, spu_tuning.md, disk_tuning.md  subsystem deep dives

build/                    all build outputs land here (gitignored)

For a deeper look at the engine internals see docs/architecture.md; to add your own benchmark, docs/adding_a_benchmark.md. For what changed between releases - including the corrections to findings cellmark previously published - see CHANGELOG.md.


What cellmark actually does (full app)

Eleven pages, switched with L2/R2 on a controller. L1/R1 cycle the variant within a page.

1. Cell - SPU compute (all 6 SPEs in parallel)

Mode Pipeline Peak (3.2 GHz) Purpose
SP FMA Stress Even (SP float) 153.6 GFLOPS OC stability, deterministic verify
DP FMA Stress Even (DP float) 13.1 GFLOPS DP throughput (7-cycle stall)
Int Multiply Even (mpya) 153.6 GIOPS Integer pipeline
Recip/Rsqrt Even (estimate+NR) 76.8 GOPS Newton-Raphson refinement
Shuffle Storm Odd (shufb) 76.8 GOPS Odd pipeline
Dual-Issue Even+Odd 230.4 GOPS Theoretical max

SP FMA stress verifies a deterministic accumulator chain at intervals; if a single bit drifts, the SPE reports an error and the run is flagged unstable (unlikely for this to happen). Other modes track throughput only.

2. PPE - PowerPC core benchmarks (PPE0 thread, SPUs idle)

  • VMX FMA - vmaddfp throughput, 20 independent chains. 23.5 GFLOPS = 92% of the 25.6 peak; the original 6-chain kernel measured 12.2 and was mistaken for an SMT platform ceiling. The kernel reports a 12/16/20/24-chain sweep every run: 12 and 16 tie at ~20.9, the knee is at 20 (PMU confirms the cost is register-dependency issue stalls, with dispatch-blocked at 0.5%)
  • FP scalar FMA / FXU add - 12 chains of fmadd (10-cycle) = 5.4 GFLOPS, 84% of peak; add at 3.0 GOPS, 94%
  • SMT scaling - TH0+TH1, same-unit and cross-unit. 1.18x with correctly chained kernels (the previously published 1.30x was measured against under-chained ones - SMT was reclaiming holes our own code left)
  • L1 / L2 read bandwidth - hand-scheduled VMX loop, every pair (vaddfp slot 0, lvx slot 1) since an LSU op in slot 0 stalls a VXU op in slot 1. 16-18.6 / ~15 GB/s (±15% boot-to-boot scatter; page placement suspected) - 16 accumulators, since vaddfp is 12-cycle latency and the original 4 throttled the loads that fed them; L2 prefetches 1.5 KB ahead with dcbt. A scalar 8 B ld twin runs in the same batch and beats the vector loop at both levels (up to 21 GB/s = 0.82 loads/cycle), confirming SCEI's own table. For scale, their Reference Tool measures 10.1 / 5.3 GB/s VMX on the same silicon
  • Store bandwidth - stvx / std streams + dcbz128 line fill: ~13 / 9.6 / 16.2 GB/s to L2, and 5.0 GB/s when the target misses L2 (each 16 B store forces a 128 B RWITM read first). L1-resident stores are ~25% slower than L1-missing ones - the write-through L1D updates both caches on a store hit. Stream stores to cold-in-L1 buffers
  • Page-size probe - 8 MB chase + stream on 64 K vs 1 M pages: identical (125 ns/hop, 5.4 GB/s). PPE translation is free; don't chase large pages. Also the first PPU-from-XDR streaming number: 5.4 GB/s, a quarter of one SPE's MFC rate
  • L1 / L2 latency - Pointer chase using Sattolo's algorithm. 1.77 ns (5.7 cyc, the documented dependent-load floor) / 10.96 ns (SCEI reference: 1.6 / 12.6 ns)

3. XDR - memtest86+ style memory test (15 patterns)

15 sub-tests cycle automatically across all 6 SPEs in parallel, partitioning XDR into per-SPE slices. Allocates as much XDR as the OS will give.

Sequential, Walking 1/0, Checkerboard, Random, Moving Inversions, Bank Hammer, R/W Turnaround, Bandwidth, Own Address, Modulo-N, Block Move, Bit Fade, BW Pipelined, Coherency, Latency.

4. Disk I/O - HDD/SSD/SSHD characterisation

Sequential 64KB and random 4K read/write on a 32 MB test file (writes are post-fsync these are true media latency, not write-cache). A 13-probe diagnostic suite separates per-call LV2 overhead from FAT cluster fragmentation, exercises cellFsSetIoBuffer and cellFsStRead, and finishes with three contention probes that simulate a real game's I/O mix (audio + texture streaming + sequential level load). See docs/disk_tuning.md for what those probes actually told us.

5. Cell DMA BW - aggregate XDR <-> LS bandwidth

All 6 SPEs in parallel pulling (GET) or pushing (PUT) 16 KB MFC chunks against XDR through a double-buffered pipeline. Each SPE owns a 1 MB XDR slice so they don't fight for the same cache lines. Headline numbers on stock 3.2 GHz: GET ~22.8 GB/s, PUT ~22.1 GB/s, both within ~13% of the 25.6 GB/s XDR1 DRAM ceiling. Stresses the EIB-to-MIC path, the bottleneck is DRAM, not the EIB or the MFC.

6. Fabric - EIB / atomic / mailbox / branch hint

Cell's interconnect characterised seven ways:

  • Pairs: 3 disjoint SPE<->SPE LS-to-LS flows via the SDK fixed peer-LS EA mapping. Aggregate ~115 GB/s across 6 SPEs (74% of per-direction ring ceiling).
  • Saturate: sweeps all 15 SPE pair-matchings to discover the hypervisor-hidden physical placement at runtime, then presses the winner with GET+PUT. Best matching: 137.9 GB/s = 90% of the 6-SPE port-bound ceiling (153.6), +24% over the fixed logical pairing; the worst matching is half the best. The 204.8/307.2 GB/s marketing numbers require the 8-SPE ring and are unreachable by arithmetic on PS3 silicon.
  • Hotspot (5→1): many-to-one gather into one SPE's LS, ~23.7 GB/s 93% of the 25.6 GB/s LS port ceiling).
  • Hotspot sweep (N=1..5): proves the destination LS port is the bottleneck immediately at N=1; adding readers splits the same pool.
  • NxN matrix (30 pairs): resolves the EIB ring's cyclic order - logical SPE IDs 0–5 follow the physical ring order, though not uniformly spaced (the saturate sweep proves PPE/MIC/IOIF stops interleave between SPE sites).
  • Atomic cache (getllar/putllc ping-pong): 150 ns/bounce between two SPEs, the locking fabric latency floor. A free-for-all contention sweep (2-6 SPEs hammering one line) degrades only 7.25 -> 5.33 Mops with loss rate saturating at 48% - lock-free structures stay viable at maximum contention.
  • Mailbox (PPE<->SPE with lv2 event queue): 11.6 μs round-trip - ~75x slower than atomic cache, dominated by lv2 syscall overhead.
  • SPE branch hint (hbrr): 17 vs 30 cyc/iter on a tight 12-cyc loop body; 1.71x speedup for correctly hinted backward branches.

All of these characterise different parts of the same EIB fabric and were published first on retail/DECR PS3 silicon as part of cellmark. See docs/cell_tuning.md §3 for the full architectural analysis.

7. Workload - real-workload benchmarks with scoring

Cinebench-style: each workload gets a normalised score (stock 3.2 GHz = 100), composite = geometric mean across all active workloads.

  • Pi BBP - Bailey-Borwein-Plouffe hex digit extraction at position N=10000. Realistic "DP scalar number theory" workload, deliberately showcases Cell's weakness (~10% of DP peak even with the DP divide/compare/floor emulated by hand-rolled native sequences; the portable-C version managed 1.2%). 321 digits/sec at stock.
  • FFT - 1D radix-4 complex SP FFT, N=1024 (4^5, 5 passes), SIMD batched across 4 vector lanes (CellBuzz/FFTC-inspired, twiddle-free first pass, pre-splatted N-entry twiddle tables, 4-butterfly unroll). 725.7 Mpoints/sec at stock, ~24% of SP-FMA peak - 2.29x over the radix-2 kernel, 3.9x over v2.0. Self-test validates every twiddle (impulse@1 vs w^k) each batch.
  • N-body - all-pairs gravitational, N=4080, 50 iterations/batch, hardware rsqrte, 4 interleaved interaction chains to cover the ~59-cycle dependency path. SPU's strength workload: 3978 Mpairs/sec at stock = ~49% of SP-FMA peak (~76 GFLOPS aggregate).

7.1 The CELLMARK score

autobench.cfg = score runs the headline benchmark: five compute disciplines, each normalised so a stock 3.2 GHz PS3 scores 1000, with the CELLMARK number as their geometric mean:

discipline exercises stock reference
Pi BBP DP scalar / number theory (Cell's weak side) 321.2 digits/s
FFT radix-4 SIMD butterfly, LS-bound 725.7 Mpts/s
N-body SP FMA + rsqrt, compute-bound 3978 Mpairs/s
Mandelbrot (autopilot dive) iterative escape, branch/pipeline 2448.9 Miter/s
Kleinian Reflectoids (keyframe flight) ray-march DE + reflections, recip-heavy 8.13 fps

The render disciplines fly a deterministic autopilot (per-frame camera path, identical frame sequence every run) rather than a static view - the mandelbrot dive keeps the SPEs in deep high-iteration regions and off the vsync cap, so both are honestly compute-bound. R3 toggles the autopilot manually on either renderer.

The geometric mean means no single discipline can carry the score - an overclock that only helps SP FMA moves N-body but the composite moves by the fifth root. Per-discipline sub-scores are printed alongside so a skewed chip shows where it is skewed. Candidate future disciplines (each exercising a currently-unscored unit): AES/SHA (integer/shuffle), DCT/JPEG encode (fixed-point SIMD), convolution/FIR (LS streaming), and a rigid-body physics solve (gather/scatter + branchy SP).

7.2 Autobench - headless scripted runs

Every suite above (and the score) can run with no controller and no human: drop an autobench.cfg listing suite names next to the app or in /dev_hdd0/cellmark/, and cellmark walks the steps, prints AUTOBENCH result id=... key=value lines to TTY, and exits. One command measures a build end-to-end (~8 min for everything); a stored baseline turns any future run into a diff. Config format, suite list, deploy recipe, and the rules for reading each field honestly (which numbers are vsync-locked, which scatter boot-to-boot, why render comparisons use fixed frame counts) are in docs/autobench.md, with the v2.5 stock DECR baseline table.

8. Burn-In - all-units saturation

The v2.4 "inferno": every measured unit of the chip at once, with each running the pattern its own bench proved optimal. All 6 SPEs grind dual-issue FMA bursts with a 10-deep MFC DMA ring polled between bursts - 4 SPEs carry LS-pair traffic on the topology-discovered ring matching, 2 SPEs stream XDR get+put - plus periodic free-for-all atomics on a shared line. PPE TH0 runs 20-chain VMX + dcbz/stvx store bursts (castouts add a second XDR master); TH1 runs 12-chain scalar FPU. Steady state on stock: 112 GFLOPS SPE compute + 82.5 GB/s EIB + 19.6 GB/s XDR + both PPE threads + 2 Mops atomics, simultaneously and indefinitely - saturation score ~72, with every point below 100 attributable to a named shared resource (LS-port arbitration, SBI queues, SMT dispatch). Building it also uncovered the MFC head-of-line rule (see cell_tuning §2.3.5): mixing XDR-bound and LS-bound DMA in one MFC queue collapses the fast class 5x. Leave it running for hours to find thermal/stability issues.

9. Render - SPE Mandelbrot with RSX display

The first cellmark bench that puts pixels on screen instead of just text. All 6 SPEs compute a 1920x1080 (or fallback) ARGB Mandelbrot frame straight into XDR; RSX's NV3089 2D image scaler blits MAIN->LOCAL onto the active scanout buffer over FlexIO. Two-pack interleaved SIMD inner loop (8 pixels per outer iter; the Michael Kohn naken_asm trick applied through GCC intrinsics) runs 10 even-pipe instructions per 4-pixel pack per iteration - escaped lanes iterate on saturating garbage instead of being select-frozen (SPU SP has no inf/nan, and the alive mask is sticky), and the alive mask itself is the iteration counter. Cardioid + period-2 bulb analytic test pre-traps lanes guaranteed in-set, skipping the iteration loop entirely for the bulk of the heart-shape interior (pre-trapped pixels are not counted in Miter/s - the metric is executed iterations only). View-change detection caches the last computed frame so static viewing drops the SPE batch and runs at vsync 60 fps with the cached buffer.

Interactive: left stick pans, right stick Y zooms, X/O step zoom, d-pad LEFT/RIGHT cycles palett (rainbow, fire, ocean, grayscale, electric), d-pad UP/DOWN rotates hue, Square cycles max_iter 64 4096, L1 resets the view. SELECT/R3 toggles the same deterministic seahorse-dive autopilot as the SPE renderer (both fractal renderers fly the identical frame sequence).

If the 9 MB 1080p buffer won't allocate (tight retail heap), the bench walks a resolution fallback ladder and uses RSX bilinear (NV3089 FOH) to upscale whatever fits onto the full screen, so you still get fullscreen output. Exercises subsystems no other bench touches: the RSX 2D transfer engine, IO MMU mapping (cellGcmMapMainMemory), and the FlexIO main-mem -> VRAM path.

9.1 RSX Mandelbrot - fragment ALU calibration (v2.5)

A second, RSX-side Mandelbrot on its own page: the same seahorse dive computed entirely in a fragment shader instead of on the SPEs, which makes it a direct instrument for the RSX's arithmetic throughput.

Fragment ALU calibration (autobench.cfg = rsxmandel): it ships a second, branch-free fragment shader that runs exactly 255 iterations on every pixel, making W*H*255*fps an exact ALU rate. Measured: 2.64 Giter/s fp32 = 1.08x the entire 6-SPE aggregate (fp32 runs at full fp16 rate; the G70's half types only pay via register pressure). The shipping early-out shader beats brute force 1.51x despite the G70's dynamic-branch penalty. And the answer to "why is RSX mandelbrot slow": 1080p x 255 iterations is 528M iterations/frame - ~20x a 60 fps budget at the measured rate; the SPE version wins wall-clock 3.2x because its early-out is mask-granular and its cardioid pre-trap is free. See docs/cell_tuning.md §5.5.

9.2 Kleinian raymarcher - ray compaction (v2.5)

The Reflectoids Pseudo-Kleinian raymarcher (a faithful port of knighty's boxplorer2 scene: multi-bounce reflections, 5-tap AO, orbit-trap colouring) is the most divergent SPE workload in cellmark - adjacent rays terminate hundreds of steps apart. v2.5 replaces the fixed 16-ray packet tracer with a ray-compacted persistent wavefront: 16 lanes always carry live rays, a terminated lane is queued for shading and instantly refilled (reflection bounce or next pixel), and shading runs 8 real terminations at a time. Frames render in 8-row strips since rays finish out of order.

On top of compaction, the v2.5 kernel: fuses all four ray groups through a single 16-wide DE fold body (GCC never jams adjacent 8-wide calls - verified in the disassembly); marches with over-relaxed sphere tracing (ω=1.4, sphere-overlap rollback, exact distances kept for termination and shading); drops the DE's inf/nan guards (SPU single-precision saturates instead); and computes normals (4-tap tetrahedral) and the 5-tap AO as saturated 16-wide DE batches

  • the AO tap distances are a fixed geometric sequence, so the "serial" occlusion chain is really ten independent evaluations.

Measured on identical frame sets of the deterministic keyframe flight (the kbench harness), the full v2.5 kernel over the v2.4 packet tracer: 1.31x fast / 2.27x default / 2.76x quality - quality goes 2.53 -> 6.99 fps. The march runs at the even-pipe issue ceiling. autobench.cfg = kbench measures a fixed frame count per preset (180/90/60 frames from the path start) so builds of different speed render the identical frame set - a fixed time window lets faster builds fly deeper into the path and skews every average. wall_ms over the fixed frame set is the only valid cross-build comparator.

RSX assists (v2.5): the raymarcher now uses the RSX for three things beyond scaling. (1) An FXAA post pass samples the SPE frame as an XDR texture and writes the scanout at native res (always on; the NV3089 blit remains as the no-shader fallback), costing ~8 ms of display path for real edge AA. (2) An optional depth-of-field pass (L3) blurs by circle-of-confusion from an SPE-written primary-hit depth buffer, with autofocus on the screen centre. (3) A pipelined cone-march prepass (D-pad UP): a fragment shader marches the PKlein DE at 160x90 for the NEXT autopilot frame while the SPEs render the current one, and primary rays start at the conservative distance it reports - a byte-packed ARGB8 target, because G70 float-RT writes come out zero. The prepass ships off by default: the relaxed march superseded its win everywhere but ~7% at fast preset, and its glow term is inherently quantized to the 4x4 map texels - visible as a block grid on the flight. The kpre autobench suite still measures it for the record (see cell_tuning §5.4).


Running it

The compiled package is on the Releases page.

  1. Transfer cellmark.pkg to the PS3 (FTP, USB, /dev_hdd0/packages/, etc.)
  2. Install via Package Manager (★ Install Package Files)

Controls:

Button Action
L2 / R2 Previous / next page (category)
L1 / R1 / D-pad Previous / next benchmark within page
X (Cross) Run selected disk bench / probe suite
Square Toggle disk bench / probe view
Triangle Toggle file logging
SELECT + START Exit

On the Render page the Mandelbrot bench takes over the analog sticks and d-pad for pan/zoom/palette/hue. The global L2/R2 page navigation still works to leave.

File logging appends to /dev_hdd0/game/CELLMARK0/USRDIR/cellmark.log on every mode change, every memtest pass, and on exit. Useful for long stability runs where you want a paper trail of what passed before something hung.

Building from source

python tools/build.py retail      # build/cellmark.elf + cellmark.self (CFW)
python tools/build.py decr        # build/cellmark_decr.elf + .self (ProDG/Target Manager)
python tools/build.py clean       # remove build/
python tools/build.py spu_fft     # rebuild just one SPU bench (faster iter)

python make_pkg.py --variant retail   # build/cellmark.pkg (NPDRM-signed)
python make_pkg.py --variant decr     # build/cellmark_decr.pkg

Requires the Cell SDK 3.0 toolchain at C:\usr\local\cell (or override via CELL_SDK env var). Python 3.7+ for the build driver.

Adding a benchmark

python tools/new_bench.py raytracer --category workload
# scaffolds benches/raytracer/ from _template/, prints registration steps

See docs/adding_a_benchmark.md for the full guide.


Compatibility

Console PS3 with CFW/HEN (retail or DEX)
Firmware Tested on Evilnat 4.92. Should work on any modern CFW.
Best on All. Though if you are chasing OC numbers, pre CECH-2000 systems are the best, but there are exceptions even among 2000 series (same motherboard revision, diferent part number or manufacturer)

Different Cell process nodes (90 nm / 65 nm / 45 nm) hit different stability ceilings, and most non-trivial overclocks require overvolting. Use at your own risk! See cell-xdr-overclocking for the hardware side.


Why this exists

This was built alongside cell-xdr-overclocking as a way to verify CELL/XDR overclock stability and measure how much performance an OC actually unlocks. The compute kernels tell you whether the SPEs are doing math correctly at the new clock; the memtest catches XDR errors that only appear under sustained load; the disk page exists because the stock PS3 disk benchmarks were terrible and an SSHD upgrade turned out to be bottlenecked entirely on the OS, not the drive.

If you find a configuration that fails here that worked in something else, open an issue! That's exactly the data this was built to surface.


Credits and sources


License

MIT License


Author

sagemono - design, implementation, and the days lost to figuring out why cellFsRead was returning 9 MB/s.

About

A stress test and benchmark suite for the PlayStation 3, focused on validating Cell BE / XDR overclocks and characterising disk I/O

Resources

Stars

15 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages