Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make is_pod<T> tests be performed during compilation and not execution?

This might be an easy question, I don't master C++11 templates at all.

I have a generic vector class that is not std::vector<T> for performance reasons (very specific code).

I have observed that checking whether T is a POD or not and, when it is, perform special computations, is much more efficient than not :

void vec<T>::clear() {
  if (!std::is_pod<T>::value) {
    for (int i = 0; i < size; i++) {
       data[i].~T();
    }
  }

  size = 0;
}

Here, I don't call the destructor of T for each item (size can be really huge) and performance is really boosted. But the test if (!std::is_pod<T>::value) is useless once the template was compiled : rather than being compiled to :

void vec<int>::clear() {
  if (false) {
    for (int i = 0; i < size; i++) {
       data[i].~int();
    }
  }

  size = 0;
}

I want it to be compiled to :

void vec<int>::clear() {
  size = 0;
}

Is the compiler "clever" enough to skip if (false) blocks or if (true) tests ? Do I have to write that code somewhat differently ?

like image 219
Fabien Avatar asked Jun 19 '13 09:06

Fabien


Video Answer


2 Answers

Is the compiler "clever" enough to skip if (false) blocks or if (true) tests?

Yes, definitely. Dead code elimination is a trivial optimisation that is performed routinely. Its existence is also crucial to make many debugging libraries work efficiently (= without runtime overhead in release mode).

But I would probably still rewrite this to make it visible to the reader that this is a compile-time distinction, by overloading the function based on is_pod:

void vec<T>::do_clear(std::true_type) { }

void vec<T>::do_clear(std::false_type) {
    for (int i = 0; i < size; i++) {
       data[i].~T();
    }
}

void vec<T>::clear() {
    do_clear(std::is_trivially_destructible<T>());
    size = 0;
}

In the above code I’m using is_trivially_destructible instead of is_pod to make the code more self-explanatory, as suggested by Nicol in the comments. This technique is commonly employed in standard library implementations and other libraries. It’s known as tag dispatching.

like image 55
Konrad Rudolph Avatar answered Nov 01 '22 11:11

Konrad Rudolph


There is a language feature called pseudo destructors which is specifically designed for what you want to do. Basically given a type template parameter T you can syntactically call a destructor for it, and if, when instantiated, T is a scalar type (because for example it is a fundamental type like an int) it will compile and generate a no-op in its place.

For the remainder of POD types that are not scalar, they have trivial destructors, so will likewise generate a no-op.

Any production compiler on even the lowest optimization setting will elide a loop over a no-op. So you can safely write:

void vec<T>::clear() { 
    for (int i = 0; i < size; i++) {
       data[i].~T();
    }

    size = 0;
}

Basically, you are trying to solve an imaginary performance problem the compiler is already taking care of for you.

like image 21
Andrew Tomazos Avatar answered Nov 01 '22 10:11

Andrew Tomazos