toyBrot is a primarily a tool through which I study and compare different parallelisation techniques/paradigms/languages.
Nominally it produces a Mandelbox fractal image through a technique called Raymarching. As a piece of code, this is extremely friendly to parallelisation. The output image is calculated pixel per pixel, with each one being independent of any others.
Raymarching is convenient in that there’s no inter-task dependency and no memory constraints, as each task writes to a separate well defined address. It is a “spherical cow in perfect vacuum” type situation for parallelisation.
The way I use toyBrot, then, is by implementing the same code, which I take care to maintain as stable as is reasonable (with no changes in the algorithm itself and no specific optimisations) using different technologies. This allows me to compare not only how they perform (by, say, measuring how the same nVidia video card behaves when running the same code with Vulkan, openGL, CUDA or OpenCL) but also how easy or difficult each of these are to work with, to find resources for, etc…
This process serves as the foundation for a long running series in this blog, “Multi Your Threads” in which I discuss those experiences and look at how that performance compares to previous implementations.
All the code is publicly hosted in Gitlab, together with my other projects
Project Status
toyBrot is currently “Semi-Active”. While right now I don’t have any specific new regular C++ toyBrot implementations to work with, I have been bringing it into the context of Godot. My goal there is much the same, compare how it handles the sort of highly parallel workload that I use toyBrot for, if it imposes performance penalties compared to the raw C++ implementations, that sort of thing.
Recently I’ve used Godot to make an interactive version of toyBrot, which you can find here.
Outside of Godot, at time of writing there aren’t any new toyBrot implementations I have been thinking of, but there ARE some additional features and potentially a big refactor that I’ve long toyed with to avoid having a main.cpp. for each project which is mostly the same anyways, with only a few necessary differences.
This is all somewhat lower priority compared to other things in the pile but as a tool to work with those other things, toyBrot remains invaluable. The interactive toyBrot I mentioned above was also a tool to interact with Godot’s GUI system. As such, while I’m not sure when I’ll be back to working directly on toyBrot, it’s likely to get pulled in as a means to examine and probe other things.
Legacy toyBrot
Originally toyBrot rendered a regular Mandelbrot 2D fractal and many of the original implementations were built for that. At one point, though, that was just not demanding enough to be useful as any sort of benchmark, at which point it was changed to the current Raymarching implementation. The old code is still available in the mandelbrot folder but it is no longer maintained.
Not all implementations made it through the update. HC C++ was dropped entirely as that technology was discontinued by AMD once they decided to put all their eggs on HIP instead. Additionally, at time of writing, I haven’t migrated any sort of MPI` enabled implementation, mostly since I’ve been distant from that sort of environment.
Dependencies and implementations
In order to verify your output for correctness (or if you want to save your image and use it as a background or wallpaper) there are two main optional dependencies which are shared for all implementations
SDL2which, if found will allow the application to display its results in a window. For CPU-based implementations, this can happen progressively as the fractal is calculatedlibPNGto enable the generated image to be saved as a png file
CMake will look for those libraries and, if they’re found, enable these functionalities for every application built. They are independent and, if you’re deploying to a headless environment, you can, say, just have
libPNG so you can later quickly eyeball to see if things got generated correctly.
As mentioned, the most basic implementations of toyBrot only require C++. If nothing else is found, you’ll always have the implementations which use
std::threads and std::launch.
Other dependencies that toyBrot will look for and, if found, enable additional implementations include:
- OpenMP
- OpenCL
- CUDA
- If found, you can optionally build CUDA with
clangas well as withnvcc
- If found, you can optionally build CUDA with
- HIP
- SYCL
- OpenGL
- OpenGL has implementations which use compute shaders as well as using a fragment shader as if it was a compute shader
- Intel oneTBB
- Intel ISPC
- There’s an additional target combining these if they’re both found
- You can build with different extension sets supported by ISPC to, say, compare the peformance between AVX2 and SSL4 for your particular target hardware
- Vulkan
- if
clspvis found, it can also be used by the generated application
- if
- Emscripten
- If emscripten is detected as the compiler, there are a CPU and GPU projects for WebAssembly. You can find live version of both in here. The CPU one in particular can be quite intense on resources
For up-to-date information, check the official repository