|
pure-cpp 1.0.0
A C++ physics simulation benchmark comparing performance with Python implementations
|
This project aims at benchmarking the impact of using Python when doing some time-consuming calculations and using a framework.
Documentation specific to this code is available on-line.
This project serves several key purposes:
Before you begin, ensure you have the following installed:
If you are not using Conan, you will need to install the following dependencies via your system's package manager:
program-options)Core, Gui, Widgets, Quick, QuickWidgets, 3DCore, 3DExtras, 3DRender, LinguistTools, Concurrent)Note for VS Code Users: The recommended C/C++ and CMake Tools extensions provide a seamless experience and are highly recommended.
This project uses a modern CMake setup with presets to ensure a consistent and straightforward build process across different platforms.
The easiest way to configure the project is to use the provided helper scripts located in the utils/ directory. They will create the correct build directory and run CMake with the appropriate preset for you.
On Linux, macOS, or other Unix-like systems:
Use the setup-build.sh script. You can see all options by running it with --help.
On Windows:
Use the setup-build.ps1 PowerShell script. You can see all options by running Get-Help .\utils\setup-build.ps1 -Full.
After configuration, you can build the project using CMake:
If you prefer not to use the helper scripts, you can configure the project manually using CMake Presets.
List available presets:
**(Optional) Use Conan for dependencies:** If you choose a conan-* preset, you must first run conan install.
Configure with CMake:
Build the project:
The project is fully configured for a seamless experience in VS Code:
CMakePresets.json file.dev-gcc-ninja).CMake: Build** command.F5**. The launch.json is pre-configured to work with CMake Tools.To update the translation source (.ts) files after making changes to the UI or source code, build the lupdate target:
To add a new language (e.g., German), follow these steps:
Generate the new .ts file: From your build directory, run the following command:
This will create an empty pure-cpp/locales/pure-cpp_de.ts file.
Populate the new file with strings: Run the lupdate target again to scan your source code and fill the new file with translatable strings.
pure-cpp_de.ts in Qt Linguist, fill in the translations, and save the file.The build system provides several additional targets for common tasks:
To generate the documentation with Doxygen, build the doxygen target:
The output will be in build/<preset-name>/doc/html/. You can enable other formats (e.g., LaTeX) by setting CMake options like -DGEN_LATEX=ON during the configuration step.
To install the compiled binary and any associated resources, build the install target. This may require administrator privileges.
You can create a distributable package (e.g., TGZ, ZIP) using CPack after building the project.
To see all available command-line options and their descriptions, run the executable with the --help flag:
The project includes a comprehensive test suite using Google Test. To run all tests:
To run tests with CTest (CMake's test runner):
To run a specific test:
To run tests with coverage reporting (requires coverage flags enabled during build):
The application includes a flexible logging system to monitor its behaviour and diagnose issues. You can control the log output and verbosity using the following command-line arguments:
-l, --log-level {debug,info,warning,critical,fatal}: Sets the logging verbosity. Higher levels include lower ones (e.g., info will also show warning, critical, and fatal messages). The default level is critical.-c, --debug-console: Prints log messages to the standard output (your terminal).-f, --debug-file <path/to/logfile.log>: Writes log messages to the specified file.-s, --debug-syslog <identifier>: Sends log messages to the system's Syslog service, using the given identifier (Unix-like systems only).You can combine these options. For example, to see all debug messages on the console and also save them to a file, you would use:
The simulation is built on a modular, multi-threaded architecture designed to separate the physics calculations from the user interface, ensuring a responsive application.
main.cpp (Entry Point)**: Orchestrates the application lifecycle. It initializes the QApplication, parses command-line arguments (using cmd_line_parser.hpp), sets up logging (logger.hpp), and creates the MainWindow.display.hpp/cpp (View)**: Manages the Qt3DWindow and all visual aspects of the scene. It is responsible for setting up the camera and lighting, and for creating and updating the visual representation of each body. It owns and manages the PhysicsWorker thread.physics_worker.cpp (Controller)**: Acts as a bridge between the UI and the physics engine. It runs in a separate QThread and owns the Space object. Its primary role is to trigger physics steps and emit signals with the updated data.space.hpp/cpp (Physics Model)**: The core physics engine. It is completely decoupled from Qt and the UI. It manages all physical state and contains the main computeDynamics loop, which handles integration, force calculation, and collision detection/response.body.hpp (Data Structure)**: Implements a high-performance Structure-of-Arrays (SoA) data layout with the Bodies class. This ensures that data for physics calculations (e.g., all positions, all velocities) is stored contiguously in memory for cache efficiency and vectorization. The BodyProxy class provides a convenient Array-of-Structures (AoS)-like interface for easy access to individual body data.The application uses a signal-and-slot mechanism to create a non-blocking simulation loop, which keeps the UI responsive at all times.
Display class moves a PhysicsWorker instance to a separate QThread and starts the thread.PhysicsWorker calls space.computeDynamics() to run one full step of the simulation. This is a blocking call that runs entirely in the physics thread.PhysicsWorker emits an updatedBodyData signal, passing the new positions and orientations as raw data for efficiency.Display class's updateFrame slot, which runs in the main UI thread, receives this signal. It iterates through the visual objects and updates their 3D transforms using optimised in-place updates to avoid object allocations.updateFrame uses QTimer::singleShot(0, ...) to schedule the next call to the PhysicsWorker's performSingleStep slot. Using a zero-delay timer posts an event to the Qt event loop, which will be processed as soon as the UI thread is idle. This prevents the physics steps from queuing up faster than the UI can render them, ensuring a smooth and stable frame rate.This architecture effectively decouples the rendering rate from the simulation rate. The physics engine can run as fast as possible in its own thread, while the UI updates at a smooth, consistent pace.
This project employs several key strategies to achieve high performance in a C++ environment.
body.hpp module uses a Structure-of-Arrays (SoA) layout. Instead of a list of body objects each holding their own data (AoS), all positions, velocities, and masses are stored in contiguous std::vector containers. This design is highly cache-friendly and enables effective vectorization and parallelization.nanoflann::KDTree) is used for a "broad-phase" check to quickly discard pairs of bodies that are too far apart to collide. Only the remaining candidate pairs are passed to a more precise, but more expensive, "narrow-phase" check.dt) based on the maximum acceleration, velocity, angular velocity, and angular acceleration in the system. This improves both performance and stability by allowing for larger, faster steps when bodies are far apart and smaller, more precise steps during close encounters or collisions.This programme is free software; you can redistribute it and/or modify it under the terms of the GNU General Public Licence as published by the Free Software Foundation; either version 3 of the licence, or (at your option) any later version. See the file named “LICENSE.txt” or on-line.
POSIX is a registered trademark owned by the Open Group.
Linux is a registered trademark owned by Torvalds, Linus.
macOS and Xcode are registered trademarks owned by Apple.
Microsoft Windows and Visual studio are registered trademarks owned by Microsoft.
Oracle Solaris is a registered trademark owned by Oracle Corporation.
Copyright © 2024 – 2025 Le Bars, Yoann.