CLI tool
AhmadHassan-BTed/ThreadRace avatar
AhmadHassan-BTed/ThreadRace

ThreadRace: a C++ harness that times sequential work against threaded work

Quantifying the pulse of parallelism: A C++ concurrency telemetry framework measuring the human-perceived efficiency of concurrent execution strategies.

2,651 stars0 forksC++MIT

At a glance

What is it?
ThreadRace is a C++ concurrency telemetry framework that runs the Ackermann function sequentially and in parallel, times both, and can visualise the recursion with an SDL2 and OpenGL suite. It suits C++ developers who want to see thread coordination costs, and its headline benchmark is a deliberately punishing workload.
Who is it for?
Adopt ThreadRace as a teaching and instrumentation framework for C++ concurrency, because the strategy pattern separation makes a new execution strategy a single class, and the visualizer makes thread coordination visible in a way a millisecond figure does not.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 77 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Ackermann as the benchmark workload, and why that matters

ThreadRace is a C++ concurrency telemetry framework whose stated purpose is to measure, analyze and visualise the performance difference between sequential and parallel execution paradigms.

The workload is the Ackermann function, a deeply recursive mathematical operation, used as a stand-in for high-complexity, processor-intensive work that demands thread synchronisation and compute optimisation. The comparison is between a Typical sequential execution strategy and a Threaded parallel one, so the framework produces a speedup figure for the same task run two ways.

There is a methodological point here that deserves to be said plainly, because it determines how you read any number the tool prints. The Ackermann function's cost grows in a way that is a property of the function, not of the problem domain. It is a useful stressor for thread coordination, since it is recursive and its call tree is deep, but a speedup measured on it does not transfer to an image pipeline or a request handler. The framework is measuring the machinery, and it does that well, but the workload is synthetic by design.

The AckermannTask class holds the two parameters m and n, implements execute() and getName(), and keeps the recursion in a private ackermann(m, n) method. The framework is therefore parameterised in a way that lets you run the same shape of work at different depths.

Strategy pattern, two implementations, one benchmarker

The class diagram in the README shows a small, conventional object-oriented design, and it is worth reading because the separation is what makes the framework extensible.

ITask is an interface with execute() and getName() returning a string. IExecutionStrategy is the second interface, with run(ITask, iterations) and getStrategyName() returning a string. AckermannTask implements the task interface. SequentialStrategy and ThreadedStrategy each implement the strategy interface, and Benchmarker holds an IExecutionStrategy and exposes runBenchmark(ITask, iterations).

The consequence is direct. Adding a third execution model, a thread pool with a work queue, or a strategy that splits the input differently, means writing one class that satisfies IExecutionStrategy and handing it to the Benchmarker. The benchmarker does not know which strategy it holds, and the task does not know how it will be run. That is the strategy pattern doing its job in a place where it earns its keep, since the whole point of the tool is comparing strategies.

The README describes the codebase as using clean object-oriented patterns that isolate telemetry capture, computational task definitions, execution strategies and visual rendering modules, so the visualiser is a separate concern from the timing core, and the benchmark can be built and used without it.

Two binaries, and the visualizer needs SDL2 and OpenGL

The Makefile builds two targets from the same source tree, and the split is worth understanding before you build.

The core benchmark target is time_tester, built from main.cpp, Benchmarker.cpp, AckermannTask.cpp, SequentialStrategy.cpp and ThreadedStrategy.cpp. The visualizer target is ackermann_visualizer, and its source list is much longer: visualizer_main.cpp, AckermannStackEngine.cpp, AudioSynthesizer.cpp, QuantumTreeRenderer.cpp, Landscape3DRenderer.cpp, SpiralOrbitRenderer.cpp, RaceEngine.cpp, RaceArenaRenderer.cpp and FontRenderer.cpp.

The compiler settings are fixed in the Makefile: g++ with -std=c++11, -g, -O2, -Wall and -Wextra, an include path of -Iinclude, and -pthread. The visualizer adds -lSDL2 -lGL -pthread on top of that. So building the visualizer needs SDL2 and OpenGL development libraries present on the machine, and the core timing tool does not.

The README gives the build and run command for the native suite:

bash
make all
./bin/ackermann_visualizer [m] [n]

The m and n arguments are the Ackermann parameters, so the same binary renders different recursion depths. The Makefile also handles Windows, switching the target names to .exe, using a PowerShell one-liner to create directories and del for removal, so the same source builds on both platforms.

The project is marked as C++11 and production ready, licensed MIT, with a build badge, and the last push to the main branch was on 2026-08-07.

Four render modes, one of which is the actual race

The visualizer suite has four modes, and the fourth is the one that answers the framework's own question.

Mode 1 is a Quantum Branching Network, an interactive trace of the recursive call stack in 2D and 3D, with glowing OpenGL nodes, Bezier branch connectors, starburst particles fired on base-case evaluations and camera tracking that follows the active branch.

Mode 2 is a Hyper-Logarithmic 3D Landscape, a terrain height field that renders the growth of the function using the compression Z(m, n) = ln(1 + ln(1 + A(m, n))). That double logarithm is what makes the surface renderable at all, since the raw values overflow the practical range of a height field almost immediately.

Mode 3 is a Golden Spiral Phase Orbit, which maps call stack depth and frame steps onto a particle galaxy arranged along a logarithmic golden ratio orbit, with glowing trails and depth rings.

Mode 4 is the Head-to-Head Thread Race Arena, a side-by-side competition between sequential execution on one thread and parallel execution, showing active thread count, memory footprint, speedup telemetry, step progress bars and finish line detection. This is the mode that connects the visual work to the benchmark output.

Two more features apply across the modes. There is polyphonic audio sonification, an SDL2 audio callback that synthesises C major pentatonic harmonic notes as the recursive stacks push and resolve, and a HUD overlay with live parameter feedback, stack depth counters, call count statistics and a key legend.

The control scheme is extensive: number keys switch modes, space and P play and pause, S single-steps, R resets, bracket keys adjust m, minus and plus adjust n, left and right change the step delay in milliseconds, I and O zoom between 0.01x and 20x, M toggles audio, H toggles the HUD, and escape or Q quits.

A framework with two audiences and one benchmark shape

The clearest way to judge ThreadRace is to separate the two things it does, because they serve different people and have different weaknesses.

The timing core is the part with lasting value for a C++ developer. It gives you a task interface, two execution strategies, a benchmarker and precise timing telemetry, and it asks almost nothing of you beyond a compiler. If you want to measure whether your own workload benefits from threading, you implement ITask and IExecutionStrategy, plug them into the Benchmarker, and get a comparison in the same shape the framework produces for the built in case.

The visualizer is the more impressive part and the more situational one. It needs SDL2 and OpenGL, it renders at 60 FPS, and it makes a recursive call tree legible in a way a spreadsheet of milliseconds is not. For teaching, for a conference talk, or for understanding where time actually goes in a deep recursion, that is real value.

The limitation is that both are welded to the Ackermann function. The visualizer's modes 2 and 3 are mathematical renderings of the function's growth and its stack depth, and the race arena compares the two built in strategies. Making them show your workload means reworking the stack engine, the renderers and the metrics, not just swapping a task class.

There is also a maintenance reality to note. The repository has no GitHub releases, so the last push on 2026-08-07 is the only freshness signal, and the project carries the usual set of community files, CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md, an.editorconfig and a docs/ directory, but there is no changelog to tell you what changed between revisions.

Editorial conclusion

Adopt ThreadRace as a teaching and instrumentation framework for C++ concurrency, because the strategy pattern separation makes a new execution strategy a single class, and the visualizer makes thread coordination visible in a way a millisecond figure does not. Do not adopt it as a general performance benchmark suite, since the workload is the Ackermann function, whose growth is a property of mathematics rather than of your data, and the numbers it produces describe that function and not your application. Before you draw conclusions from a run, check the compiler flags, since the Makefile builds with -O2 and -std=c++11, and remember the visualizer needs SDL2 and OpenGL headers and libraries that the core benchmark does not.

Frequently asked questions

How do I build and run the ThreadRace visualizer?

Run `make all` and then start the binary with `./bin/ackermann_visualizer [m] [n]`, where m and n are the Ackermann parameters. The build uses g++ with -std=c++11 -g -O2 -Wall -Wextra, and the visualizer additionally links -lSDL2 -lGL -pthread, so SDL2 and OpenGL development libraries are required.

What does ThreadRace measure?

It compares Typical sequential execution against Threaded parallel execution of the same task, with the Ackermann function as the built in benchmark workload. The timing is produced by the Benchmarker, which holds an IExecutionStrategy and runs it against an ITask for a number of iterations.

How do I add my own execution strategy to ThreadRace?

Implement the IExecutionStrategy interface, which requires run(ITask, iterations) and getStrategyName() returning a string, then hand the implementation to the Benchmarker, which stores it as an IExecutionStrategy. The task interface ITask requires execute() and getName().

What are the ThreadRace visualizer render modes?

Four: a Quantum Branching Network showing the recursive call tree, a Hyper-Logarithmic 3D Landscape rendering the function's growth as a terrain height field, a Golden Spiral Phase Orbit mapping stack depth onto particles, and a Head-to-Head Thread Race Arena comparing sequential against parallel execution with speedup telemetry.

Official sources

  1. AhmadHassan-BTed/ThreadRace on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ahmadhassan-bted-threadrace.svg)](https://hysenlabs.com/projects/ahmadhassan-bted-threadrace)

Was this useful?