IT

더블?

lottoking 2020. 10. 10. 10:24

더블? = 더블? + 더블?


이 간단한 C # 코드로 마음을 잃고 있는지 여부를 확인하기 위해 StackOverflow 커뮤니티를 ping하고 싶었습니다.

Windows 7에서 개발 중입니다. .NET 4.0, x64 Debug에서 빌드합니다.

다음 코드가 있습니다.

static void Main()
{
    double? y = 1D;
    double? z = 2D;

    double? x;
    x = y + z;
}

업무하고 끝 중괄호에 중단 점을두면 조사 식 창과 직접 실행 창에서 x = 3을 예상합니다. x = 대신 null입니다.

x86에서 작동하면 작동하는 것입니다. x64 컴파일러에 문제가 있거나 나에게 문제가 있습니까?


Douglas의 대답은 JIT 최적화 데드 코드에 대해 이야기합니다 ( x86 및 x64 컴파일러 모두이 작업을 수행합니다). 그러나 JIT 컴파일러가 데드 코드를 최적화 x하면 Locals 창 에도 즉시 그렇게 할 것입니다. 또한 조사 식 및 직접 실행 창에 액세스를 시도 할 때 "현재는 'x'이름이 없습니다."라는 오류가 표시됩니다. 그것은 당신이 일어나고있는 것이 아닙니다.

지금보고있는 것이 Visual Studio 2010의 버그입니다.

먼저이 문제를 컴퓨터 인 Win7x64 및 VS2012에서 재현했습니다. .NET 4.0 대상의 경우 x닫는 중괄호에서 장치면 3.0D와 가변합니다. 나는 .NET 3.5 오리지날 시도하기로 결정, x그것도 null이 아닌 3.0D로 설정되었습니다.

.NET 4.0 위에 .NET 4.5를 설치했기 때문에 문제를 완벽하게 재현 할 수 없기 때문에 머신을 구동하고 VS2010을 설치했습니다.

여기에서 문제를 재현 할 수 있습니다. 닫는 중괄호에의 중단 점으로 Main조사 식 창 및 지역 창, 나는 모두의 방법 톱 x이었다 null. 이것이 흥미로워지기 시작하는 곳입니다. 대신 v2.0 실행을 대상으로 거기에 널임을 발견했습니다. .NET 2.0 실행 x의 값 이 사용으로 표시 되는 다른 컴퓨터에 동일한 버전의 .NET 2.0 실행이 있기 때문에 그럴 수는 3.0D.

그럼 무슨 일이 일어나고 있습니까? windbg를 파헤친 후 문제를 발견했습니다.

VS2010은 실제로 할당되기 전에 x의 값을 보여줍니다 .

명령 포인터가 x = y + z줄을 지나기 때문에 그것이 어떻게 생겼는지 압니다 . 메서드에 몇 줄의 코드를 추가하여 직접 테스트 할 수 있습니다.

double? y = 1D;
double? z = 2D;

double? x;
x = y + z;

Console.WriteLine(); // Don't reference x here, still leave it as dead code

마지막 중괄호, 지역 주민 및 시계 창 쇼에 중단 점으로 x동일로 3.0D. 당신이 코드를 단계별 경우, 당신은 VS2010-display되지 않는가 것을 알 수 있습니다 x때까지 할당 된 것으로 후에 당신은을 통해 강화했습니다 Console.WriteLine().

이 버그가 Microsoft Connect에보고는 모르겠지만이 코드를 들어보고 싶을 수 있습니다. 확실하지 않은 것이 확실하지 않습니다.


JIT 및 VS2010에서 일어나는 일은 다음과 발생합니다.

원본 코드를 통해 VS 가하는 일과 그 이유를 알 수 있습니다. 또한 x변수가 최적화되지 않는 것을 볼 수 있습니다 (최적화를 사용하여 할 어셈블리를 표시하지 않음).

IL의 지역 변수 정의를 생생하게 보겠습니다.

.locals init (
    [0] valuetype [mscorlib]System.Nullable`1<float64> y,
    [1] valuetype [mscorlib]System.Nullable`1<float64> z,
    [2] valuetype [mscorlib]System.Nullable`1<float64> x,
    [3] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0000,
    [4] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0001,
    [5] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0002)

이것은 디버그 모드에서 출력 출력입니다. Visual Studio는 할당 중에 사용하는 추가 로컬 변수를 정의한 다음 추가 IL 명령을 추가하여 CS * 변수에서 추가의 사용자 정의 로컬 변수로 복사합니다. 다음은 무료 상황을 해당합니다.

// For the line x = y + z
L_0045: ldloca.s CS$0$0000 // earlier, y was stloc.3 (CS$0$0000)
L_0047: call instance !0 [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
L_004c: conv.r8            // Convert to a double
L_004d: ldloca.s CS$0$0001 // earlier, z was stloc.s CS$0$0001
L_004f: call instance !0 [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
L_0054: conv.r8            // Convert to a double 
L_0055: add                // Add them together
L_0056: newobj instance void [mscorlib]System.Nullable`1<float64>::.ctor(!0) // Create a new nulable
L_005b: nop                // NOPs are placed in for debugging purposes
L_005c: stloc.2            // Save the newly created nullable into `x`
L_005d: ret 

WinDbg로 좀 더 심층적 인을 해봅시다 :

VS2010에서 응용 프로그램을 따르고 방법 끝에 중단 점을 애두면 비 침습 모드에서 WinDbg를 쉽게 찾을 수 있습니다.

다음은 Main호출 스택 메서드에 대한 프레임입니다 . 우리는 IP (지시 포인터)에 관심이 있습니다.

0 : 009>! clrstack
OS 스레드 ID : 0x135c (9)
하위 SP IP 호출 사이트
000000001c48dc00 000007ff0017338d ConsoleApplication1.Program.Main (System.String [])
[등등...]

Main메서드 의 기본 기계어 코드를 보면 VS가 실행을 중단 할 때 실행 된 명령을 볼 수 있습니다.

000007ff`00173388 e813fe25f2 mscorlib_ni + 0xd431a0 호출 
           (000007fe`f23d31a0) (System.Nullable`1 [[System.Double, mscorlib]] .. ctor (Double), mdToken : 0000000006001ef2)
**** 000007ff`0017338d cc int 3 ****
000007ff`0017338e 8d8c2490000000 lea ecx, [rsp + 90h]
000007ff`00173395 488b01 mov rax, qword ptr [rcx]
000007ff`00173398 4889842480000000 mov qword ptr [rsp + 80h], rax
000007ff`001733a0 488b4108 mov rax, qword ptr [rcx + 8]
000007ff`001733a4 4889842488000000 mov qword ptr [rsp + 88h], rax
000007ff`001733ac 488d8c2480000000 lea rcx, [rsp + 80h]
000007ff`001733b4 488b01 mov rax, qword ptr [rcx]
000007ff`001733b7 4889442440 mov qword ptr [rsp + 40h], rax
000007ff`001733bc 488b4108 mov rax, qword ptr [rcx + 8]
000007ff`001733c0 4889442448 mov qword ptr [rsp + 48h], rax
000007ff`001733c5 eb00 jmp 000007ff`001733c7
000007ff`001733c7 0f28b424c0000000 movaps xmm6, xmmword ptr [rsp + 0C0h]
000007ff`001733cf 4881c4d8000000 rsp, 0D8h 추가
000007ff`001733d6 c3 ret

우리가에서 가져온 것으로 현재 IP 사용 !clrstack에를 Main, 우리는 그 실행이 지시에 중단되었다 참조 직후 에 호출 System.Nullable<double>의 생성자입니다. ( int 3디버거가 실행을 중지하는 데 사용하는 인터럽트입니다.) 해당 줄을 *로 둘러 쌌 L_0056으며 IL에서 줄을 일치시킬 수도 있습니다 .

뒤에 나오는 x64 어셈블리는 실제로이 어셈블리를 로컬 변수에 할당합니다 x. 우리의 명령어 포인터는 아직 해당 코드를 실행하지 않았기 때문에 VS2010은 x변수가 네이티브 코드에 의해 할당 되기 전에 조기에 중단 됩니다.

편집 : x64에서는 int 3위에서 볼 수 있듯이 명령이 할당 코드 앞에 배치됩니다. x86에서 해당 명령어는 할당 코드 뒤에 배치됩니다. 이것이 VS가 x64에서만 조기에 중단되는 이유를 설명합니다. 이것이 Visual Studio 또는 JIT 컴파일러의 결함인지 말하기는 어렵습니다. 어떤 응용 프로그램이 중단 점 후크를 삽입하는지 잘 모르겠습니다.


x64 JIT 컴파일러는 x86보다 최적화에서 더 공격적인 것으로 알려져 있습니다. ( x86 및 x64 컴파일러가 의미가 다른 코드를 생성하는 경우 " CLR에서 배열 경계 검사 제거 "를 참조 할 수 있습니다 .)

이 경우 x64 컴파일러는 x결코 읽히지 않는 것을 감지 하고 할당을 완전히 제거합니다. 이것은 컴파일러 최적화에서 데드 코드 제거 로 알려져 있습니다. 이러한 일이 발생하지 않도록하려면 할당 바로 뒤에 다음 줄을 추가하십시오.

Console.WriteLine(x);

3get 의 올바른 값을 인쇄 x뿐만 아니라 변수 이를 참조 하는 호출 이후에도 디버거에 올바른 값을 표시 (편집) 하는 것을 관찰 할 수 Console.WriteLine있습니다.

편집 : Christopher Currens는 Visual Studio 2010의 버그를 가리키는 대체 설명을 제공합니다 . 이는 위보다 더 정확할 수 있습니다.

참고 URL : https://stackoverflow.com/questions/13648382/double-double-double