2009-12-22 2 views
19

this answer (성능에 대해 정수 곱하기/나누기보다 비트 시프트 연산자를 사용하는 것이 좋습니다)에 대한 의견에서이 코드가 실제로 더 빠를 지 여부를 묻습니다. 내 마음의 뒤쪽에 일부 수준에서 수준, 뭔가가 작동 할 정도로 영리하다는 생각은 그 >> 1/ 2 같은 작업입니다. 그러나, 나는 이것이 실제로 사실인지 궁금하다. 그렇다면 그것이 어떤 수준으로 발생하는지 궁금하다.주어진 C#/CIL에 대해 theJITter가 생성 한 원시 코드를 볼 수있는 방법이 있습니까?

IL_0000: ldarg.0 
    IL_0001: ldc.i4.2 
    IL_0002: div 
    IL_0003: ret 
} // end of method Program::Divider 

IL_0000: ldarg.0 
    IL_0001: ldc.i4.1 
    IL_0002: shr 
    IL_0003: ret 
} // end of method Program::Shifter 

대 그래서 C# 컴파일러는 div 방출된다

테스트 프로그램은 각각 분할하고 이들 인자를 전환하는 두 가지 방법에 대해 (ON optimize)와 다음 비교 CIL을 생산 또는 shr 명령을 따르지 않아도됩니다. 이제는 JITter가 생성하는 실제 x86 어셈블러를보고 싶지만이 작업을 수행하는 방법을 모릅니다. 심지어 가능할까요? 그것은 그 디버거 옵션에 대한 주요 정보가 포함되어있어

편집는 nobugz에서 하나를 수락, 답변을

조사 결과

감사를 추가 할 수 있습니다. 무엇 결국 나를 위해 일한 것은 :

  • 스위치는
  • 같은 장소 ( JIT 최적화 할 수 있도록 우리가 원하는 즉), Tools | Options | Debugger에서 구성
  • 을 해제 스위치 오프 모듈로드에 JIT 최적화를 억제 '하는,
  • 어셈블리를 제작 해보세요 Debugger.Break() 문을 착용 할 것 (즉, 우리가 모든 코드를 디버깅 할) '내 코드 만 사용 스위치 오프
  • 한 .exe를 실행하고 휴식 할 때, 디버그 인스턴스 VS 기존를 사용
  • 이제 분해 윈도우가 당신에게

결과는 적어도 말을 계몽했다 실행될 것 실제 86를 보여줍니다 - 실제로 JITter가 산술 연산을 수행 할 수 있습니다. 디스 어셈블리 창에서 편집 된 샘플이 있습니다. 다양한 -Shifter 메서드는 >>을 사용하여 2의 제곱으로 나눕니다. 다양한 -Divider 방법은 방법은 인라인되지 않은 두 정적으로 2 분주 /

Console.WriteLine(string.Format(" 
    {0} 
    shift-divided by 2: {1} 
    divide-divided by 2: {2}", 
    60, TwoShifter(60), TwoDivider(60))); 

00000026 mov   dword ptr [edx+4],3Ch 
... 
0000003b mov   dword ptr [edx+4],1Eh 
... 
00000057 mov   dword ptr [esi+4],1Eh 

를 사용하여 정수에 의해 분할하지만, 실제 계산은와

Console.WriteLine(string.Format(" 
    {0} 
    divide-divided by 3: {1}", 
    60, ThreeDivider(60))); 

00000085 mov   dword ptr [esi+4],3Ch 
... 
000000a0 mov   dword ptr [esi+4],14h 

같은 지터에 의해 수행되었다 정적으로 3으로 나누기.

Console.WriteLine(string.Format(" 
    {0} 
    shift-divided by 4: {1} 
    divide-divided by 4 {2}", 
    60, FourShifter(60), FourDivider(60))); 

000000ce mov   dword ptr [esi+4],3Ch 
... 
000000e3 mov   dword ptr [edx+4],0Fh 
... 
000000ff mov   dword ptr [esi+4],0Fh 

및 4로 정적으로 나눕니다.

최고 : 그런 다음 인라인 및 것

Console.WriteLine(string.Format(" 
    {0} 
    n-divided by 2: {1} 
    n-divided by 3: {2} 
    n-divided by 4: {3}", 
    60, Divider(60, 2), Divider(60, 3), Divider(60, 4))); 

0000013e mov   dword ptr [esi+4],3Ch 
... 
0000015b mov   dword ptr [esi+4],1Eh 
... 
0000017b mov   dword ptr [esi+4],14h 
... 
0000019b mov   dword ptr [edi+4],0Fh 

이 모든 정적 분열을 계산!

결과가 정적이 아닌 경우에는 어떻게해야합니까? 콘솔에서 정수를 읽는 코드를 추가했습니다. 이것은 그의 부문에 대한 생산하는 것입니다 :

Console.WriteLine(string.Format(" 
    {0} 
    shift-divided by 2: {1} 
    divide-divided by 2: {2}", 
    i, TwoShifter(i), TwoDivider(i))); 

00000211 sar   eax,1 
... 
00000230 sar   eax,1 

는 그래서 CIL 다른 임에도 불구하고, 지터는 2로 나누어 것은 1.

Console.WriteLine(string.Format(" 
    {0} 
    divide-divided by 3: {1}", i, ThreeDivider(i))); 

00000283 IDIV EAX에 의해 오른쪽 이동 인 것을 알고, ECX

그리고 당신이 3

Console.WriteLine(string.Format(" 
    {0} 
    shift-divided by 4: {1} 
    divide-divided by 4 {2}", 
    i, FourShifter(i), FourDivider(i))); 

000002c5 sar   eax,2 
... 
000002ec sar   eax,2 

에 의해 분할 분할해야 그리고 알것 알고 WS 4에 의한 분할은 마지막 2

에 의해 오른쪽 이동은 (최고 다시!)입니다 그것은 정적 또는에 따라 방법을 인라인과 일을 할 수있는 최선의 방법을 일했다

Console.WriteLine(string.Format(" 
    {0} 
    n-divided by 2: {1} 
    n-divided by 3: {2} 
    n-divided by 4: {3}", 
    i, Divider(i, 2), Divider(i, 3), Divider(i, 4))); 

00000345 sar   eax,1 
... 
00000370 idiv  eax,ecx 
... 
00000395 sar   esi,2 

사용 가능한 인수. 좋은.


는 그래서 그래, 어딘가 C# 및 86 사이의 스택, 뭔가 >> 1/ 2이 동일한 지 운동 할만큼 영리입니다. C# 컴파일러, JITter 및 CLR을 함께 추가하면 이 훨씬 더 깨끗해집니다. 내 생각에 겸손한 프로그래머로 시도 할 수있는 작은 트릭보다 r이

입니다.
+0

당신은 우리 모두에게 좋은 결과를 게시 할 수 있습니까? 고마워 :) – flesh

답변

8

디버거를 구성 할 때까지 의미있는 결과를 얻지 못할 수 있습니다. 도구 + 옵션, 디버깅, 일반, "모듈로드시 JIT 최적화 억제"를 해제하십시오. 해제 모드 구성으로 전환하십시오. 샘플 조각 :

static void Main(string[] args) { 
    int value = 4; 
    int result = divideby2(value); 
} 

당신은 분해는 다음과 같습니다 권리 경우을하고는 :

00000000 ret 

당신은 식을 강제로 JIT 최적화 평가하는 바보해야합니다. 콘솔 사용.WriteLine (변수)가 도움이 될 수 있습니다. 그러면 다음과 같은 내용을보아야합니다.

0000000a mov   edx,2 
0000000f mov   eax,dword ptr [ecx] 
00000011 call  dword ptr [eax+000000BCh] 

그래, 컴파일 타임에 결과를 평가합니다. 꽤 잘 작동하지 않습니다.

+0

"모듈로드시 JIT 최적화를 억제하십시오"가 켜져있는 경우 왜 의미있는 결과를 얻지 못합니까? 내 해석에 따르면이 기능을 사용하면 최적화 된 "릴리스"원시 코드를 디버깅하는 것입니다. – Justin

3

예. Visual Studio에는 역 어셈블러가 내장되어 있습니다. 메뉴 모음에 명령을 추가해야합니다. 엑스트라/사용자 정의/명령 (실제로 영어 버전에서 그런 식으로 호출되는지는 모르겠다)으로 이동하여 메뉴 막대 어딘가에 디버거가 아닌 Dissassembly 명령을 추가하십시오.

그런 다음 프로그램에 중단 점을 설정하고 중단되면이 Disassembly 명령을 클릭하십시오. VS가 분해 된 기계 코드를 보여줍니다. 분배기-방법

예 출력 : (디버깅하는 경우에만 동안)

public static int Divider(int intArg) 
    { 
00000000 push  ebp 
00000001 mov   ebp,esp 
00000003 push  edi 
00000004 push  esi 
00000005 push  ebx 
00000006 sub   esp,34h 
00000009 mov   esi,ecx 
0000000b lea   edi,[ebp-38h] 
0000000e mov   ecx,0Bh 
00000013 xor   eax,eax 
00000015 rep stos dword ptr es:[edi] 
00000017 mov   ecx,esi 
00000019 xor   eax,eax 
0000001b mov   dword ptr [ebp-1Ch],eax 
0000001e mov   dword ptr [ebp-3Ch],ecx 
00000021 cmp   dword ptr ds:[00469240h],0 
00000028 je   0000002F 
0000002a call  6BA09D91 
0000002f xor   edx,edx 
00000031 mov   dword ptr [ebp-40h],edx 
00000034 nop    
    return intArg/2; 
00000035 mov   eax,dword ptr [ebp-3Ch] 
00000038 sar   eax,1 
0000003a jns   0000003F 
0000003c adc   eax,0 
0000003f mov   dword ptr [ebp-40h],eax 
00000042 nop    
00000043 jmp   00000045 
    } 
+2

그 명령을 VS에 추가 할 필요가 없으며, Debug-> Windows 메뉴에있다. –

2

은 그냥 디버그에서 클릭 디버깅하는 동안 - 윈도우 - 분해하거나 해당 단축키를 누르면 Ctrl + Alt + 디.

관련 문제