C # JIT 컴파일 및 .NET


87

JIT 컴파일러가 어떻게 작동하는지에 대한 세부 사항에 대해 약간 혼란 스러웠습니다. C #이 IL로 컴파일된다는 것을 알고 있습니다. 처음 실행하면 JIT입니다. 이것이 네이티브 코드로 번역되는 것을 포함합니까? .NET 런타임 (가상 머신)이 JIT 코드와 상호 작용합니까? 나는 이것이 순진하다는 것을 알고 있지만 나는 정말로 혼란스러워했습니다. 내 인상은 항상 어셈블리가 .NET 런타임에 의해 해석되지 않지만 상호 작용의 세부 사항을 이해하지 못한다는 것입니다.

답변:


84

예, JIT의 IL 코드에는 IL을 기본 기계 명령어로 번역하는 작업이 포함됩니다.

예, .NET 런타임은 런타임이 네이티브 머신 코드가 차지하는 메모리 블록을 소유하고 런타임이 네이티브 머신 코드를 호출한다는 점에서 JIT 네이티브 머신 코드와 상호 작용합니다.

.NET 런타임이 어셈블리의 IL 코드를 해석하지 않는다는 것이 맞습니다.

실행이 아직 원시 기계 코드로 JIT 컴파일되지 않은 함수 또는 코드 블록 (예 : if 블록의 else 절)에 도달하면 JIT'r가 호출되어 IL 블록을 원시 기계 코드로 컴파일합니다. . 이 작업이 완료되면 프로그램 실행이 새로 생성 된 기계어 코드에 입력되어 프로그램 논리를 실행합니다. 원시 기계어 코드 실행을 실행하는 동안 아직 기계어 코드로 컴파일되지 않은 함수에 대한 함수 호출에 도달하면 JIT'r이 호출되어 해당 함수를 "적시에" 컴파일 합니다 . 등등.

JIT'r는 함수 본문의 모든 논리를 한 번에 기계 코드로 컴파일하지 않아도됩니다. 함수에 if 문이있는 경우 if 또는 else 절의 문 블록은 실행이 실제로 해당 블록을 통과 할 때까지 JIT 컴파일되지 않을 수 있습니다. 실행되지 않은 코드 경로는 실행될 때까지 IL 형식으로 유지됩니다.

컴파일 된 원시 기계어 코드는 다음에 해당 코드 섹션이 실행될 때 다시 사용할 수 있도록 메모리에 보관됩니다. 두 번째로 함수를 호출하면 두 번째로 JIT 단계가 필요하지 않기 때문에 처음 호출 할 때보 다 빠르게 실행됩니다.

데스크톱 .NET에서 네이티브 기계어 코드는 appdomain의 수명 동안 메모리에 보관됩니다. .NET CF에서 응용 프로그램의 메모리가 부족하면 원시 기계어 코드가 폐기 될 수 있습니다. 다음에 실행이 해당 코드를 통과 할 때 원래 IL 코드에서 다시 JIT 컴파일됩니다.


14
사소한 현명한 수정- 'JIT'r가 반드시 함수 본문의 모든 논리를 컴파일하는 것은 아닙니다. 한 번에 모든 방법.
폴 알렉산더

18
이것이 수행 되는 이유 는 주로 non-jit 컴파일러가 특정 대상 플랫폼에서 특정 최적화를 사용할 수 있다고 가정 할 수 없기 때문입니다. 유일한 옵션은 가장 낮은 공통 분모로 컴파일하거나 더 일반적으로 각각 자체 플랫폼을 대상으로하는 여러 버전을 컴파일하는 것입니다. JIT는 컴파일러가 사용 가능한 최적화를 알고있는 대상 시스템에서 기계 코드에 대한 최종 변환이 수행되기 때문에 이러한 단점을 제거합니다.
Chris Shain 2011

1
Chris는 JIT가 어떤 최적화를 사용할 수 있는지 알고 있다고 생각했지만 중요한 최적화를 적용 할 시간이 없습니다. 아마도 NGEN이 더 강력 할 것입니다.
Grigory

2
@Grigory 예, NGEN은 JIT 컴파일러가 제공하지 않는 시간의 여유가 있지만 JIT가 알아내는 데 많은 시간이 걸리지 않는 컴파일 된 코드의 성능을 향상시키기 위해 내릴 수있는 코드 생성 결정이 많습니다. 예를 들어, JIT 컴파일러는 JIT 컴파일 단계에 상당한 시간을 추가하지 않고도 런타임시 발견 된 사용 가능한 하드웨어와 가장 잘 일치하는 명령어 세트를 선택할 수 있습니다. 나는 .NET JITter가 중요한 방식으로 이것을 수행한다고 생각하지 않지만 가능합니다.
dthorpe 2013 년

24

코드는 어셈블리 형식과 유사한 Microsoft Intermediate Language로 "컴파일"됩니다.

실행 파일을 두 번 클릭하면 Windows가로드 mscoree.dll된 다음 CLR 환경을 설정하고 프로그램 코드를 시작합니다. JIT 컴파일러는 프로그램에서 MSIL 코드 읽기를 시작하고 코드를 CPU가 실행할 수있는 x86 명령어로 동적으로 컴파일합니다.


2

아래 예제를 통해 IL 코드를 네이티브 CPU 명령어로 컴파일하는 방법을 설명하겠습니다.

public class Example 
{
    static void Main() 
    {
        Console.WriteLine("Hey IL!!!");
    }
}

주로 CLR은 형식에 대한 모든 세부 정보와 메타 데이터로 인해 해당 형식에서 호출되는 메서드를 알고 있습니다.

CLR이 네이티브 CPU 명령으로 IL을 실행하기 시작할 때 CLR은 Main의 코드에서 참조하는 모든 유형에 대해 내부 데이터 구조를 할당합니다.

우리의 경우에는 콘솔 유형이 하나만 있으므로 CLR은 하나의 내부 데이터 구조를 할당합니다. 내부 구조를 통해 참조 된 유형에 대한 액세스를 관리합니다.

해당 데이터 구조 내에서 CLR에는 해당 유형으로 정의 된 모든 메서드에 대한 항목이 있습니다. 각 항목에는 메소드의 구현을 찾을 수있는 주소가 있습니다.

이 구조를 초기화 할 때 CLR은 CLR 자체에 포함 된 문서화되지 않은 FUNCTION에 각 항목을 설정 합니다. 그리고 짐작할 수 있듯이이 FUNCTION 은 우리가 JIT 컴파일러라고 부르는 것입니다.

전반적으로 JIT 컴파일러는 IL을 네이티브 CPU 명령어로 컴파일하는 CLR 함수로 간주 할 수 있습니다. 이 과정이 우리의 예에서 어떻게 될지 자세히 보여 드리겠습니다.

1. Main이 WriteLine을 처음 호출하면 JITCompiler 함수가 호출됩니다.

2. JIT 컴파일러 함수는 호출되는 메서드와이 메서드를 정의하는 유형을 알고 있습니다.

3. Jit Compiler는 해당 유형이 정의 된 어셈블리를 검색하고 WriteLine 메소드의 IL 코드에서 해당 유형에 의해 정의 된 메소드에 대한 IL 코드를 가져옵니다.

4. JIT 컴파일러는 DYNAMIC 메모리 블록을 할당 한 후 JIT가 IL 코드를 네이티브 CPU 코드로 확인하고 컴파일하고 해당 CPU 코드를 해당 메모리 블록에 저장합니다.

5. 그런 다음 JIT 컴파일러는 내부 데이터 구조 항목으로 돌아가서 주소 (주로 WriteLine의 IL 코드 구현을 참조 함)를 WriteLine의 원시 CPU 명령을 포함하는 동적으로 생성 된 새 메모리 블록 주소로 대체합니다.

6. 마지막으로 JIT 컴파일러 함수는 메모리 블록의 코드로 이동하여 writeline 메서드의 네이티브 코드를 실행합니다.

7. WriteLine 실행 후 코드는 Mains '코드로 돌아가 정상적으로 실행을 계속합니다.


1

.NET은 MSIL이라는 중간 언어 (때로는 IL로 약칭 됨)를 사용합니다. 컴파일러는 소스 코드를 읽고 MSIL을 생성합니다. 프로그램을 실행하면 .NET JIT (Just In Time) 컴파일러가 MSIL 코드를 읽고 메모리에 실행 가능한 응용 프로그램을 생성합니다. 이런 일이 발생하는 것을 볼 수는 없지만 뒤에서 무슨 일이 일어나고 있는지 아는 것이 좋습니다.


0

.NET Framework는 CLR 환경을 사용하여 IL이라고도하는 MSIL (Microsoft Intermediate Language)을 생성합니다. 컴파일러는 소스 코드를 읽고 프로젝트를 빌드 / 컴파일 할 때 MSIL을 생성합니다. 이제 마침내 프로젝트를 실행하면 .NET JIT ( Just-in-time Compiler )가 작동합니다. JIT는 MSIL 코드를 읽고 CPU에서 쉽게 실행할 수있는 네이티브 코드 (x86 명령어)를 생성하고, JIT는 모든 MSIL 명령어를 읽고 한 줄씩 실행합니다.

당신이 관심이 있다면, 무대 뒤에서 무슨 일이 일어나는지, 이미 답변을 받았습니다. 따르십시오- 여기

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.