Regarding C++11 noexcept


(Niels Dekker) #1

It was an unpleasant surprise to me, to read here at the forum (December 2017) that noexcept might sometimes harm the performance. As noted by @matt.mccormick at warning: dynamic exception specifications are deprecated in C++11

Now I’m just posted this question to Microsoft’s Developer Community forum: C++11 noexcept implementation still a performance loss with VS2017? Hope that Visual C++ is doing better by now…

I do have some good news, already :slight_smile: I just profiled the following code on Visual Studio 2017 (15.9.5), Release:

std::vector<itk::NeighborhoodAllocator<int>> neighborhoodAllocators;

itk::NeighborhoodAllocator<int> neighborhoodAllocator;
neighborhoodAllocator.set_size(1);
neighborhoodAllocator[0] = 42;

neighborhoodAllocators.assign(7500000, neighborhoodAllocator);

neighborhoodAllocators.reserve(neighborhoodAllocators.capacity() + 1);

I observed that the last line of code, calling std::vector::reserve, runs more than four times faster now that itk::NeighborhoodAllocator has a ITK_NOEXCEPT move-constructor:

itk::NeighborhoodAllocator move-constructor without ITK_NOEXCEPT: ~0.6 second
itk::NeighborhoodAllocator move-constructor with ITK_NOEXCEPT: ~0.13 second :tada: :tada: :tada:

The ITK_NOEXCEPT itk::NeighborhoodAllocator move-constructor that I proposed was merged onto the ITK git master branch by @phcerdan, earlier this week:

So when a class has a manually implemented non-throwing move-constructor that is faster than its copy-constructor, the noexcept specifier is certainly beneficial. :slight_smile:


(Niels Dekker) #2

Check https://developercommunity.visualstudio.com/content/problem/425370/c11-noexcept-implementation-still-a-performance-lo.html for the replies by @Terry Mahaffey :slight_smile:


(Niels Dekker) #3

It appears to me that three categories of non-throwing functions could be distinguished:

  1. Those where adding a noexcept specifier does evidently improve the runtime performance, in a way that is observable, reproducible, and logically explainable.

  2. Those where adding noexcept appears neutral to the performance: in these cases, no significant effect has been observed.

  3. Those where adding noexcept actually harms the performance.

Would it be a good idea to use the C++11 noexcept specifier directly, instead of the ITK_NOEXCEPT macro, for the first category of functions?

I would like to propose to use noexcept, instead of ITK_NOEXCEPT, for those functions for which the performance gain is obvious. For example, the move-constructor and the move-assignment operator of itk::NeighborhoodAllocator.

In cases where the performance gain is not (yet) obvious, ITK_NOEXCEPT could still be used.

What do you think?


(Bradley Lowekamp) #4

Thank you for your work on trying to figure this out.

How many compilelers have you tested on to make this conclusion?

Another case of note is where I added the old no throw was in the base objects and command classes in methods related to the destructor and managing reference between objects. As I recall if an exception occurs here it can lead to leaks or worse yet invalid referenced.


(Pablo Hernandez-Cerdan) #5

ITK_NOEXCEPT expands to noexcept already.
Check the generated file in /path/build/Modules/Core/Common/itk_compiler_detection.h

#  if defined(ITK_COMPILER_CXX_NOEXCEPT) && ITK_COMPILER_CXX_NOEXCEPT
#    define ITK_NOEXCEPT noexcept
#    define ITK_NOEXCEPT_EXPR(X) noexcept(X)
#  else
#    define ITK_NOEXCEPT
#    define ITK_NOEXCEPT_EXPR(X)
#  endif

I guess ITK 5.0 doesn’t have to worry about compilers that do not support noexcept, so we could use noexcept directly.

But there is no difference between them.

ITK_NOEXCEPT is not an optional qualifier that the user can switch off if you meant that.


(Matt McCormick) #6

@Niels_Dekker as you have noted and Terry Mahaffey notes in the reply, the behavior depends on the specific situation and the compiler. And, in many cases, the compiler can infer whether exception-related optimizations are possible or not.

@jhlegarreta and I are working towards a GitHub check that will show performance differences across the different platforms and compilers.

Once we have the check in place, we will be able to determine 1., 2., and 3. We have observed that behavior varies vastly across compliers sometimes. Once we have performance testing, we could add the specification for 1. remove it for ‘2.’ and ‘*3.’ If we find that Visual Studio just has issues in some cases where the other compilers see a performance increase, we could define ITK_NOEXCEPT to be empty with Visual Studio. Where it always demonstrates peformance improvements, we could use noexcept.

Until then, ITK_NOEXCEPT serves a useful, grep’able marker that is still functional.


(Niels Dekker) #7

@matt.mccormick Thanks for your explanation! I’m very interested to hear about any preliminary result from the GitHub check you and @jhlegarreta are working towards.

So far I haven’t seen any example for which adding noexcept would actually harm the performance. Did you, already?

A complete example to check the performance of neighborhoodAllocators.reserve is at:

In this case, it appears that std::vector::reserve became more than 4x faster when a noexcept move-constructor was added to itk::NeighborhoodAllocator. But I only tried VS2017 (Version 15.9.5) Release x64. I’m very interested to hear if the performance gain from noexcept can be reproduced with other compilers.


(Pablo Hernandez-Cerdan) #8

The performance gain of flagging noexcept a move constructor will be noticed by all the compilers, because it is at the std library level. Not related with any internals of compilers. The std algorithms choose the move (usually faster) only if it is noexcept or if the compiler can infer that is noexcept.

The harm at performance because flagging noexcept ANY function happens with the VS compiler, because its internal implementation as you were answered by the VS developer. This is a low-level detail. The performance increase at the algorithm level will be much greater than this implementation-detail drawback. But also help us to not go crazy flagging everything noexcept.

My 2 cents and take away for us would be: let’s put noexcept only where matters for the std algorithms to choose the fastest route. This is at move and swaps. And only if the compiler cannot infer it by default.


(Matt McCormick) #9

I have not seen a lot of evidence in any direction – putting my science hat on, I would definitely like to see more evidence :tophat::microbe::microscope: .

This is great! :heart:

After testing on GCC / Linux and Apple / Clang, it would be reasonable to mark noexcept.


(Niels Dekker) #10

Personally I find item 14 from Scott Meyers, Effective Modern C++ (2015) quite convincing: “Declare functions noexcept if they won’t emit exceptions”. If you don’t have access to the book, there’s a draft version of the item at https://aristeia.com/EC++11-14/noexcept%202014-03-31.pdf

But honestly, I just got myself some evidence that noexcept might indeed harm the performance, in some very specific cases, when using Visual C++. I could reproduce this issue with the latest version of VS2017 (version 15.9.5), Release configuration, but only for 32-bit (Win32 x86). I could not reproduce the issue for 64-bit.

For a large number of function calls (numberOfFuncCalls greater than 10,000,000), I compared

for (int i = 0; i < numberOfFuncCalls; ++i)
{
  NoexceptFunc(false);
}

for (int i = 0; i < numberOfFuncCalls; ++i)
{
  UnspecifiedFunc(false);
}

The functions NoexceptFunc(bool) and UnspecifiedFunc(bool) were equivalent, except that the first one had a noexcept specifier. To avoid inlining, I put both of them in a DLL, separate from the main application. NoexceptFunc(bool) was implemented as follows:

__declspec(dllexport) void NoexceptFunc(bool doThrowException) noexcept
{
  assert(!doThrowException);
  ThrowIf(doThrowException);
}

And ThrowIf(doThrowException) was as follows:

void ThrowIf(const bool doThrowException)
{
  if (doThrowException) throw std::exception{};
}

It appeared to me that the function call UnspecifiedFunc(false) was twice as fast as NoexceptFunc(false)!

If there’s any interest, I can post the entire example on my github.