# Test Parametrization Some of CUB's tests are very slow to build and are capable of exhausting RAM during compilation/linking. To avoid such issues, large tests are split into multiple executables to take advantage of parallel computation and reduce memory usage. CUB facilitates this by checking for special `%PARAM%` comments in each test's source code, and then uses this information to generate multiple executables with different configurations. ## Using `%PARAM%` The `%PARAM%` hint provides an automated method of generating multiple test executables from a single source file. To use it, add one or more special comments to the test source file: ```cpp // %PARAM% [definition] [label] [values] ``` CMake will parse the source file and extract these comments, using them to generate multiple test executables for the full cartesian product of values. - `definition` will be used as a preprocessor definition name. By convention, these begin with `TEST_`. - `label` is a short, human-readable label that will be used in the test executable's name to identify the test variant. - `values` is a colon-separated list of values used during test generation. Only numeric values have been tested. ## Special Labels ### CDP / RDC Testing If a `label` is `cdp`, it is assumed that the parameter is used to explicitly test variants built with and without CDP support. The `values` for such a parameter must be `0:1`, with `0` indicating CDP disabled (RDC off) and `1` indicating CDP enabled (RDC on). Tests that do not contain a variant labeled `cdp` will only enable RDC if the CMake variable `CUB_ENABLE_TESTS_WITH_RDC` is true. ## Example For example, if `test_baz.cu` contains the following lines: ```cpp // %PARAM% TEST_FOO foo 0:1:2 // %PARAM% TEST_CDP cdp 0:1 ``` Six executables and CTest targets will be generated with unique definitions (only c++17 targets shown): | Executable Name | Preprocessor Definitions | RDC State | |----------------------------------|-----------------------------|-----------| | `cub.cpp17.test.baz.foo_0.cdp_0` | `-DTEST_FOO=0 -DTEST_CDP=0` | Disabled | | `cub.cpp17.test.baz.foo_0.cdp_1` | `-DTEST_FOO=0 -DTEST_CDP=1` | Enabled | | `cub.cpp17.test.baz.foo_1.cdp_0` | `-DTEST_FOO=1 -DTEST_CDP=0` | Disabled | | `cub.cpp17.test.baz.foo_1.cdp_1` | `-DTEST_FOO=1 -DTEST_CDP=1` | Enabled | | `cub.cpp17.test.baz.foo_2.cdp_0` | `-DTEST_FOO=2 -DTEST_CDP=0` | Disabled | | `cub.cpp17.test.baz.foo_2.cdp_1` | `-DTEST_FOO=2 -DTEST_CDP=1` | Enabled | ## Changing `%PARAM%` Hints Since CMake does not automatically reconfigure the build when source files are modified, CMake will need to be rerun manually whenever the `%PARAM%` comments change. ## Building and Running Split Tests CMake will generate individual build and test targets for each test variant, and also provides build "metatargets" that compile all variants of a given test. The variants follow the usual naming convention for CUB's tests, but include a suffix that differentiates them (e.g. `.foo_X.bar_Y` in the example above). ### Individual Test Variants Continuing with the `test_baz.cu` example, the test variant that uses `-DTEST_FOO=1 -DTEST_BAR=4` can be built and run alone: ```bash # Build a single variant: make cub.cpp17.test.baz.foo_1.bar_4 # Run a single variant bin/cub.cpp17.test.baz.foo_1.bar_4 # Run a single variant using CTest regex: ctest -R cub\.cpp17\.test\.baz\.foo_1\.bar_4 ``` ### All Variants of a Test Using a metatarget and the proper regex, all variants of a test can be built and executed without listing all variants explicitly: ```bash # Build all variants using the `.all` metatarget make cub.cpp17.test.baz.all # Run all variants: ctest -R cub\.cpp17\.test\.baz\. ``` ## Debugging Running CMake with `--log-level=VERBOSE` will print out extra information about all detected test variants. ## Additional Info Ideally, only parameters that directly influence kernel template instantiations should be split out in this way. If changing a parameter doesn't change the kernel template type, the same kernel will be compiled into multiple executables. This defeats the purpose of splitting up the test since the compiler will generate redundant code across the new split executables. The best candidate parameters for splitting are input value types, rather than integral parameters like BLOCK_THREADS, etc. Splitting by value type allows more infrastructure (data generation, validation) to be reused. Splitting other parameters can cause build times to increase since type-related infrastructure has to be rebuilt for each test variant.