Friday, March 7, 2014

Functor optimization

I have this piece of code that can be compiled with -On (n > 0) but cannot be compiled with -O0.

#include <iostream>

class Functor1 {
public:
    void operator()() const {
        std::cout << "Functor 1" << "\n";
    }
};

class Functor2 {
public:
    void operator()() const {
        std::cout << "Functor 2" << this << "\n";
    }
};

template <typename FunctorType>
class TemplateWithStaticMember {
public:
    TemplateWithStaticMember() {
        functor_();
    }
private:
    static const FunctorType functor_;  // THIS LINE!!!
};

/* Incomplete fix:
template <typename FunctorType>
const FunctorType TemplateWithStaticMember<FunctorType>::functor_; */

int main(int argc, char* argv[]) {
    TemplateWithStaticMember<Functor1> f1;
    // TemplateWithStaticMember<Functor2> f2;
}

Under GCC 4.8, when compile with -O0, we get this error:

/tmp/ccFIY33S.o: In function `TemplateWithStaticMember::TemplateWithStaticMember()': main.cpp:(.text._ZN24TemplateWithStaticMemberI8Functor1EC2Ev[_ZN24TemplateWithStaticMemberI8Functor1EC5Ev]+0xd): undefined reference to `TemplateWithStaticMember::functor_' collect2: error: ld returned 1 exit status

At other optimization levels, the code can be compiled and executed just fine.

If we uncomment the second functor, the code always fails, regardless of optimization levels.

The reason is our template declares a static constant variable functor_ (at the line marked with THIS LINE!!!). At high level optimization, the compiler finds out that we only use the functor object to execute a function so the compiler inlines the function and optimizes away the functor object. Without optimization, the compiler requires a definition of functor_ and fails to find one.

When we use f2, its functor_'s operator() refers back to itself via this. That requires the functor object to actually be allocated. But because we have not defined any such functor object, the compiler will fail to compile our code.

I find this piece of code interesting because usually higher (not lower) optimizations make code fail. For example: Prof. John Regehr initially blogged about undefined behavior under optimizations, and STACK team at MIT published a paper about optimization-safe code.

1 comment:

  1. C++ spec requires static member of a class must be declared explicitly? Add following line before main() solve all cases

    template
    const FunctorType TemplateWithStaticMember::functor_;

    ReplyDelete