3

I'd like to encapsulate a float in two structures for example:

struct AngleDeg {
    explicit AngleDeg(float angle):value(angle) {}
    float value;
};

struct AngleRad {
    explicit AngleRad(float angle):value(angle) {}
    float value;
};

Then in a class overload a function using these structs, something like:

...
void DoStuff(AngleRad angle);
inline void DoStuff(AngleDeg angle) { DoStuff(Deg2Rad(angle.value)); }
...

Will this be as efficient as using the following two functions ?

void DoStuffRad(float angle);
inline void DoStuffDeg(float angle) { DoStuffRad(Deg2Rad(angle)); }
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Thelvyn
  • 264
  • 3
  • 8

5 Answers5

5

It is likely not to affect the runtime speed of your program, but can increase the odds of making the program correct. That's efficient!

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
1

It's impossible to say for sure. If you want to be 100% sure, check the generated assembly (or just time the execution of the code).

However, two observations that might help you:

  • first, your struct has the same size as a float, so there's no wasted space. And there is no unnecessary indirection: the contained float can be accessed as efficiently as before. So there's no fundamental reason why it should be slower
  • however, it is possible that this might throw off certain compiler optimizations. Perhaps it simply generates different code for structs than for built-in types in some cases. Or perhaps it uses a different calling convention for floats than for "objects which are the size of a float".

So maybe, maybe not. But it's unlikely that the speed difference will make a measurable difference.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • Your first point is not guaranteed by the standard: http://stackoverflow.com/questions/5994302/c-sizeof-wrapper-class – Thomas Eding Jul 29 '12 at 10:09
  • True, practically none of this is *guaranteed*. But in practice, it'll be the case – jalf Jul 29 '12 at 10:18
1

I thought the same as @jalf for space and access however wasn't sure. So I listened to @quamrana and @jalf, and made a runtime test.

In debug mode, the approach with structs is about 30% slower however in release mode they are equivalent. It may be compiler dependent but I will use the approach with structs.

Thelvyn
  • 264
  • 3
  • 8
  • 1
    This is not surprising: compilers will not inline functions in "debug mode" (whatever that means) so that you can have a stack trace should something go wrong. – Alexandre C. Jul 29 '12 at 10:11
  • By debug mode, I meant without optimization. The impact of the approach while debugging should be stressed because it can be a bottleneck when debugging real-time applications. – Thelvyn Jul 29 '12 at 10:18
  • 1
    That sounds about right. Wrapping in a struct means that the optimizer has to work a little bit harder. It can do that (so release builds are unaffected), but in debug builds, where the optimizer is disabled, you take a performance hit. – jalf Jul 29 '12 at 10:19
  • 1
    If you find it messes up time profiles or w/e, you can optimize just the one file (and leave out its debug info) and see if it changes anything. – Thomas Eding Jul 29 '12 at 10:25
  • 1
    @trinithis: it is not generally a matter of debug information (they are in separate sections of the library, and only loaded on demand). It is just that, as Alexandre C pointed out, to obtain proper stack traces the inline functions are not, in fact, inlined in most Debug mode. – Matthieu M. Jul 29 '12 at 12:24
1

This question was investigated in a Technical Report on C++ Performance. See appendix D.3 for the Stepanove Abstraction Penalty Benchmark. To quote

The structure of the benchmark is really quite simple. It adds 2000 doubles in an array 25000 times. It does it in 13 different ways that introduce more and more abstract ways of doing it:
0 - uses simple Fortran-like for loop.
1 - 12 use STL style accumulate template function with plus function object.
1, 3, 5, 7 ,9, 11 use doubles. 2, 4, 6, 8, 10, 12 use Double - double wrapped in a class.
1, 2 - use regular pointers.
3, 4 - use pointers wrapped in a class.
5, 6 - use pointers wrapped in a reverse-iterator adaptor.
7, 8 - use wrapped pointers wrapped in a reverse-iterator adaptor.
9, 10 - use pointers wrapped in a reverse-iterator adaptor wrapped in a reverse-iterator adaptor.
11, 12 - use wrapped pointers wrapped in a reverse-iterator adaptor wrapped in a reverse-iterator adaptor.

Output on Ideone: the abstraction penalty (geometric mean of all 13 tests) for a modern compiler (gcc 4.5.1) is less than 1%.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
0

For any compiler worth your money (even if it is free), it will have no overhead over using a plain float.

Thomas Eding
  • 35,312
  • 13
  • 75
  • 106