Morwenn / cpp-sort

Sorting algorithms & related tools for C++14
MIT License
619 stars 57 forks source link

What about constexpr? #58

Open Morwenn opened 8 years ago

Morwenn commented 8 years ago

Standard constexpr algorithms

P0202 proposes to add constepxr to many functions from <cstring> and to basically every algorithm from <algorithm>. Apparently, the proposal was rather well-received and we might have constexpr algorithms in the standard at some point in the future.

So, what about cpp-sort? Since we're already using C++14, we can use the improved constexpr but I feel that it won't solve every problem:

I would be trivial to change some algorithms to be constexpr but it's probably not the case for most of them. A partial constexpr support could be a starting point.


The first revision of P0202 takes several design choices into consideration and gives the following set of rules to make standard algorithms constexpr (<cstring> has been disqualified from constexpr enhancements):

Basically, we will wait for a constexpr-ified standard library (at least for std::copy and std::move), then we can start to constexpr-ify cpp-sort. The whole iter_move thing might make things harder to do though. Anyway, this is definitely a future issue.

Use cases

I often wondered whether there were use cases for constexpr sorting algorithms. Here is a list of situations where sorting at compile-time might be useful:

Morwenn commented 8 years ago

Marking this as a future issue for now. Apparently not everyone thinks P0202 is a good idea as is (can't remember where I read that, but I definitely did), and too many existing parts of the standard library would have to be implemented again in order to be usable at compile-time. Some of them even rely on constexpr versions of std::memcpy and friends, which means that it will be hard to get a portable implementation without an officially constexpr standard library.

Morwenn commented 5 years ago

C++20 is approaching with plenty of extended constexpr and compile-time utilities, most notably:

All those improvements should make C++20 the suitable candidate for making most of the library constexpr. However there are caveats to take into account: we're supposed to use std::construct_at and std::destruct_at instead of placement new and [pseudo-]destructor calls to construct and destruct objects. Moreover objects created at compile time must be explicitly destroyed, we can't elide the destructor call at compile time even if it is trivial, so we need to check std::is_constant_evaluated to decide whether to call the trivial destructors or not (ignoring them at runtime still helps with debug mode performance).

Morwenn commented 5 years ago

Another advantage I hadn't thought of is that undefined behaviour isn't allowed when invoking constexpr functions, so it would be something valuable in the testsuite. Once many functions are marked constexpr, on interesting way to test them would be to use Catch2's STATIC_REQUIRE when possible.

Both compile-time checks and runtime checks are interesting though (just to make sure that all the tests are run even when there are failures at compile time), so I would most likely just add another CI job per compiler for compile-time tests, and define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE for the ones that already exist. That would warrant another option in the testsuite CMake file.

Morwenn commented 3 years ago

Instead of completely waiting for C++20, I decided to start rolling out some constexpr support in version 1.10.0: the idea is that even if we don't provide constexpr sorters ourselves, the library components such as sorter_facade can still be used to create constexpr sorters. This is still fairly limited since passing ranges to a sorter in a constexpr context won't work before C++17, but this is somewhat enabling without having to change many things.

Morwenn commented 2 years ago

In the spirit of a few messages above, 1.13.0 ships with a new CMake option called CPPSORT_STATIC_TESTS which controls CATCH_CONFIG_RUNTIME_STATIC_REQUIRE. I changed tests to use STATIC_CHECK instead of CHECK when it was trivial to do so. By default all tests are deferred to runtime, but setting that new option to ON allows the changed ones to run at compile time instead.

The constexpr support is rolling out slowly, but those are all good first steps.

Morwenn commented 2 years ago

Changing the whole test suite would be quite the task and I still don't have a master plan for it, so for now I decided to go with the simplest approach: have a new test where constexpr sorters are called at compile time on a small array. This is quite limited for a bunch of reasons (for example it only tests algorithms that work on random-access iterators) but will already allow to see which sorters can be made trivially constexpr and which ones require additional work. We need to start somewhere and that's a good place to start if any, iterations to increase the coverage can happen later.

List of sorters to try to make constexpr:

Adapters:

Measures of presortedness:

Other actions:

I will first perform tests on the 2.x.y branch, but might also backport the ones that can trivially be made constexpr to the 1.x.y branch, guarded with a macro only enabling the feature in C++20 mode.

Morwenn commented 2 years ago

Components that can reasonably be marked constexpr without too much additional work in 1.x.y: