-
Notifications
You must be signed in to change notification settings - Fork 5
Add design spec for C++17 migration #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
joshbainbridge
wants to merge
1
commit into
main
Choose a base branch
from
cpp-17-migration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,269 @@ | ||
| # Migrate project to C++17 standard and reduce complexity | ||
|
|
||
| GitHub Issue: https://github.com/AcademySoftwareFoundation/openqmc/issues/86 | ||
|
|
||
| ## Summary | ||
|
|
||
| Migrate OpenQMC from C++14 to C++17 to eliminate the binary/header-only | ||
| install split and reduce build system complexity. C++17 `inline` variables | ||
| remove the need for the `OQMC_ENABLE_BINARY` mechanism entirely, allowing the | ||
| library to remain header-only without duplicating large static arrays across | ||
| compilation units. | ||
|
|
||
| ## Motivation | ||
|
|
||
| OpenQMC includes six large blue noise lookup tables (key and rank tables for | ||
| PMJ, Sobol, and Lattice samplers) defined in `include/oqmc/bntables.h`. Each | ||
| table is 2^16 entries of `uint32_t`, totalling roughly 1.5 MB of data across | ||
| all six. | ||
|
|
||
| In C++14, `constexpr` namespace-scope variables have internal linkage. When | ||
| `bntables.h` is included from multiple translation units, each unit gets its | ||
| own copy of the table data in the final binary. To avoid this, the project | ||
| offers a binary variant controlled by the `OPENQMC_ENABLE_BINARY` CMake | ||
| option. When enabled, the tables are declared `extern` in the header and | ||
| defined once in `src/bntables.cpp`, which is compiled into a static or shared | ||
| library. | ||
|
|
||
| This creates user-facing complexity: | ||
|
|
||
| - Three CMake options (`OPENQMC_ENABLE_BINARY`, `OPENQMC_SHARED_LIB`, | ||
| `OPENQMC_FORCE_PIC`) that exist solely for this purpose. | ||
| - Conditional `INTERFACE` vs `STATIC`/`SHARED` library target logic in | ||
| `src/CMakeLists.txt`. | ||
| - A preprocessor split (`#if defined(OQMC_ENABLE_BINARY)`) in `bntables.h`. | ||
| - Documentation explaining the trade-off between header-only and binary | ||
| installs. | ||
|
|
||
| C++17 introduces `inline` variable declarations. An `inline constexpr` | ||
| namespace-scope variable has external linkage and is guaranteed to have a | ||
| single definition across all translation units. This directly solves the | ||
| duplication problem with no build system workaround required. | ||
|
|
||
| ## Changes — Headers | ||
|
|
||
| Replace the conditional compilation in `include/oqmc/bntables.h`. The current | ||
| pattern for each sampler namespace (repeated for `pmj`, `sobol`, `lattice`): | ||
|
|
||
| ```cpp | ||
| #if defined(OQMC_ENABLE_BINARY) | ||
| extern const std::uint32_t keyTable[size]; | ||
| extern const std::uint32_t rankTable[size]; | ||
| #else | ||
| constexpr std::uint32_t keyTable[] = { | ||
| #include "data/pmj/keys.txt" | ||
| }; | ||
| constexpr std::uint32_t rankTable[] = { | ||
| #include "data/pmj/ranks.txt" | ||
| }; | ||
| #endif | ||
| ``` | ||
|
|
||
| Becomes: | ||
|
|
||
| ```cpp | ||
| inline constexpr std::uint32_t keyTable[] = { | ||
| #include "data/pmj/keys.txt" | ||
| }; | ||
| inline constexpr std::uint32_t rankTable[] = { | ||
| #include "data/pmj/ranks.txt" | ||
| }; | ||
| ``` | ||
|
|
||
| The `#if defined(OQMC_ENABLE_BINARY)` blocks and corresponding `#else` / | ||
| `#endif` are removed entirely. | ||
|
|
||
| ## Changes — Source code | ||
|
|
||
| ### `src/bntables.cpp` | ||
|
|
||
| Delete this file. It only exists to provide `extern` definitions for the | ||
| binary variant. | ||
|
|
||
| ## Changes — Build system | ||
|
|
||
| ### `CMakeLists.txt` (root) | ||
|
|
||
| Remove the following options: | ||
|
|
||
| ```cmake | ||
| option(OPENQMC_ENABLE_BINARY "Build binary to reduce memory cost.") | ||
| option(OPENQMC_SHARED_LIB "Make a shared library, in place of static.") | ||
| option(OPENQMC_FORCE_PIC "Force PIC for static libraries.") | ||
| ``` | ||
|
|
||
| ### `src/CMakeLists.txt` | ||
|
|
||
| Replace the conditional library creation: | ||
|
|
||
| ```cmake | ||
| if(OPENQMC_ENABLE_BINARY) | ||
| if(OPENQMC_SHARED_LIB) | ||
| add_library(${PROJECT_NAME} SHARED) | ||
| else() | ||
| add_library(${PROJECT_NAME} STATIC) | ||
| endif() | ||
|
|
||
| if(OPENQMC_FORCE_PIC) | ||
| set_target_properties(${PROJECT_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) | ||
| endif() | ||
|
|
||
| target_sources(${PROJECT_NAME} PRIVATE bntables.cpp) | ||
| target_compile_options(${PROJECT_NAME} PRIVATE ${OPENQMC_CXX_FLAGS}) | ||
| target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include) | ||
| else() | ||
| add_library(${PROJECT_NAME} INTERFACE) | ||
| endif() | ||
| ``` | ||
|
|
||
| With: | ||
|
|
||
| ```cmake | ||
| add_library(${PROJECT_NAME} INTERFACE) | ||
| ``` | ||
|
|
||
| Remove the `OQMC_ENABLE_BINARY` compile definition block: | ||
|
|
||
| ```cmake | ||
| if(OPENQMC_ENABLE_BINARY) | ||
| target_compile_definitions(${PROJECT_NAME} INTERFACE OQMC_ENABLE_BINARY) | ||
| endif() | ||
| ``` | ||
|
|
||
| Change the C++ standard requirement: | ||
|
|
||
| ```cmake | ||
| # Before | ||
| target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_14) | ||
|
|
||
| # After | ||
| target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_17) | ||
| ``` | ||
|
|
||
| ### `CMakePresets.json` | ||
|
|
||
| The `unix` preset sets `CMAKE_CXX_STANDARD` to `"14"` (line 18). Update to | ||
| `"17"`. | ||
|
|
||
| ### `cmake/examples/` | ||
|
|
||
| Update `cxx_std_14` to `cxx_std_17` in the manual include example. | ||
|
|
||
| ## Changes — Documentation | ||
|
|
||
| ### `README.md` | ||
|
|
||
| - Change "This C++14 (CPU, GPU) header only library" to reference C++17. | ||
| - Update the requirements section: change "no dependencies other than C++14" | ||
| to C++17 and adjust VFX Reference Platform compatibility to CY2021. | ||
| - Remove or update references to the binary install variant and its CMake | ||
| options throughout the document. | ||
|
|
||
| Update minimum tested compiler versions: | ||
|
|
||
| - **GCC**: GCC 7 is the minimum requirement for C++17. No change needed. | ||
| - **Clang**: Clang 5 is the minimum requirement for C++17. No change needed. | ||
| - **NVCC**: Minimum version moves from NVCC 10 to NVCC 11. Update tested | ||
| compiler documentation and CI matrix accordingly. | ||
|
|
||
| ### `mkdocs/home.html` | ||
|
|
||
| - Update "Header-only C++14 library" and "CY2018" references on line 77. | ||
|
|
||
| ## Migration risks | ||
|
|
||
| - **Downstream breakage**: Projects using `OPENQMC_ENABLE_BINARY`, | ||
| `OPENQMC_SHARED_LIB`, or `OPENQMC_FORCE_PIC` will see CMake errors on | ||
| upgrade. This is an intentional breaking change. A CHANGELOG entry and | ||
| migration note should accompany the release. | ||
| - **VFX Reference Platform**: This migration moves the minimum from CY2018 | ||
| to CY2021. | ||
|
|
||
| ## Candidate C++17 improvements | ||
|
|
||
| Beyond the core `inline constexpr` change, the following are candidate | ||
| improvements enabled by C++17 that could be included in this migration or | ||
| follow-up work. | ||
|
|
||
| ### Hex float literals | ||
|
|
||
| `include/oqmc/float.h` line 34: | ||
|
|
||
| ```cpp | ||
| // Before | ||
| constexpr auto floatOneOverTwoPower32 = 1.0f / (1ull << 32); ///< 0x1p-32 | ||
|
|
||
| // After | ||
| constexpr auto floatOneOverTwoPower32 = 0x1p-32f; | ||
| ``` | ||
|
|
||
| Hex float literals (P3.9) are a C++17 feature. This replaces a computed | ||
| constant with an exact representation, removing the reliance on compile-time | ||
| evaluation of the division. | ||
|
|
||
| ### `[[maybe_unused]]` attribute | ||
|
|
||
| The project defines a custom `OQMC_MAYBE_UNUSED` macro in | ||
| `include/oqmc/unused.h` (line 11) to suppress unused variable warnings: | ||
|
|
||
| ```cpp | ||
| #define OQMC_MAYBE_UNUSED(exp) (void)(exp) | ||
| ``` | ||
|
|
||
| This is used in 6 locations across the codebase: | ||
|
|
||
| - `include/oqmc/stochastic.h` line 36 — `maxIndexSize` | ||
| - `include/oqmc/lattice.h` lines 51, 62 — `cache` parameter | ||
| - `include/oqmc/sobol.h` lines 51, 62 — `cache` parameter | ||
| - `src/shapes.h` lines 95, 110 — `x` and `y` parameters | ||
|
|
||
| C++17 provides `[[maybe_unused]]` as a standard attribute. All uses can be | ||
| replaced, and `unused.h` can be deleted. For example: | ||
|
|
||
| ```cpp | ||
| // Before | ||
| OQMC_MAYBE_UNUSED(cache); | ||
|
|
||
| // After (on parameter) | ||
| [[maybe_unused]] const void* cache | ||
| ``` | ||
|
|
||
| ### Nested namespace declarations | ||
|
|
||
| `include/oqmc/bntables.h` uses nested namespaces with separate declarations: | ||
|
|
||
| ```cpp | ||
| namespace oqmc | ||
| { | ||
| namespace bntables | ||
| { | ||
| namespace pmj | ||
| { | ||
| ``` | ||
|
|
||
| C++17 allows compact nested namespace syntax: | ||
|
|
||
| ```cpp | ||
| namespace oqmc::bntables::pmj | ||
| { | ||
| ``` | ||
|
|
||
| However, care should be taken to remain consistent with the rest of the | ||
| codebase. If adopted, it should apply uniformly (e.g. `bntables.h` uses 3 | ||
| levels of nesting, while most other files use 1-2). This is a low-priority | ||
| stylistic change. | ||
|
|
||
| ### `if constexpr` | ||
|
|
||
| `include/oqmc/owen.h` line 51 uses a runtime `if` to check dimension 0: | ||
|
|
||
| ```cpp | ||
| if(dimension == 0) | ||
| { | ||
| return reverseBits16(index); | ||
| } | ||
| ``` | ||
|
|
||
| This could become `if constexpr` if the function were templated on | ||
| `dimension`. However, this would be an API change and the compiler likely | ||
| optimises this already. Low priority. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth noting that OPENQMC_ENABLE_BINARY was solving two problems at once: C++14's internal-linkage duplication in the final binary, and having each TU parse ~393K lines of integer literals. C++17 inline constexpr fixes the duplication but reintroduces the per-TU parse cost. The spec should measure this on a representative consumer and accept the trade-off explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great feedback. A really good point about the compiler time. I'll see if we can setup a test to verify the effect this change would have on this.