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.
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.
Eleven pages, switched with L2/R2 on a controller. L1/R1 cycle the variant within a page.
| 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.
- VMX FMA -
vmaddfpthroughput, 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;addat 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
vaddfpis 12-cycle latency and the original 4 throttled the loads that fed them; L2 prefetches 1.5 KB ahead withdcbt. A scalar 8 Bldtwin 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/stdstreams +dcbz128line 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)
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.
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.
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.
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/putllcping-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.
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).
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).
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.
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.
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.
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.
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).
The compiled package is on the Releases page.
- Transfer
cellmark.pkgto the PS3 (FTP, USB,/dev_hdd0/packages/, etc.) - 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.
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.pkgRequires 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.
python tools/new_bench.py raytracer --category workload
# scaffolds benches/raytracer/ from _template/, prints registration stepsSee docs/adding_a_benchmark.md for the full guide.
| 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.
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.
- Catherine H. Crawford, Paul Henning, Michael Kistler, Cornell Wright - Accelerating Computing with the Cell Broadband Engine Processor
- Michael Kistler, Michael Perrone, Fabrizio Petrini - CELL MULTIPROCESSOR COMMUNICATION NETWORK: BUILT FOR SPEED
- David A. Bader, Virat Agarwal, Seunghwa Kang - Computing discrete transforms on the Cell Broadband Engine
- Farshad Khunjush, Nikitas J. Dimopoulos - Extended Characterization of DMA Transfers on the Cell BE Processor
- David A. Bader, Virat Agarwal - FFTC: Fastest Fourier Transform for the IBM Cell Broadband Engine and source code
- David A. Bader , Virat Agarwal, Kamesh Madduri, Seunghwa Kang - High performance combinatorial algorithm design on the Cell Broadband Engine processor
- David A. Bader, Sulabh Patel - High Performance MPEG-2 Software Decoder on the Cell Broadband Engine and source code
- Olaf Lubeck, Michael Lang, Ram Srinivasan, Greg Johnson - Implementation and performance modeling of deterministic particle transport (Sweep3D) on the IBM Cell/B.E.
- Jakub Kurzak, Jack Dongarra - Implementation of the Mixed-Precision High Performance LINPACK Benchmark on the CELL Processor
- Arnd Bergmann - Linux on Cell Broadband Engine status update
- Daniel A. Brokenshire - Maximizing the power of the Cell Broadband Engine processor: 25 tips to optimal application performance
- David A. Bader, Seunghwa Kang - Optimizing JPEG2000 Still Image Encoding on the Cell Broadband Engine and source code
- Daniel Jiménez-González Xavier Martorell, Alex Ramírez - Performance Analysis of Cell Broadband Engine for High Memory Bandwidth Applications
- Luke Cico, Robert Cooper, Jon Greene, Michael Pepe - Performance Benchmarks and Programmability of the IBM/Sony/Toshiba Cell Broadband Engine Processor
- Jacob Johnson - POWER EFFICIENCY AND SCALING OF THE CELL BROADBAND ENGINE
- Hauser, Jochem H., Cambier Jean-Luc, Surampudi Surya, Gollnick Torsten - Programming the IBM Cell Broadband Engine a general parallelization strategy
- Filip Blagojevic, Alexandros Stamatakis, Christos D. Antonopoulos, Dimitrios S. Nikolopoulos - RAxML-Cell: Parallel Phylogenetic Tree Inference on the Cell Broadband Engine
- Pieter Bellens, Josep M. Perez, Felipe Cabarcas, Alex Ramirez, Rosa M. Badia, Jesus Labarta - CellSs: Scheduling techniques to better exploit memory hierarchy
- Michael Gschwind, Fred G. Gustavson, Jan Prins - High Performance Computing with the Cell Broadband Engine
- Michael Kistler, John Gunnels, Daniel Brokenshire, Brad Benton - Programming the Linpack benchmark for the IBM PowerXCell 8i processor
- B.C. Vishwas, Abhishek Gadia and Mainak Chaudhuri - Implementing a parallel matrix factorization library on the cell broadband engine
- Michael Gschwind - The Cell Broadband Engine: Exploiting Multiple Levels of Parallelism in a Chip Multiprocessor
- Samuel Williams, John Shalf, Leonid Oliker, Shoaib Kamil, Parry Husbands, Katherine Yelick - The potential of the cell processor for scientific computing
- Sándor Héman, Niels Nes, Marcin Zukowski, Peter Boncz - Vectorized Data Processing on the Cell Broadband Engine
- Nascar1243, villahed94, gypsy, RGBeter, NGX, DoublesAdvocate - Testing, feedback and moral support.
- Sony / IBM / Toshiba - Cell Broadband Engine Programming Handbook (v1.1, April 2007) and the SPU instruction timing reference; the PPE VMX latencies in
ppe_benchmarks.ccome from there. - memtest86+ - The test methodology used in the
spu_memtest.cfollows the standard pattern set: Walking 1/0, Checkerboard, Moving Inversions, Bank Hammer, Bit Fade, etc. The patterns themselves are decades-old academic memory-testing techniques; this is an independent SPU/DMA implementation of them, but credit where it's due. - The PS3 developer community -
ps3py,scetool, and decades of patient reverse engineering.
MIT License
sagemono - design, implementation, and the days lost to figuring out why cellFsRead was returning 9 MB/s.
