OK, dorzuciłem randoma, aby mi się procek (a konkretnie układ przewidywania skoków) nie nauczył sekwencji wywołań metod wirtualnych :) Mam nadzieję, że rzeczywiście mi się to udało.
Kod obecny:
using System;
using System.Collections.Generic;
using System.Diagnostics;
public class Program
{
interface InterfejsA
{
long metodaA(long x);
}
class KlasaA1 : InterfejsA
{
public long metodaA(long x)
{
return x * 5 + (x >> 3) + 1;
}
}
class KlasaA2 : InterfejsA
{
public long metodaA(long x)
{
return x * 3 + (x >> 5) + 1;
}
}
interface InterfejsB
{
long metodaB(long x);
}
class KlasaB1 : InterfejsB
{
public virtual long metodaB(long x)
{
return x * 5 + (x >> 3) + 2;
}
}
class KlasaB2 : InterfejsB
{
public virtual long metodaB(long x)
{
return x * 3 + (x >> 5) + 2;
}
}
class KlasaB1plus : KlasaB1
{
public override long metodaB(long x)
{
return x * 5 + (x >> 3) + 3;
}
}
class KlasaB2plus : KlasaB2
{
public override long metodaB(long x)
{
return x * 3 + (x >> 5) + 3;
}
}
abstract class KlasaC
{
public abstract long metodaC(long x);
}
class KlasaC1 : KlasaC
{
public override long metodaC(long x)
{
return x * 5 + (x >> 3) + 4;
}
}
class KlasaC2 : KlasaC
{
public override long metodaC(long x)
{
return x * 3 + (x >> 5) + 4;
}
}
class KlasaC1plus : KlasaC1
{
public override long metodaC(long x)
{
return x * 5 + (x >> 3) + 5;
}
}
class KlasaC2plus : KlasaC2
{
public override long metodaC(long x)
{
return x * 3 + (x >> 5) + 5;
}
}
abstract class KlasaD
{
public abstract long metodaD(long x);
}
class KlasaD1 : KlasaD
{
sealed public override long metodaD(long x)
{
return x * 5 + (x >> 3) + 6;
}
}
class KlasaD2 : KlasaD
{
sealed public override long metodaD(long x)
{
return x * 3 + (x >> 5) + 6;
}
}
public static void Main()
{
var listA = new List<InterfejsA>();
var listB = new List<InterfejsB>();
var listBplus = new List<InterfejsB>();
var listC = new List<KlasaC>();
var listCplus = new List<KlasaC>();
var listD = new List<KlasaD>();
var random = new Random(0);
for (int i = 0; i < 12345; i++)
{
if (random.Next() % 2 == 0)
{
listA.Add(new KlasaA1());
listB.Add(new KlasaB1());
listBplus.Add(new KlasaB1plus());
listC.Add(new KlasaC1());
listCplus.Add(new KlasaC1plus());
listD.Add(new KlasaD1());
}
else
{
listA.Add(new KlasaA2());
listB.Add(new KlasaB2());
listBplus.Add(new KlasaB2plus());
listC.Add(new KlasaC2());
listCplus.Add(new KlasaC2plus());
listD.Add(new KlasaD2());
}
}
Stopwatch sw;
long result = -1;
for (int i = 0; i < 3; i++)
{
sw = Stopwatch.StartNew();
for (int n = 0; n < 1234; n++)
for (int j = 0; j < 12345; j++)
{
result += listA[j].metodaA(result);
}
sw.Stop();
Console.WriteLine("AAA Time: " + sw.ElapsedMilliseconds + " ms");
sw = Stopwatch.StartNew();
for (int n = 0; n < 1234; n++)
for (int j = 0; j < 12345; j++)
{
result += listB[j].metodaB(result);
}
sw.Stop();
Console.WriteLine("BBB Time: " + sw.ElapsedMilliseconds + " ms");
sw = Stopwatch.StartNew();
for (int n = 0; n < 1234; n++)
for (int j = 0; j < 12345; j++)
{
result += listBplus[j].metodaB(result);
}
sw.Stop();
Console.WriteLine("BBB plus Time: " + sw.ElapsedMilliseconds + " ms");
sw = Stopwatch.StartNew();
for (int n = 0; n < 1234; n++)
for (int j = 0; j < 12345; j++)
{
result += listC[j].metodaC(result);
}
sw.Stop();
Console.WriteLine("CCC Time: " + sw.ElapsedMilliseconds + " ms");
sw = Stopwatch.StartNew();
for (int n = 0; n < 1234; n++)
for (int j = 0; j < 12345; j++)
{
result += listCplus[j].metodaC(result);
}
sw.Stop();
Console.WriteLine("CCC plus Time: " + sw.ElapsedMilliseconds + " ms");
sw = Stopwatch.StartNew();
for (int n = 0; n < 1234; n++)
for (int j = 0; j < 12345; j++)
{
result += listD[j].metodaD(result);
}
sw.Stop();
Console.WriteLine("DDD Time: " + sw.ElapsedMilliseconds + " ms");
}
Console.WriteLine(result);
}
}
Wyniki na dotnetfiddle (.NET 4.7.2):
AAA Time: 442 ms
BBB Time: 461 ms
BBB plus Time: 450 ms
CCC Time: 439 ms
CCC plus Time: 439 ms
DDD Time: 438 ms
AAA Time: 453 ms
BBB Time: 462 ms
BBB plus Time: 459 ms
CCC Time: 421 ms
CCC plus Time: 431 ms
DDD Time: 409 ms
AAA Time: 465 ms
BBB Time: 467 ms
BBB plus Time: 453 ms
CCC Time: 410 ms
CCC plus Time: 441 ms
DDD Time: 416 ms
-6807050055717496448
Wyniki u mnie (.NET Core 2.1.503 na Ubuntu 16.04.5, Core i5-4670 @ 3.8 GHz):
AAA Time: 239 ms
BBB Time: 236 ms
BBB plus Time: 238 ms
CCC Time: 221 ms
CCC plus Time: 224 ms
DDD Time: 222 ms
AAA Time: 238 ms
BBB Time: 236 ms
BBB plus Time: 237 ms
CCC Time: 221 ms
CCC plus Time: 224 ms
DDD Time: 222 ms
AAA Time: 238 ms
BBB Time: 236 ms
BBB plus Time: 237 ms
CCC Time: 221 ms
CCC plus Time: 223 ms
DDD Time: 222 ms
-6807050055717496448
No i w sumie wnioski pokrywają się z pierwotnymi:
- metody wirtualne z interfejsów mają lekko większy narzut niż metody wirtualne z klas abstrakcyjnych
- głębokość dziedziczenia, ilość nadpisywań metod, modyfikatory virtual, sealed, etc nie mają wpływu na wydajność metod wirtualnych
Na dotnedfiddle jest większy rozrzut czasów niż u mnie. Prawdopodobnie na dotnetfiddle naraz chodzi wiele procesów obciążających procka i zaburzają wyniki.