かなり奇妙です。今日まで、入れ子になった if-else の前に常に高いチャンス句を配置する必要があると考えていました。
簡単なセットアップ:
配列Zoo[]
には、重みに基づいて 5 つのクラスの 10,000 個のオブジェクトが含まれます。たとえば、4,3,2,1,0 (4000 匹の猫、3000 匹の犬、2000 匹のニワトリ、1000 匹のウサギ、0 匹のフクロウを意味します) であり、シャッフルするかどうかを指定できます (正確に順番に)。
次にif-else
、各配列メンバーを確認するために使用します。
結果: 時間 (ミリ秒)
Weights 43210 01234 22222 43210 01234 22222
Shuffle Yes Yes Yes No No No
Polymorphism 101 100 107 26 26 27
If Else 77 28 59 17 16 17
If Else Reverse 28 77 59 16 17 16
Switch 21 21 21 18 19 18
If-Else
逆のほうが よりもはるかに優れているのを見たとき、それは私の目を引きましif-else
た。ここではif-else
、Cat->Dog->Chicken->Rabbit->Owl を検査します。逆バージョンでは、それらを逆の順序で検査します。
また、シャッフルなしのバージョンで、すべての方法が大幅に改善されることを誰かが説明できますか? (キャッシュまたはメモリのヒット率が高いためだと思いますか?)
アップデート
Weights 27 9 3 1 0 0 1 3 9 27 27 9 3 1 0 0 1 3 9 27
Shuffle Yes Yes No No
Polymorphism 84 82 27 27
If Else 61 20 17 16
If Else Reverse 20 60 16 17
Switch 21 21 18 18
コードは次のとおりです。
class Animal : AnimalAction
{
public virtual int Bart { get; private set; }
public int Type { get; private set; }
public Animal(int animalType)
{
this.Type = animalType;
}
}
interface AnimalAction
{
int Bart { get; }
}
class Cat : Animal
{
public Cat()
: base(0)
{
}
public override int Bart
{
get
{
return 0;
}
}
}
class Dog : Animal
{
public Dog()
: base(1)
{
}
public override int Bart
{
get
{
return 1;
}
}
}
class Chicken : Animal
{
public Chicken()
: base(2)
{
}
public override int Bart
{
get
{
return 2;
}
}
}
class Rabbit : Animal
{
public Rabbit()
: base(3)
{
}
public override int Bart
{
get
{
return 3;
}
}
}
class Owl : Animal
{
public Owl()
: base(4)
{
}
public override int Bart
{
get
{
return 4;
}
}
}
class SingleDispatch
{
readonly Animal[] Zoo;
int totalSession;
SingleDispatch(int totalSession, int zooSize)
{
this.totalSession = totalSession;
Zoo = new Animal[zooSize];
int[] weights = new int[5] { 0, 1, 2, 3, 4 };
int totalWeights = weights.Sum();
int[] tiers = new int[4];
int accumulated = 0;
for (int i = 0; i < 4; i++)
{
accumulated += weights[i] * zooSize / totalWeights;
tiers[i] = accumulated;
}
for (int i = 0; i < tiers[0]; i++)
{
Animal nextAnimal = new Cat();
Zoo[i] = nextAnimal;
}
for (int i = tiers[0]; i < tiers[1]; i++)
{
Animal nextAnimal = new Dog();
Zoo[i] = nextAnimal;
}
for (int i = tiers[1]; i < tiers[2]; i++)
{
Animal nextAnimal = new Chicken();
Zoo[i] = nextAnimal;
}
for (int i = tiers[2]; i < tiers[3]; i++)
{
Animal nextAnimal = new Rabbit();
Zoo[i] = nextAnimal;
}
for (int i = tiers[3]; i < zooSize; i++)
{
Animal nextAnimal = new Owl();
Zoo[i] = nextAnimal;
}
Zoo.FisherYatesShuffle();
}
public static void Benchmark()
{
List<Tuple<string, double>> result = new List<Tuple<string, double>>();
SingleDispatch myBenchmark = new SingleDispatch(1000, 10000);
result.Add(TestContainer.RunTests(10, myBenchmark.SubClassPoly));
result.Add(TestContainer.RunTests(10, myBenchmark.Ifelse));
result.Add(TestContainer.RunTests(10, myBenchmark.IfelseReverse));
result.Add(TestContainer.RunTests(10, myBenchmark.Switch));
foreach (var item in result)
{
Console.WriteLine("{0,-30}{1:N0}", item.Item1, item.Item2);
}
Console.WriteLine();
}
void SubClassPoly()
{
long sum = 0;
for (int i = 0; i < totalSession; i++)
{
foreach (var myAnimal in Zoo)
{
sum += myAnimal.Bart;
}
}
}
void Ifelse()
{
long sum = 0;
for (int i = 0; i < totalSession; i++)
{
foreach (var myAnimal in Zoo)
{
if (myAnimal.Type == 0)
{
sum += 0;
}
else if (myAnimal.Type == 1)
{
sum += 1;
}
else if (myAnimal.Type == 2)
{
sum += 2;
}
else if (myAnimal.Type == 3)
{
sum += 3;
}
else
{
sum += 4;
}
}
}
}
void IfelseReverse()
{
long sum = 0;
for (int i = 0; i < totalSession; i++)
{
foreach (var myAnimal in Zoo)
{
if (myAnimal.Type == 4)
{
sum += 4;
}
else if (myAnimal.Type == 3)
{
sum += 3;
}
else if (myAnimal.Type == 2)
{
sum += 2;
}
else if (myAnimal.Type == 1)
{
sum += 1;
}
else
{
sum += 0;
}
}
}
}
void Switch()
{
long sum = 0;
for (int i = 0; i < totalSession; i++)
{
foreach (var myAnimal in Zoo)
{
switch (myAnimal.Type)
{
case 0:
sum += 0;
break;
case 1:
sum += 1;
break;
case 2:
sum += 2;
break;
case 3:
sum += 3;
break;
case 4:
sum += 4;
break;
default:
break;
}
}
}
}
}