5

EDITWidok demontażu kodu C# 64-bit Release jest o 75% dłuższy niż 32-bitowy kod debugowania?

Testowałem uwalnianie w 32 bitów, a kod był zwarty. Dlatego poniższy problem jest 64-bitowy.


Używam VS 2012 RC. Debugowanie jest 32-bitowe, a Release - 64-bitowe. Poniżej jest debug następnie zwolnić demontaż linii kodu:

  crc = (crc >> 8)^crcTable[((val & 0x0000ff00) >> 8)^crc & 0xff]; 
0000006f mov   eax,dword ptr [ebp-40h] 
00000072 shr   eax,8 
00000075 mov   edx,dword ptr [ebp-3Ch] 
00000078 mov   ecx,0FF00h 
0000007d and   edx,ecx 
0000007f shr   edx,8 
00000082 mov   ecx,dword ptr [ebp-40h] 
00000085 mov   ebx,0FFh 
0000008a and   ecx,ebx 
0000008c xor   edx,ecx 
0000008e mov   ecx,dword ptr ds:[03387F38h] 
00000094 cmp   edx,dword ptr [ecx+4] 
00000097 jb   0000009E 
00000099 call  6F54F5EC 
0000009e xor   eax,dword ptr [ecx+edx*4+8] 
000000a2 mov   dword ptr [ebp-40h],eax 
----------------------------------------------------------------------------- 
     crc = (crc >> 8)^crcTable[((val & 0x0000ff00) >> 8)^crc & 0xff]; 
000000a5 mov   eax,dword ptr [rsp+20h] 
000000a9 shr   eax,8 
000000ac mov   dword ptr [rsp+38h],eax 
000000b0 mov   rdx,124DEE68h 
000000ba mov   rdx,qword ptr [rdx] 
000000bd mov   eax,dword ptr [rsp+00000090h] 
000000c4 and   eax,0FF00h 
000000c9 shr   eax,8 
000000cc mov   ecx,dword ptr [rsp+20h] 
000000d0 and   ecx,0FFh 
000000d6 xor   eax,ecx 
000000d8 mov   ecx,eax 
000000da mov   qword ptr [rsp+40h],rdx 
000000df mov   rax,qword ptr [rsp+40h] 
000000e4 mov   rax,qword ptr [rax+8] 
000000e8 mov   qword ptr [rsp+48h],rcx 
000000ed cmp   qword ptr [rsp+48h],rax 
000000f2 jae   0000000000000100 
000000f4 mov   rax,qword ptr [rsp+48h] 
000000f9 mov   qword ptr [rsp+48h],rax 
000000fe jmp   0000000000000105 
00000100 call  000000005FA5D364 
00000105 mov   rax,qword ptr [rsp+40h] 
0000010a mov   rcx,qword ptr [rsp+48h] 
0000010f mov   ecx,dword ptr [rax+rcx*4+10h] 
00000113 mov   eax,dword ptr [rsp+38h] 
00000117 xor   eax,ecx 
00000119 mov   dword ptr [rsp+20h],eax 

Co jest cały dodatkowy kod w wersji 64 bitowej robi? Testuje na co? Nie testowałem tego, ale 32-bitowy kod powinien działać znacznie szybciej.

EDIT

Cała funkcja:

public static uint CRC32(uint val) 
{ 
    uint crc = 0xffffffff; 

    crc = (crc >> 8)^crcTable[(val & 0x000000ff)^crc & 0xff]; 
    crc = (crc >> 8)^crcTable[((val & 0x0000ff00) >> 8)^crc & 0xff]; 
    crc = (crc >> 8)^crcTable[((val & 0x00ff0000) >> 16)^crc & 0xff]; 
    crc = (crc >> 8)^crcTable[(val >> 24)^crc & 0xff]; 

    // flip bits 
    return (crc^0xffffffff); 
} 
+0

Pomoże to zobaczyć całą funkcję, a nie tylko jej pojedynczą linię. –

+3

Próbujesz porównać różnice, gdy jeden jest zbudowany do debugowania, a inne wydanie jest nieprawidłowe. Porównaj co najmniej ten sam gatunek (debug/debug lub release/release). –

+0

@KenWhite Zgadzam się. Ale to fakt, że debugowanie jest bardziej kompaktowe, zwróciło moją uwagę, plus fakt dodania dodatkowych kontroli. – IamIC

Odpowiedz

8

Podejrzewam, że używasz "Przejdź do demontażu" podczas debugowania kompilacji wydania, aby uzyskać kod zespołu.

Po przejściu do Narzędzia -> Opcje, debugowanie, Ogólne i wyłączenie "Wyłącz optymalizację JIT przy ładowaniu modułu" Mam listę montażową x64 bez sprawdzania błędów.

Wygląda na to, że domyślnie nawet w trybie zwolnienia kod nie jest zoptymalizowany, jeśli dołączony jest debugger. Miej to na uwadze podczas próby porównawczej kodu.

PS: Benchmarking pokazuje x64 nieco szybciej niż x86, 4,3 kontra 4,8 sekundy dla 1 miliarda wywołań funkcji.

Edytuj: Punkty przerwania nadal działały dla mnie, inaczej nie byłbym w stanie zobaczyć demontażu po odznaczeniu.Twój przykład linia z góry wygląda następująco (VS 2012 RC):

crc = (crc >> 8)^crcTable[((val & 0x0000ff00) >> 8)^crc & 0xff]; 
00000030 mov   r11d,eax 
00000033 shr   r11d,8 
00000037 mov   ecx,edx 
00000039 and   ecx,0FF00h 
0000003f shr   ecx,8 
00000042 movzx  eax,al 
00000045 xor   ecx,eax 
00000047 mov   eax,ecx 
00000049 cmp   rax,r9 
0000004c jae   00000000000000A4 
0000004e mov   eax,dword ptr [r8+rax*4+10h] 
00000053 xor   r11d,eax 
+1

Tak, masz rację. Wygląda więc na to, że w 64-bitowym trybie debugowania wydania dodano sprawdzenia, ale nie w trybie 32-bitowym. Dlatego faktyczne wydanie będzie "czyste". – IamIC

+0

Oczywiście przy wyłączonej opcji "Przejdź do demontażu" punkty przerwania przestają działać, więc nie mogę sprawdzić. Ale wierzę, że odpowiedziałeś na to pytanie, wsparte przez twoje testy porównawcze. – IamIC

+0

Nie mam pojęcia, jak uzyskać punkty przerwania do pracy i można zobaczyć demontaż. Czy korzystasz z wersji 64-bitowej? Który VS? – IamIC

1

Patrząc na kod ten jest związany z błędem kontroli dostępu crcTable. Robi twoje granice, zanim zacznie wbijać się w tablicę.

W kodzie 32-bitowym widzisz ten

0000008e mov   ecx,dword ptr ds:[03387F38h] 
.... 
0000009e xor   eax,dword ptr [ecx+edx*4+8] 

W tym przypadku jest to ładuje adres bazowy tablicy z 03387F38h a następnie przy użyciu standardowej arytmetyki wskaźników, aby uzyskać dostęp do poprawnego wpisu.

W 64-bitowym kodzie wydaje się to bardziej skomplikowane.

000000b0 mov   rdx,124DEE68h 
000000ba mov   rdx,qword ptr [rdx] 

Ten ładuje adres do RDX zarejestrować

000000da mov   qword ptr [rsp+40h],rdx 
... 
00000105 mov   rax,qword ptr [rsp+40h] 
0000010a mov   rcx,qword ptr [rsp+48h] 
0000010f mov   ecx,dword ptr [rax+rcx*4+10h] 

Przenosi adres na stosie, a potem na to przenosi go do rejestru RAX i wykonuje tę samą pracę wskaźnik dostępu do tablicy .

Prawie wszystko pomiędzy 000000da a 00000100/00000105 wydaje się być kodem walidacyjnym. Reszta kodu mapuje się całkiem dobrze między 64-bitowym a 32-bitowym kodem, z mniej agresywnym wykorzystaniem rejestru w 64-bitowym kodzie.

+0

Dokonałem edycji: przetestowałem tryb zwolnienia w 32-bitowym, a wynik był identyczny z 32-bitowym debugowaniem: krótki. Jest to z pewnością 64-bitowy problem. – IamIC

+0

@IanC Ach, dobrze wiedzieć. Zaktualizowałem odpowiedź, aby to uwzględnić. –

+0

To jest naprawdę nieoczekiwane. Budowanie w trybie 32-bitowym wyraźnie spowoduje szybszy kod. Teraz zastanawiam się, czy kod 64-bitowy jest wypełniony sprawdzaniem granic tablicy i złożonym kodem dostępu. – IamIC

0

exp^crc & 0xFF jest kompilowany jako exp^(CR & 0xFF):

00000082 mov   ecx,dword ptr [ebp-40h] 
00000085 mov   ebx,0FFh 
0000008a and   ecx,ebx 
0000008c xor   edx,ecx 

trzeba napisać wyrażenie jako ?

(exp^crc) & 0xff 

Wersja 64-bitowa jest zdecydowanie mniej zoptymalizowana niż wersja 32-bitowa. CLR ma dwie oddzielne implementacje kompilatora JIT.

Ponadto, jeśli ocena jest krytyczna, użyj niebezpiecznego kodu do usunięcia sprawdzania ograniczeń.

+0

Wygląda na to, że pełne wydanie w 64-bitowym jest 12% szybsze niż 32-bitowe. Odpowiedź brzmi: załączony debugger uniemożliwił optymalizację w 64 bitach, ale nie 32. Po prostu tak, jak jest skonfigurowany. Kod jest standardowym CRC. – IamIC