Investigating C++ ’17 and if constexpr

Investigating C++ ’17 and if constexpr

The latest update to the C++ standard, C++ ’17, is currently undergoing final approvals prior to publication, with the final draft having been approved at the end of July this year. There are many new features and adjustments to the language in the standard; however, for today we are only going to investigate one new feature, that being if constexpr. We are going to investigate the if constexpr feature and see if we can improve compilation times when compared to other similar techniques.

if constexpr is a new way to perform branching in C++ ’17. If the result of the branch is known at compile-time, only the code that will actually be executed is compiled. This offers a clear performance benefit over branching at runtime since it removes the necessity of branch prediction, ensuring that only instructions which perform meaningful work are executed.

The capability of determining the code path at compile-time is something that has already existed for a long time in C++ with templates. Rather than using a branch at all, one can simply write a templated version of the code, relying on the compiler to generate code that matches the value of whatever template parameters are provided to it. What is being done is a tradeoff, one in which we take extra time in building the code — as the compiler generates the needed template code — in exchange for faster runtime performance.

One method of potentially mitigating the extra build time is explicit specialization, where the developer explicitly writes the code corresponding to each version of the template that is going to be needed. There are some runtime performance benefits that might be achievable through this process, but the primary benefit will come from significantly reduced compilation time. However, rewriting the code results in a longer development time and it requires substantially higher effort to maintain the code. This cost can itself be mitigated using templating scripts and code generation tools, but even these methods impose their own costs either through additional dependencies or additional complexity in the build pipeline.

All of which brings us back to if constexpr. The essential runtime benefit obtained from using an if constexpr branch should be functionally identical to that which can be obtained from using templates. Using if constexpr, however, may be a better alternative if it can offer improved compilation time without the added complexity of explicit specialization.

To investigate this, we constructed a simple program which uses a variety of methods to differentiate function calls to perform an update to a 3D volume. Since the objective here is to compare build times rather than runtimes, all the functions eventually end up running the same code. What we care about is how much time is required to compile the executable.

These times were obtained using GCC 7.1.0  on an Intel XEON E5-2650 v3. 

As expected, the generic template builds the slowest, since the compiler is generating code on-the-fly, and the explicitly specialized template code builds essentially as fast as the runtime branch code. The if constexpr code, however, is inhabiting a middle ground between the two. In addition, the if constexpr code is just as concise as the runtime branching code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
      for (int i1 = offset; i1 < volumeDimFast - offset; ++i1)
      {
          if constexpr(SpatialOrder == 8)
              CalculateEighthOrderStencil(i3, i2, i1);
          else if constexpr(SpatialOrder == 10)
              CalculateTenthOrderStencil(i3, i2, i1);
          else if constexpr(SpatialOrder == 12)
              CalculateTwelfthOrderStencil(i3, i2, i1);
          else
              std::cout << "Wrong spatial order submitted: " << SpatialOrder << std::endl;
      }

 

 

In the case of this small example, the savings in terms of code written over the explicitly templated code is about forty lines, but one can easily see that if branching conditions were more complex, the difference would grow quickly.

In conclusion, it appears that using if constexpr can be a viable alternative to templates when performing branches, producing code which executes in essentially the same time, and reducing compilation time slightly over the generic template. Using explicitly specialized templates will still lead to the fastest build times, though at the cost of increased code size and higher code maintenance obligations. The choice of which method to use of course depends on many factors, but it’s worth knowing that if constexpr represents a potentially attractive alternative to the traditional methods.

The code used to generate these results is available to download. Note that as of writing, if constexpr is only supported by GCC 7, Clang 5 (prerelease), Visual Studio 2017 15.3 Preview, and EDG eccp 4.14, though additional compiler support should arrive soon.