C++. From Art to Analytics (part IV)

Deconstructed

Welcome to the 4th chapter of the Deconstructed series. You can find the previous ones here: part 1, part 2 and part 3.

Back to C++ 30 years later

After the bittersweet experience of Mojo I chose to move to good old C++. I had developed my dissertation thesis (1994) in Borland C++, a pioneering project in Windows 3.1. Some nostalgic people in the audience may sympathise, for the new ones it may seem like the stone age. In the early 1990s, C++ was still maturing—templates were just emerging, exceptions were awkward, and the Standard Library was minimal. The first official standard, C++98, didn’t arrive until 1998 and many have followed since then.

Over the past three decades, C++ has transformed dramatically from a relatively simple extension of C into a complex, feature-rich language that powers everything from operating systems to game engines: smart pointers for memory safety, lambdas for functional programming and a great standard library. I could use some LLM code assistants to help me porting the code, but as I mentioned in a post today, they help very little on the creative part or trying to optimize for speed (what I cared most !!).

One of the reasons to choose C++ was performance and availability. Performance remains C++’s hallmark, keeping it a favorite for applications where speed is non-negotiable. Thirty years ago, its ability to provide low-level control over hardware—often dubbed “zero-cost abstractions”—set it apart, and that hasn’t changed. I started developing it with the free version of Visual Studio from Microsoft, that I knew relatively well and has a nice debugging environment. A huge leap compared to Mojo.

Debugging Matrices and Visualizing data: Still a Challenge

Debugging in C++ has come a long way, but arrays and matrices remain a stubborn pain point. Back in the ’90s, developers relied on crude tools—think printf statements and basic debuggers that barely understood the language. Today’s debuggers, integrated into IDEs, can inspect complex types, visualize matrix contents, and trace execution with precision. Yet, when compared to Matlab or Python (numpy) inspecting matrices, especially multi-dimensional ones is very rudimentary unless the data is an image. If you want to generate a rich graph it is going to be hard too. I resorted to a matplotlib port just to give you an idea.

If anyone in the audience knows a better way, let me know !

CMake and VS Code: Modern Wonders

The rise of CMake and Visual Studio Code (VS Code) has revolutionized C++ development, making it more accessible than ever, therefore I decided mid-way to make (no pun intended) a move to CMake. CMake, now the standard build system, emerged as a cross-platform savior, allowing me to jump between Windows and Linux (thanks to another wonder: windows subsystem Linux) and develope it with a single configuration. It’s not always intuitive—setting it up can feel like a puzzle—but its flexibility and integration with other tools are unmatched. Meanwhile, VS Code, originally a lightweight editor, has morphed into a C++ powerhouse with extensions for code completion, debugging, and many tools.

What about peformance ?

Oh yes, performance … we wanted to optimize my deconstruction code. Once I got all up and running I started to see the challenges of identifying bottlenecks on iteration cycles of 500-800 ns. I had hit a similar barrier below the 1 µs range. How to identify bottlenecks?

Here I would like to introduce two more wonders:

VTune is a product by Intel, freely available that will help you diagnose bottlenecks on complicated environments. The environment now, with many processors and a rich operative system is complex ! Tracy is an open-source wonder, very lightweight and featured to benchmark applications in the nanosecod regime. It introduces a low overhead and you can even do remote benchmarking. It only became important in the last chapter but I think is worth to introduce them together.

My initial analysis was that with my code design I could not squeeze more efficiency, the code was very memory intensive and I started to think that I should try what I always thought would not work: multi-threading. This was always a very difficult problem to parallelize: you choose randomly two pixels of the image and evaluate the change in a simulated annealing algorithm. You need to either detect or avoid collisions (two threads working on the very same pixel).

That would be the next and final chapter :)