| # Unspecified Behavior Randomization | |
| ## Background | |
| Consider the follow snippet which steadily happens in tests: | |
| ```cpp | |
| std::vector<std::pair<int, int>> v(SomeData()); | |
| std::sort(v.begin(), v.end(), [](const auto& lhs, const auto& rhs) { | |
| return lhs.first < rhs.first; | |
| }); | |
| ``` | |
| Under this assumption all elements in the vector whose first elements are equal | |
| do not guarantee any order. Unfortunately, this prevents libcxx introducing | |
| other implementations because tests might silently fail and the users might | |
| heavily depend on the stability of implementations. | |
| ## Goal | |
| Provide functionality for randomizing the unspecified behavior so that the users | |
| can test and migrate their components and libcxx can introduce new sorting | |
| algorithms and optimizations to the containers. | |
| For example, as of LLVM version 13, libcxx sorting algorithm takes | |
| [O(n^2) worst case](https://llvm.org/PR20837) but according | |
| to the standard its worst case should be `O(n log n)`. This effort helps users | |
| to gradually fix their tests while updating to new faster algorithms. | |
| ## Design | |
| - Introduce new macro `_LIBCPP_DEBUG_RANDOMIZE_UNSPECIFIED_STABILITY` which should | |
| be a part of the libcxx config. | |
| - This macro randomizes the unspecified behavior of algorithms and containers. | |
| For example, for sorting algorithm the input range is shuffled and then | |
| sorted. | |
| - This macro is off by default because users should enable it only for testing | |
| purposes and/or migrations if they happen to libcxx. | |
| - This feature is only available for C++11 and further because of | |
| `std::shuffle` availability. | |
| - We may use [ASLR](https://en.wikipedia.org/wiki/Address_space_layout_randomization) or | |
| static `std::random_device` for seeding the random number generator. This | |
| guarantees the same stability guarantee within a run but not through different | |
| runs, for example, for tests become flaky and eventually be seen as broken. | |
| For platforms which do not support ASLR, the seed is fixed during build. | |
| - The users can fix the seed of the random number generator by providing | |
| `_LIBCPP_RANDOMIZE_UNSPECIFIED_STABILITY_SEED=seed` definition. | |
| This comes with some side effects if any of the flags is on: | |
| - Computation penalty, we think users are OK with that if they use this feature. | |
| - Non reproducible results if they don't use the fixed seed. | |
| ### Impact | |
| Google has measured couple of thousands of tests to be dependent on the | |
| stability of sorting and selection algorithms. As we also plan on updating | |
| (or least, providing under flag more) sorting algorithms, this effort helps | |
| doing it gradually and sustainably. This is also bad for users to depend on the | |
| unspecified behavior in their tests, this effort helps to turn this flag in | |
| debug mode. | |
| ### Potential breakages | |
| None if the flag is off. If the flag is on, it may lead to some non-reproducible | |
| results, for example, for caching. | |
| ### Currently supported randomization | |
| - `std::sort`, there is no guarantee on the order of equal elements | |
| - `std::partial_sort`, there is no guarantee on the order of equal elements and | |
| : on the order of the remaining part | |
| - `std::nth_element`, there is no guarantee on the order from both sides of the | |
| : partition | |
| Patches welcome. |