Sconibulus
Sconibulus

Reputation: 732

Switch inside loops impacting performance?

I'm in a scenario where I'm looping through data and formatting it in specific ways based on a setting, and I'm concerned that what I feel is best stylistically might impede performance.

The basic pattern of the code is as follows

enum setting {single, multiple, foo, bar};
Data data = getData(Connection conn, int id);
setting blah = data.getSetting();
foreach (Item item in data)
{
   switch(blah)
   {
      case blah.single:
        processDataSingle(item blah);
        break;
      ...
   }
}

My concern is that there might be thousands, or even tens of thousands of items in data. I was wondering if having the switch inside the loop where it may be evaluated repeatedly might cause some serious performance issues. I know I could put the switch before the loop, but then each case contains it, which seems much less readable, in that it's less apparent that the basic function remains the same.

Upvotes: 4

Views: 1085

Answers (6)

RoadBump
RoadBump

Reputation: 751

If the values in the switch statement are near one to another, the compiler will produce a lookup table instead of N if statements. It increases performance, but it's hard to say when the compiler will decide to do this.
Instead you can create a Dictionary<switchType,Delegate>, populate it with pairs of value-action, and then selecting the appropriate action will take about O(1) as dictionary is a hash table.
dictionary[value].Invoke().

Upvotes: 0

Tim Schmelter
Tim Schmelter

Reputation: 460118

You could create a method to keep readability, then pass the data to the method:

void processAllData(IEnumerable<Item> data,  setting blah)
{
    switch(blah)
    {
      case blah.single:
        foreach (Item item in data)
        {

        }
    }
    // next case, next loop ...
}

Then it's just a one-liner:

processAllData(data, blah);

This approach is readable since it encapsulates complexity, concise since you only see what you have to see and efficient since you can optimize the cases.

Upvotes: 2

Corey Adler
Corey Adler

Reputation: 16137

It certainly does have the potential to affect performance if you're talking about possibly running the comparison tens of thousands of times or more. The other problem that could potentially arise in the code that you've written here is what happens if you need to add to your enum. Then you'd need to open up this code and adjust it to take care of that circumstance, which violates the Open/Closed Principle.

The best way, IMO, to solve both problems at once would be to use a Factory pattern to take care of this (see posts here and here for some advice on starting that). All you'd need to do is have an interface whose implementations would call the method that you'd want to call in your switch code above. Create a factory and have it pick which implementation to return back to your code (before the loop) based on the enum passed in. At that point all your loop needs to do is to call that interface method which will do exactly what you wanted.

Afterwards, any future feature additions will only require you to create another implementation of that interface, and adjust the enum accordingly. No muss, no fuss.

Upvotes: 1

Larry
Larry

Reputation: 18031

By using a Action delegate this way, you can factorize your code a lot

enum setting {single, multiple, foo, bar};
Data data = getData(Connection conn, int id);

var processAll = new Action<Action<item>>(action =>
                    {
                        foreach(var item in data)                           
                            action(item);
                    });

setting blah = data.getSetting();

switch(blah)
{
    case blah.single:
       processAll(item => processDataSingle(item, blah));
       break;
       ...
}

Upvotes: 1

Matthew Watson
Matthew Watson

Reputation: 109567

It's almost certainly slower to put the switch in the loop like that. Whether it's significant or not is impossible to say - use a Stopwatch to see.

Upvotes: 0

Rawling
Rawling

Reputation: 50114

You could set up a delegate/action once, then call it every time in the loop:

Data data = getData(Connection conn, int id);
setting blah = data.getSetting();
Action<Item> doThis;
switch (blah)
{
  case blah.single:
      doThis = i => processSingleData(i blah);
      break;
  ...
}
foreach (Item item in data)
{
    doThis(item);
}

Basically, put the body of each "case" in an Action, select that Action in your switch outside the loop, and call the Action in the loop.

Upvotes: 11

Related Questions