Compagnie Gaspard Buma
enfr

News

Workshop lubyk

Workshop interne avec l’équipe de l’EPFL-ECAL-lab pour évaluer lubyk. Si tout va bien, on aura quelques démos à mettre en ligne…

Prochain spectacle

Nous travaillons actuellement sur le spectacle des bateaux pour nulle part prévu pour l’automne 2012.

Virtual vs Functor fight

Two C++ heroes (virtual and functor) are in a boat. The boat sinks in a sea full of sharks. They have to swim to the coast. Who gets eaten by the shark ?

africageographic200509p4243

Two C++ heroes (virtual and functor) are in a boat. The boat sinks in a sea full of sharks. They have to swim to the coast. Who gets eaten by the shark ?

To test this, I wrote a C++ file, using both virtual methods and functors (pointer to member method). I then compiled it with the -S flag in g++ to save the assembly code once with a static all, once with a call to the virtual method and once with a call to the functor. I finally made a diff between with a version without calls to see the exact assembly generated by each call.

You can have a look at the C++ code here.

The resulting difference is the following (PowerPC) assembly code :

static:
((B*)b)->swim();

lwz r0,60(r30)
mr r3,r0
bl ...swimEv

dynamic:
b->vswim();

lwz r2,60(r30)
lwz r2,0(r2)
lwz r0,0(r2)
lwz r3,60(r30)
mr r12,r0
mtctr r12
bctrl

functor
(*caller)(b);

lwz r0,56(r30)
lwz r3,60(r30)
mr r12,r0
mtctr r12
bctrl

And here the same differences but with ‘O3’ optimization level (inline functions, make code as fast as possible) :

static:
((B*)b)->swim();

mr r3,r29
bl ...swimEv

dynamic:
b->vswim();

lwz r2,0(r29)
mr r3,r29
lwz r12,0(r2)
mtctr r12
bctrl

functor
(*caller)(b);

mr r3,r29
bl ..call_swimI1B...

Before yelling “hey functors are as fast as static cast!”, you must note that the static version is totally inlined, using the values set into the registers from previous calls.

For a brief understanding of what this assembler is about, here is a quick reminder of the codes used above (more on PowerPC instructions) :

  • lwz Load Word and Zero: sets a register with a pointer (adding zeros to the right).
  • mr Move Right: copy value from left element to right element.
  • bl Branch: subroutine call to address on the right (“L__ZN…” replace by real address during link time).
  • mtctr Move To CTR: move register value to ctr register.
  • bctrl Branch to CTR: subroutine call to address in ctr.

discussion

From the code we get with optimization, we can see that functor calls are like static calls. But the call leads to “call_swim”, our templated static function, not “swim”. So it all depends on the overhead created by “call_swim”. Is it less then 3 assembly operations ? No ! The static function takes about 12 operations, from what I could understand. So it’s a clear looser.

As you can see, functor needs 9 more operations per swim cycle: he’s going to get eaten by the sharks !

We will stop using functors in rubyk and move to virtuals where possible to avoid this 9 operations overhead.

conclusion

So functors are useless ? No ! They can make things virtual members can’t. We will have to use them to enable the exposition of member methods to the command line and Lua scripts (we do not know in advance the name of theses methods, there is no virtual for them in the base class). We also learned that we should use optimization when compiling our “production” version of rubyk.