Suresh
Suresh

Reputation: 9605

Efficient switch statement

In the following two versions of switch case, I am wondering which version is efficient.

1:

string* convertToString(int i)
{
    switch(i)
    {
    case 1:
        return new string("one");
    case 2:
        return new string("two");
    case 3:
        return new string("three");
        .
        .
    default:
        return new string("error");
    }
}

2:

string* convertToString(int i)
{
    string *intAsString;
    switch(i)
    {
    case 1:
        intAsString = new string("one");
        break;
    case 2:
        intAsString = new string("two");
        break;
    case 3:
        intAsString = new string("three");
        break;
        .
        .
    default:
        intAsString = new string("error");
        break;
    }
return intAsString;
}

1: has multiple return statements will it cause compiler to generate extra code?

Upvotes: 3

Views: 2861

Answers (14)

Charles Eli Cheese
Charles Eli Cheese

Reputation: 783

The funny part is you worry about efficieny of break then return but make a new string every time.

The answer is it's up to the compiler, but it should not matter either way. Avoiding the new string will if you call this all the time.

The switch can often be optimized so that it performs a jump instead of a bunch of if else, but if you look in the assembly source you'll generally be underwhelmed by how little the optimizer does.

Upvotes: 0

Hassan Syed
Hassan Syed

Reputation: 20496

There should not be any measurable difference, return statements should not generate any machinery. They should put a pointer to the string object (allocated on the heap) on the stack of the callsite.

Upvotes: 0

Steve Jessop
Steve Jessop

Reputation: 279395

You optimise[*] switch statements by doing as little work as possible in the switch (because it's uncertain whether the compiler will common up the duplication). If you insist on returning a string by pointer, and using a switch statement, I'd write this:

string *convertToString(int i) {
    const char *str;
    switch(i) {
        case 1 : str = "one"; break;
        // etc
        default : str = "error"; break;
    }
    return new string(str);
}

But of course for this example I'd probably just use a lookup table:

const char *values[] = {"error", "one", ... };

string convertToString(unsigned int i) {
    if (i >= sizeof(values)/sizeof(*values)) i = 0;
    return values[i];
}

That said, I just answered a question about the static initialization order fiasco, so you don't in general want rules of thumb which demand globals. What you do has to depend on the context of the function.

[*] Where I mean the kind of rule-of-thumb optimisation that you do when writing portable code, or in your first version, in the hope of creating code that is clear to read and won't need too much real optimisation. Real optimisation involves real measurements.

Upvotes: 1

Thorsten79
Thorsten79

Reputation: 10128

You can never know how optimization will influence the code produced unless you compile with a specific compiler version, a specific set of settings and a specific code base.

C++ optimizing compilers may decide to turn your source code upside down to gain a specific optimization only available for compiler architecture so-and-so without you ever knowing it. A powerful optimizing compiler may e.g. find out that only 2 out of 10 cases are ever needed and will optimize away the whole switch-case-statement.

So my answer to your question is: Mu.

Upvotes: 3

Sebastian
Sebastian

Reputation: 4950

Consider keeping the strings as static constants:

static char const g_aaczNUMBER[][] = 
    {
        {"Zero"}, { "One" }, ...
    };

static char const g_aczERROR[] = { "Error" };

char* convertIntToString(int i) const { 
    return i<0 || 9<i ? g_aczERROR : g_aaczNUMBER[i]; 
}

Upvotes: 1

philsquared
philsquared

Reputation: 22493

There should be no difference in the compiled code. However:

  1. You'll probably find returning the strings by value to be more efficient.
  2. If there are a lot of strings consider prepopulating a vector with them (or declare a static array) and use i as the index in.

Upvotes: 6

unwind
unwind

Reputation: 400109

This is a premature optimization worry.

The former form is clearer and has fewer source lines, that is a compelling reason to chose it (in my opinion), of course.

You should (as usual) profile your program to determine if this function is even on the "hot list" for optimization. This will tell you if there is a performance penalty for using break.

As was pointed out in the comments, it's very possible that the main performance culprit of this code is the dynamically allocated strings. Generally, when implementing this kind of "integer to string" mapping function, you should return string constants.

Upvotes: 29

epatel
epatel

Reputation: 46051

A switch statement is basically a series of if statements as generated machine instructions. One simple optimization strategy is to place the most frequent case first in the switch statement.

I also recommend the same solution as Sebastian but without the assert.

static const char *numberAsString[] = {
    "Zero",
    "One",
    "Two",
    "Three",
    "Four",
    "Five",
    "Six",
};

const char *ConvertToString(int num) {
  if (num < 1 || num >= (sizeof(numberAsString)/sizeof(char*))) 
    return "error";
  return numberAsString[num];
}

Upvotes: 3

Konrad
Konrad

Reputation: 41027

I would suggest something of the form:

void CScope::ToStr( int i, std::string& strOutput )
{
   switch( i )
   {
   case 1:
        strOutput = "Some text involving the number 1";

   ... etc etc
}

By returning a pointer to a string created on the heap, you risk memory leaks. Specifically regarding your question, I would suggest that the least number of return paths is more advisable than premature optimisation.

Upvotes: 1

Jerod Venema
Jerod Venema

Reputation: 44652

There won't be any difference in efficiency here. Certainly none that will matter. The only benefit of going with option #2 is if you'll need to do some post-processing of the string that applies to all cases.

Upvotes: 0

Nick Lewis
Nick Lewis

Reputation: 4240

They will almost certainly both be compiled to an identical, highly-efficient branch table. Use whichever one you feel is clearer.

Upvotes: 1

Daniel Daranas
Daniel Daranas

Reputation: 22644

Both are.

What you should really be concerned about is your use of pointers here. Is it necessary? Who will delete these strings? Isn't there a simpler alternative?

Upvotes: 9

Gunther Piez
Gunther Piez

Reputation: 30449

If you turn optimizing on, both functions will very likely generate equivalent code.

Upvotes: 2

sth
sth

Reputation: 229874

The compiler most probably will optimize both versions to the same code.

Upvotes: 1

Related Questions