C++ Benchmark: Switch Cases

Recently busy with programming, I realized I was going to run into a number of switch cases, no pun intended. I decided to check up on what would be faster, switch statements or arrays with function pointers. What follows is my short analysis.

I tried 6 different methods:

  • Integer switch
  • DFAA (direct function access array)
  • Integer switch, whose cases contain normal functions
  • Integer switch, whose cases contain virtual functions
  • Integer switch, whose cases contain inline functions
  • If-else statements utilizing the == operator of a class

The DFAA is an array of function pointers. How it works is that, rather than comparing an index directly via a switch case, it simply goes straight to an index in an array, where there is the desired function to be used.

Without further ado, the code:

#include
#include

enum E
{ e1=0, e2, e3, e4 };

// the DFAA
static void func( int& v )
{
v++;
}

// special struct for testing operator==
struct C
{
int v;
bool operator==( int p )
{
return v==p;
}
};

// special classes for testing virtual function access
class Abstract
{
public:
virtual void apply( int& v )=0;
};

class Implementation : public Abstract
{
public:
virtual void apply( int& v )
{
v++;
}
};

// special inline func
inline static void inlineFunc( int& v )
{
v++;
}

// ---------------- Main debug
int main()
{
int i=0, i1=0, i2=0, i3=0, i4=0;
const int iterationCap = 1000000;
E option;
float switchTime=0, DFAATime=0, switch2Time=0, normalFuncTime=0, virtualFuncTime=0, inlineFuncTime=0;
clock_t switchStartTime, switchEndTime,
switch2StartTime, switch2EndTime,
DFAAStartTime, DFAAEndTime,
normalFuncStartTime, normalFuncEndTime,
virtualFuncStartTime, virtualFuncEndTime,
inlineFuncStartTime, inlineFuncEndTime;

// DFAA
void (*a[4])(int&);
a[0] = &func;
a[1] = &func;
a[2] = &func;
a[3] = &func;

switchStartTime = clock();
while ( i < iterationCap )
{
option = (E)(i % 4);
i++;
switch( option )
{
case e1: i1++; break;
case e2: i2++; break;
case e3: i3++; break;
case e4: i4++; break;
}
}
switchEndTime = clock();

i=0;

DFAAStartTime = clock();
while ( i < iterationCap )
{
option = (E)(i % 4);
i++;
(*(a[option]))( i1 );
}
DFAAEndTime = clock();

i=0;

C c;
c.v = 1;
int fairOption;
switch2StartTime = clock();
while ( i < iterationCap )
{
c.v = c.v % 4;
i++;
if ( c == 0 )
i1++;
else if ( c == 1 )
i2++;
else if ( c == 2 )
i3++;
else if ( c == 3 )
i4++;
}
switch2EndTime = clock();

i=0; i1=0; i2=0; i3=0; i4=0;

normalFuncStartTime = clock();
while ( i < iterationCap )
{
option = (E)(i % 4);
i++;
switch( option )
{
case e1: func(i1); break;
case e2: func(i2); break;
case e3: func(i3); break;
case e4: func(i4); break;
}
}
normalFuncEndTime = clock();

i=0; i1=0; i2=0; i3=0; i4=0;

Abstract* abstract = new Implementation();
virtualFuncStartTime = clock();
while ( i < iterationCap ) { option = (E)(i % 4); i++; switch( option ) { case e1: abstract->apply(i1); break;
case e2: abstract->apply(i2); break;
case e3: abstract->apply(i3); break;
case e4: abstract->apply(i4); break;
}
}
virtualFuncEndTime = clock();

i=0; i1=0; i2=0; i3=0; i4=0;

inlineFuncStartTime = clock();
while ( i < iterationCap )
{
option = (E)(i % 4);
i++;
switch( option )
{
case e1: inlineFunc(i1); break;
case e2: inlineFunc(i2); break;
case e3: inlineFunc(i3); break;
case e4: inlineFunc(i4); break;
}
}
inlineFuncEndTime = clock();

switchTime = ((float)( switchEndTime - switchStartTime ) / CLOCKS_PER_SEC) * 1000;
DFAATime = ((float)( DFAAEndTime - DFAAStartTime ) / CLOCKS_PER_SEC) * 1000;
switch2Time = ((float)( switch2EndTime - switch2StartTime ) / CLOCKS_PER_SEC) * 1000;
normalFuncTime = ((float)( normalFuncEndTime - normalFuncStartTime ) / CLOCKS_PER_SEC) * 1000;
virtualFuncTime = ((float)( virtualFuncEndTime - virtualFuncStartTime ) / CLOCKS_PER_SEC) * 1000;
inlineFuncTime = ((float)( inlineFuncEndTime - inlineFuncStartTime ) / CLOCKS_PER_SEC) * 1000;

printf( "\nClocks per second: %li \nSwitch time: %f", CLOCKS_PER_SEC, switchTime );
printf( "\n\nDFAA time: %f\nDiff from int switch: %f", DFAATime, DFAATime - switchTime );
printf( "\n\nSwitch2 time: %f\nDiff from int switch: %f", switch2Time, switch2Time - switchTime );
printf( "\n\nNormal func time: %f\nDiff from int switch: %f\nDiff from DFAA: %f", normalFuncTime, normalFuncTime - switchTime, normalFuncTime - DFAATime );
printf( "\n\nVirtual func time: %f\nDiff from int switch: %f\nDiff from normal func: %f\nDiff from DFAA: %f", virtualFuncTime, virtualFuncTime - switchTime, virtualFuncTime - normalFuncTime, virtualFuncTime - DFAATime );
printf( "\n\nInline func time: %f\nDiff from int switch: %f\nDiff from normal func: %f\n\n", inlineFuncTime, inlineFuncTime - switchTime, inlineFuncTime - normalFuncTime );

return 0;
}

And the results…

  1. Switch, whose cases contain inline functions (what on earth??)
  2. Switch
  3. Switch, whose cases contain normal functions
  4. Switch, whose cases contain virtual functions
  5. If-else statements utilizing the == operator of a class
  6. The DFAA

Example output:

Clocks per second: 1000000
Switch time: 10.735000

DFAA time: 24.990999
Diff from int switch: 14.256000

Switch2 time: 24.704000 (if-else blocks)
Diff from int switch: 13.969001

Normal func time: 8.951000
Diff from int switch: -1.783999
Diff from DFAA: -16.039999

Virtual func time: 9.923000
Diff from int switch: -0.811999
Diff from normal func: 0.972000
Diff from DFAA: -15.067999

Inline func time: 8.954000
Diff from int switch: -1.780999
Diff from normal func: 0.003000

Since I don’t check the binary, I have no idea what the compiler did to optimize. On one hand, it surprised me that the DFAA was so horrible. On the other hand, it was an original idea from a guy (me) who never wrote a compiler. What was slightly more surprising was the fact that the inline function came in first, at least most of the time. Every now and then, my results showed it to be slower (odd), but the other results stayed relatively the same.

All in all, this means C++ was designed to be used a particular way, and it is optimized for such a case. I’ve toyed with creating interpreted languages over the years, and the DFAA is a possible replacement for the switch. Switches are a problem in interpreted languages when you want to avoid too much syntactical rigidity.

I did not try if-else statements using integers. After all, I’m sure the switch statements are optimized in such a way as to act as streamlined if-else clauses. Seeing as how poorly the if-else statements with class functions did in my benchmark tests, I’m rather confident in my suspicion.

Advertisements

About chronologicaldot

Just a Christ-centered, train-loving, computer geek.
This entry was posted in software and tagged , . Bookmark the permalink.

Enter the space and time of my little world... Welcome Earthling.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s