Mockowanie - interfejs vs virtual

0

Hierarchię C zamieniłem na coś takiego:

    abstract class KlasaC
    {
        public abstract long metodaC(long x);
    }
 
    class KlasaC1 : KlasaC
    {
        sealed public override long metodaC(long x)
        {
            return x * 5 + (x >> 3) + 4;
        }
    }
 
    class KlasaC2 : KlasaC
    {
        sealed public override long metodaC(long x)
        {
            return x * 3 + (x >> 5) + 4;
        }
    }

Oraz wywaliłem odwołania do klas KlasaCXplus. Wyniki na dotnetfiddle (czyli na .NET 4.7.2) się nie zmieniły. Za to .NET Core zwariował. Daje mi takie wyniki:

AAA      Time: 40 ms
BBB      Time: 79 ms
BBB plus Time: 40 ms
CCC      Time: 61 ms
AAA      Time: 40 ms
BBB      Time: 80 ms
BBB plus Time: 40 ms
CCC      Time: 60 ms
AAA      Time: 40 ms
BBB      Time: 80 ms
BBB plus Time: 40 ms
CCC      Time: 62 ms
AAA      Time: 40 ms
BBB      Time: 81 ms
BBB plus Time: 40 ms
CCC      Time: 60 ms
AAA      Time: 40 ms
BBB      Time: 81 ms
BBB plus Time: 40 ms
CCC      Time: 64 ms
AAA      Time: 40 ms
BBB      Time: 81 ms
BBB plus Time: 40 ms
CCC      Time: 62 ms
AAA      Time: 40 ms
BBB      Time: 80 ms
BBB plus Time: 40 ms
CCC      Time: 61 ms
AAA      Time: 40 ms
BBB      Time: 82 ms
BBB plus Time: 40 ms
CCC      Time: 63 ms
AAA      Time: 40 ms
BBB      Time: 80 ms
BBB plus Time: 40 ms
CCC      Time: 63 ms
AAA      Time: 40 ms
BBB      Time: 82 ms
BBB plus Time: 40 ms
CCC      Time: 62 ms
AAA      Time: 40 ms
BBB      Time: 88 ms
BBB plus Time: 40 ms
CCC      Time: 59 ms
AAA      Time: 40 ms
BBB      Time: 81 ms
BBB plus Time: 40 ms
CCC      Time: 63 ms
-5396451711880332420

Ciężko nawet z tego wyciągnąć jakieś sensowne wnioski. Dodatkowo wyrzucenie sealed z klas KlasaCx skutkuje tylko nieznacznym polepszeniem wyników, ale dla klas KlasaBx (nie plus).

Mam wrażenie, że dałem ciała trochę z robieniem mikrobenchmarków i do głosu dochodzi zbyt mocno przewidywanie skoków w procesorze. Hmmm....

0

Mam pytanie jeszze chłopaki ;) . Czy ktos z was bawił się już NET Native ?

0

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.

0
NET 4.7.2
AAA      Time: 224 ms
BBB      Time: 234 ms
BBB plus Time: 243 ms
CCC      Time: 221 ms
CCC plus Time: 211 ms
DDD      Time: 210 ms
AAA      Time: 225 ms
BBB      Time: 219 ms
BBB plus Time: 235 ms
CCC      Time: 220 ms
CCC plus Time: 210 ms
DDD      Time: 212 ms
AAA      Time: 240 ms
BBB      Time: 223 ms
BBB plus Time: 223 ms
CCC      Time: 213 ms
CCC plus Time: 212 ms
DDD      Time: 212 ms
-5365697809158889376
--------------------------------------
Net Core
AAA      Time: 231 ms
BBB      Time: 222 ms
BBB plus Time: 225 ms
CCC      Time: 204 ms
CCC plus Time: 205 ms
DDD      Time: 207 ms
AAA      Time: 218 ms
BBB      Time: 228 ms
BBB plus Time: 227 ms
CCC      Time: 210 ms
CCC plus Time: 215 ms
DDD      Time: 219 ms
AAA      Time: 220 ms
BBB      Time: 216 ms
BBB plus Time: 216 ms
CCC      Time: 205 ms
CCC plus Time: 207 ms
DDD      Time: 206 ms
4061248913899638804

1 użytkowników online, w tym zalogowanych: 0, gości: 1