Niska wydajność szablonu funkcji

Niska wydajność szablonu funkcji
Kamil9132
  • Rejestracja:prawie 14 lat
  • Ostatnio:prawie 4 lata
  • Postów:173
0

Witam.
Czy znacznie dłuższy czas wykonywania funkcji z parametrem template jest prawidłowym zachowaniem w c#?
A:

Kopiuj
		private void TestA(byte[] data, byte a, byte b)
		{
			for (int index = 0; index < data.Length; ++index)
			{
				if (data[index] == a)
				{
					data[index] = b;
				}
			}
		}

B:

Kopiuj
		private void TestB(byte[] data, byte a, byte b)
		{
			for (int index = 0; index < data.Length; ++index)
			{
				if (data[index].Equals(a))
				{
					data[index] = b;
				}
			}
		}

C

Kopiuj
		private void TestC<T>(T[] data, T a, T b)
		{
			for (int index = 0; index < data.Length; ++index)
			{
				if (data[index].Equals(a))
				{
					data[index] = b;
				}
			}
		}

Wywołanie:

Kopiuj
			TestA(data, 0, 1);
			timer.Restart();

			TestA(data, 0, 1);
			timer.LogTime("TestA");

			TestB(data, 0, 1);
			timer.LogTime("TestB");

			TestC(data, (byte) 0, (byte) 1);
			timer.LogTime("TestC");

Timer:

Kopiuj
	class Timer
	{
		private Stopwatch stopwatch;

		public Timer()
		{
			stopwatch = new Stopwatch();
			stopwatch.Start();
		}

		public void LogTime(string messagePrefix, bool restart = false)
		{
			Console.WriteLine(messagePrefix + ": {0}", stopwatch.ElapsedMilliseconds);

			if (restart)
			{
				stopwatch.Restart();
			}
		}

		public void Restart()
		{
			stopwatch.Restart();
		}
	}

Spodziewałem się różnicy czasów wykonywania funkcji w zakresie błędu pomiarowego, jednak wyniki są następujące:

Kopiuj
TestA: 7
TestB: 15
TestC: 76

Różnica pomiędzy kodami funkcji jest dobrze widoczna w wersji disassembled:
A:

Kopiuj
		{
001E22A8  push        ebp  
001E22A9  mov         ebp,esp  
001E22AB  push        edi  
001E22AC  push        esi  
001E22AD  push        ebx  
001E22AE  sub         esp,40h  
001E22B1  mov         esi,ecx  
001E22B3  lea         edi,[ebp-38h]  
001E22B6  mov         ecx,0Bh  
001E22BB  xor         eax,eax  
001E22BD  rep stos    dword ptr es:[edi]  
001E22BF  mov         ecx,esi  
001E22C1  mov         dword ptr [ebp-3Ch],ecx  
001E22C4  mov         dword ptr [ebp-40h],edx  
001E22C7  cmp         dword ptr ds:[144268h],0  
001E22CE  je          001E22D5  
001E22D0  call        7194D3B5  
001E22D5  xor         edx,edx  
001E22D7  mov         dword ptr [ebp-44h],edx  
001E22DA  mov         dword ptr [ebp-4Ch],0  
001E22E1  mov         dword ptr [ebp-48h],0  
001E22E8  nop  
			for (int index = 0; index < data.Length; ++index)
001E22E9  xor         edx,edx  
			for (int index = 0; index < data.Length; ++index)
001E22EB  mov         dword ptr [ebp-44h],edx  
001E22EE  nop  
001E22EF  jmp         001E2339  
			{
001E22F1  nop  
				if (data[index] == a)
001E22F2  mov         eax,dword ptr [ebp-44h]  
001E22F5  mov         edx,dword ptr [ebp-40h]  
001E22F8  cmp         eax,dword ptr [edx+4]  
001E22FB  jb          001E2302  
001E22FD  call        7194C64A  
001E2302  movzx       eax,byte ptr [edx+eax+8]  
001E2307  movzx       edx,byte ptr [ebp+0Ch]  
001E230B  cmp         eax,edx  
001E230D  sete        al  
001E2310  movzx       eax,al  
001E2313  mov         dword ptr [ebp-48h],eax  
001E2316  cmp         dword ptr [ebp-48h],0  
001E231A  je          001E2335  
				{
001E231C  nop  
					data[index] = b;
001E231D  mov         eax,dword ptr [ebp-44h]  
001E2320  mov         edx,dword ptr [ebp-40h]  
001E2323  cmp         eax,dword ptr [edx+4]  
001E2326  jb          001E232D  
001E2328  call        7194C64A  
001E232D  mov         ecx,dword ptr [ebp+8]  
001E2330  mov         byte ptr [edx+eax+8],cl  
				}
001E2334  nop  
			}
001E2335  nop  
			for (int index = 0; index < data.Length; ++index)
001E2336  inc         dword ptr [ebp-44h]  
001E2339  mov         eax,dword ptr [ebp-44h]  
001E233C  mov         edx,dword ptr [ebp-40h]  
001E233F  cmp         eax,dword ptr [edx+4]  
001E2342  setl        al  
001E2345  movzx       eax,al  
001E2348  mov         dword ptr [ebp-4Ch],eax  
001E234B  cmp         dword ptr [ebp-4Ch],0  
001E234F  jne         001E22F1  
		}
001E2351  nop  
001E2352  lea         esp,[ebp-0Ch]  
001E2355  pop         ebx  
001E2356  pop         esi  
001E2357  pop         edi  
001E2358  pop         ebp  
001E2359  ret         8  

B:

Kopiuj
		{
001E2C60  push        ebp  
001E2C61  mov         ebp,esp  
001E2C63  push        edi  
001E2C64  push        esi  
001E2C65  push        ebx  
001E2C66  sub         esp,44h  
001E2C69  mov         esi,ecx  
001E2C6B  lea         edi,[ebp-50h]  
001E2C6E  mov         ecx,11h  
001E2C73  xor         eax,eax  
001E2C75  rep stos    dword ptr es:[edi]  
001E2C77  mov         ecx,esi  
001E2C79  mov         dword ptr [ebp-3Ch],ecx  
001E2C7C  mov         dword ptr [ebp-40h],edx  
001E2C7F  cmp         dword ptr ds:[144268h],0  
001E2C86  je          001E2C8D  
001E2C88  call        7194D3B5  
001E2C8D  xor         edx,edx  
001E2C8F  mov         dword ptr [ebp-44h],edx  
001E2C92  mov         dword ptr [ebp-4Ch],0  
001E2C99  mov         dword ptr [ebp-48h],0  
001E2CA0  nop  
			for (int index = 0; index < data.Length; ++index)
001E2CA1  xor         edx,edx  
			for (int index = 0; index < data.Length; ++index)
001E2CA3  mov         dword ptr [ebp-44h],edx  
001E2CA6  nop  
001E2CA7  jmp         001E2CF4  
			{
001E2CA9  nop  
				if (data[index].Equals(a))
001E2CAA  mov         eax,dword ptr [ebp-44h]  
001E2CAD  mov         edx,dword ptr [ebp-40h]  
001E2CB0  cmp         eax,dword ptr [edx+4]  
001E2CB3  jb          001E2CBA  
001E2CB5  call        7194C64A  
001E2CBA  lea         ecx,[edx+eax+8]  
001E2CBE  movzx       edx,byte ptr [ebp+0Ch]  
001E2CC2  call        70A9E740  
001E2CC7  mov         dword ptr [ebp-50h],eax  
001E2CCA  movzx       eax,byte ptr [ebp-50h]  
001E2CCE  mov         dword ptr [ebp-48h],eax  
001E2CD1  cmp         dword ptr [ebp-48h],0  
001E2CD5  je          001E2CF0  
				{
001E2CD7  nop  
					data[index] = b;
001E2CD8  mov         eax,dword ptr [ebp-44h]  
001E2CDB  mov         edx,dword ptr [ebp-40h]  
001E2CDE  cmp         eax,dword ptr [edx+4]  
001E2CE1  jb          001E2CE8  
001E2CE3  call        7194C64A  
001E2CE8  mov         ecx,dword ptr [ebp+8]  
001E2CEB  mov         byte ptr [edx+eax+8],cl  
				}
001E2CEF  nop  
			}
001E2CF0  nop  
			for (int index = 0; index < data.Length; ++index)
001E2CF1  inc         dword ptr [ebp-44h]  
001E2CF4  mov         eax,dword ptr [ebp-44h]  
001E2CF7  mov         edx,dword ptr [ebp-40h]  
001E2CFA  cmp         eax,dword ptr [edx+4]  
001E2CFD  setl        al  
001E2D00  movzx       eax,al  
001E2D03  mov         dword ptr [ebp-4Ch],eax  
001E2D06  cmp         dword ptr [ebp-4Ch],0  
001E2D0A  jne         001E2CA9  
		}
001E2D0C  nop  
001E2D0D  lea         esp,[ebp-0Ch]  
001E2D10  pop         ebx  
001E2D11  pop         esi  
001E2D12  pop         edi  
001E2D13  pop         ebp  
001E2D14  ret         8  

C:

Kopiuj
		{
001E2D28  push        ebp  
001E2D29  mov         ebp,esp  
001E2D2B  push        edi  
001E2D2C  push        esi  
001E2D2D  push        ebx  
001E2D2E  sub         esp,50h  
001E2D31  mov         esi,ecx  
001E2D33  lea         edi,[ebp-5Ch]  
001E2D36  mov         ecx,14h  
001E2D3B  xor         eax,eax  
001E2D3D  rep stos    dword ptr es:[edi]  
001E2D3F  mov         ecx,esi  
001E2D41  mov         dword ptr [ebp-3Ch],ecx  
001E2D44  mov         dword ptr [ebp-40h],edx  
001E2D47  cmp         dword ptr ds:[144268h],0  
001E2D4E  je          001E2D55  
001E2D50  call        7194D3B5  
001E2D55  xor         edx,edx  
001E2D57  mov         dword ptr [ebp-44h],edx  
001E2D5A  mov         dword ptr [ebp-4Ch],0  
001E2D61  mov         dword ptr [ebp-48h],0  
001E2D68  nop  
			for (int index = 0; index < data.Length; ++index)
001E2D69  xor         edx,edx  
			for (int index = 0; index < data.Length; ++index)
001E2D6B  mov         dword ptr [ebp-44h],edx  
001E2D6E  nop  
001E2D6F  jmp         001E2DDD  
			{
001E2D71  nop  
				if (data[index].Equals(a))
001E2D72  mov         ecx,7049FDD8h  
001E2D77  call        001330F4  
001E2D7C  mov         dword ptr [ebp-50h],eax  
001E2D7F  mov         eax,dword ptr [ebp-44h]  
001E2D82  mov         edx,dword ptr [ebp-40h]  
001E2D85  cmp         eax,dword ptr [edx+4]  
001E2D88  jb          001E2D8F  
001E2D8A  call        7194C64A  
001E2D8F  lea         eax,[edx+eax+8]  
001E2D93  mov         dword ptr [ebp-58h],eax  
001E2D96  mov         eax,dword ptr [ebp-50h]  
001E2D99  mov         edx,dword ptr [ebp+0Ch]  
001E2D9C  mov         byte ptr [eax+4],dl  
001E2D9F  mov         eax,dword ptr [ebp-50h]  
001E2DA2  mov         dword ptr [ebp-5Ch],eax  
001E2DA5  mov         ecx,dword ptr [ebp-58h]  
001E2DA8  mov         edx,dword ptr [ebp-5Ch]  
001E2DAB  call        70A9E6F4  
001E2DB0  mov         dword ptr [ebp-54h],eax  
001E2DB3  movzx       eax,byte ptr [ebp-54h]  
001E2DB7  mov         dword ptr [ebp-48h],eax  
001E2DBA  cmp         dword ptr [ebp-48h],0  
001E2DBE  je          001E2DD9  
				{
001E2DC0  nop  
					data[index] = b;
001E2DC1  mov         eax,dword ptr [ebp-44h]  
001E2DC4  mov         edx,dword ptr [ebp-40h]  
001E2DC7  cmp         eax,dword ptr [edx+4]  
001E2DCA  jb          001E2DD1  
001E2DCC  call        7194C64A  
001E2DD1  mov         ecx,dword ptr [ebp+8]  
001E2DD4  mov         byte ptr [edx+eax+8],cl  
				}
001E2DD8  nop  
			}
001E2DD9  nop  
			for (int index = 0; index < data.Length; ++index)
001E2DDA  inc         dword ptr [ebp-44h]  
001E2DDD  mov         eax,dword ptr [ebp-44h]  
001E2DE0  mov         edx,dword ptr [ebp-40h]  
001E2DE3  cmp         eax,dword ptr [edx+4]  
001E2DE6  setl        al  
001E2DE9  movzx       eax,al  
001E2DEC  mov         dword ptr [ebp-4Ch],eax  
001E2DEF  cmp         dword ptr [ebp-4Ch],0  
001E2DF3  jne         001E2D71  
		}
001E2DF9  nop  
001E2DFA  lea         esp,[ebp-0Ch]  
001E2DFD  pop         ebx  
001E2DFE  pop         esi  
001E2DFF  pop         edi  
001E2E00  pop         ebp  
001E2E01  ret         8  

Różnice:
A vs B:
https://www.diffchecker.com/MLvvg635

B vs C:
https://www.diffchecker.com/Yag3Tfx8

Środowisko: Microsoft Visual Studio 2017 Community
Target Framework: .NET Framework 4.5.2
Wyniki czasowe pochodzą z wersji Release, kod disassembled z wersji Debug.
Funkcje są maksymalnie uproszczone w celu pokazania różnic, właściwy kod jest znacznie dłuższy, jednak różnice w czasie wykonywania sprawdzają się do zaprezentowanego problemu.

W obecnej sytuacji najprostszym rozwiązaniem jest napisanie funkcji bez używania szablonu, jednak w przypadku użycia jej z innymi typami wymagane byłoby jej kopiowanie, co nie jest dobrym rozwiązaniem.
Czy istnieje inne rozwiązanie?

edytowany 2x, ostatnio: Kamil9132
Pixello
  • Rejestracja:prawie 10 lat
  • Ostatnio:4 miesiące
  • Lokalizacja:Podkarpacie
  • Postów:448
2
  1. To nie jest szablon tylko metoda generyczna. To że coś się nazywa tak w c++ to nie znaczy że w c# też.
  2. Genreryki muszą zostać wyrenderowane przez clr'a. Powtórz sobie test 1000 i różnica nie będzie tak widoczna. W C# wiele rzeczy w przypadku cold run wyjdzie że jest wolne.
  3. W C# generyki są renderowane w locie, w c++ (z tego co sie orentuje) w czasie kompilacji.
  4. Jaki ma sens ma pokazywanie asma w debug a czasu z 'release'? Większość optymalizacji jest robiona w runtime wlasnie. IL z release potrafi być nawet 2/3 razy krótszy z 'release', a do tego dojdą optymalizacje jita właśnie.
  5. .net 4.5.2? Co to za archeologia?

W obecnej sytuacji najprostszym rozwiązaniem jest napisanie funkcji bez używania szablonu, jednak w przypadku użycia jej z innymi typami wymagane byłoby jej kopiowanie, co nie jest dobrym rozwiązaniem.

Dokładnie tak to jit robi żebyś nie musiał ręcznie.

edytowany 5x, ostatnio: Pixello
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)