実際、ここにいくつかのコメントがあるにもかかわらず、多少速くなる可能性があります。
実際にテストしてみましょう。
using System;
using System.Diagnostics;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
int count = 1000000000;
Stopwatch sw = Stopwatch.StartNew();
for (int way = 1; way <= 3; ++way)
test1(count, way);
var elapsed1 = sw.Elapsed;
Console.WriteLine("test1() took " + elapsed1);
sw.Restart();
for (int way = 1; way <= 3; ++way)
test2(count, way);
var elapsed2 = sw.Elapsed;
Console.WriteLine("test2() took " + elapsed2);
Console.WriteLine("test2() was {0:f1} times as fast.", + ((double)elapsed1.Ticks)/elapsed2.Ticks);
}
static void test1(int count, int way)
{
for (int i = 0; i < count; ++i)
{
switch (way)
{
case 1: doWork1(); break;
case 2: doWork2(); break;
case 3: doWork3(); break;
}
}
}
static void test2(int count, int way)
{
switch (way)
{
case 1:
for (int i = 0; i < count; ++i)
doWork1();
break;
case 2:
for (int i = 0; i < count; ++i)
doWork2();
break;
case 3:
for (int i = 0; i < count; ++i)
doWork3();
break;
}
}
static void doWork1()
{
}
static void doWork2()
{
}
static void doWork3()
{
}
}
}
doWork()メソッドは何もしないので、これは非常に非現実的です。ただし、ベースラインのタイミングがわかります。
Windows7x64システムでのRELEASEビルドで得られる結果は次のとおりです。
test1() took 00:00:03.8041522
test2() took 00:00:01.7916698
test2() was 2.1 times as fast.
したがって、ループをswitchステートメントに移動すると、2倍以上高速になります。
次に、doWork()にコードを追加して、もう少し現実的にしましょう。
using System;
using System.Diagnostics;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
int count = 1000000000;
Stopwatch sw = Stopwatch.StartNew();
for (int way = 1; way <= 3; ++way)
test1(count, way);
var elapsed1 = sw.Elapsed;
Console.WriteLine("test1() took " + elapsed1);
sw.Restart();
for (int way = 1; way <= 3; ++way)
test2(count, way);
var elapsed2 = sw.Elapsed;
Console.WriteLine("test2() took " + elapsed2);
Console.WriteLine("test2() was {0:f1} times as fast.", + ((double)elapsed1.Ticks)/elapsed2.Ticks);
}
static int test1(int count, int way)
{
int total1 = 0, total2 = 0, total3 = 0;
for (int i = 0; i < count; ++i)
{
switch (way)
{
case 1: doWork1(i, ref total1); break;
case 2: doWork2(i, ref total2); break;
case 3: doWork3(i, ref total3); break;
}
}
return total1 + total2 + total3;
}
static int test2(int count, int way)
{
int total1 = 0, total2 = 0, total3 = 0;
switch (way)
{
case 1:
for (int i = 0; i < count; ++i)
doWork1(i, ref total1);
break;
case 2:
for (int i = 0; i < count; ++i)
doWork2(i, ref total2);
break;
case 3:
for (int i = 0; i < count; ++i)
doWork3(i, ref total3);
break;
}
return total1 + total2 + total3;
}
static void doWork1(int n, ref int total)
{
total += n;
}
static void doWork2(int n, ref int total)
{
total += n;
}
static void doWork3(int n, ref int total)
{
total += n;
}
}
}
今、私はこれらの結果を得ます:
test1() took 00:00:03.9153776
test2() took 00:00:05.3220507
test2() was 0.7 times as fast.
これで、ループをスイッチに入れるのが遅くなります。この直感に反する結果は、これらの種類のものの典型であり、コードを最適化しようとしているときに常にタイミングテストを実行する必要がある理由を示しています。(そして、このようなコードの最適化は、ボトルネックがあると疑う正当な理由がない限り、通常は行うべきではありません。コードのクリーンアップに時間を費やしたほうがよいでしょう。;))
私は他のいくつかのテストを行いましたが、少し単純なdoWork()メソッドの場合、test2()メソッドの方が高速でした。これは、JITコンパイラが最適化で何ができるかに大きく依存します。
注:2番目のテストコードの速度が異なる理由は、test1()のようにループに入っていないときに、doWork()の呼び出しをインライン化するときに、JITコンパイラが「ref」呼び出しを最適化できるためだと思います。 ; 一方、test2()の場合は、(何らかの理由で)できません。