C#, IL 그리고 Native

[제목] C#, IL 그리고 Native

C++ 혹은 Native 코드를 써왔던 C# 개발자들은 간혹 C# 코드가 Native처럼 실행되기를 바랄 때가 있다. 이 개발자들은 문득 C++ 처럼 빠른 스피드에 대한 회상에 젖거나 혹은 ILSpy로도 쉽게 복원되지 않는 C# Native(?) 코드를 갖고 싶어할지도 모른다. 그렇다면 이것은 가능할까?

C# 코드를 빌드하면 MSIL 이라는 중간 코드 (Intermediate Language)를 만들어 낸다. 프로그램 실행시 이 IL 코드는 JIT Compiler에 의해 컴파일되어 Native 코드로 변경된다. IL 방식의 가장 큰 장점은 실행 머신 환경 (32/64 bit, x86/AMD 등)에 관계없이 동일 코드를 실행할 수 있다는 것으로 이는 개발자가 배포 환경에 대해 거의 신경 쓰지 않아도 되게 한다. 반면 IL 방식의 단점은 실행시 어쨌든 IL 코드를 컴파일해야 한다는 것인데, 이는 속도를 저하시키는 원인이 된다. .NET에서는 이러한 속도 저하를 최대한 줄이기 위해 프로그램 실행시 전체 IL 코드를 컴파일하는 것이 아니라 호출되는 메서드 단위로 컴파일을 진행한다. 즉 실행 중 어떤 메서드가 호출되면 그 메서드만 JIT 컴파일러가 Native로 변경하는 것이다. 따라서 만약 프로그램 실행 전 과정에서 어떤 메서드가 한번도 호출되지 않는다면 그 메서드는 Native 코드로 변경되지 않는다.

C# 코드를 이러한 IL 방식이 아닌 완전한 Native 코드로 변환하는 것은 가능할까?
가장 먼저 떠오르는 방법은 .NET Framework에서 제공하는 NGEN(Native Image Generator)이라는 Tool을 사용하는 것이다. C# 코드를 빌드하여 EXE 혹은 DLL을 만든 후, NGEN을 사용하면 이 IL 파일을 Native 이미지로 변경하여 GAC 안에 설치한다.

NGEN 실행

NGEN은 Signed 어셈블리 혹은 Unsigned 어셈블리에 공히 사용할 수 있는데, 이를 실행하면 *.ni.exe (혹은 DLL의 경우 *.ni.dll)을 GAC에 생성한다. 아래는 .NET 4.0으로 컴파일된 위의 TestConsole.exe를 NGEN으로 설치한 후의 ni 파일 위치이다.

Native Image 위치

하지만 이 Native 이미지는 직접 실행할 수 없고, 또한 그 EXE/DLL을 그대로 배포할 수도 없다. 만약 위의 *.ni.exe 파일을 C:\Public 에 복사한 후 실행해 보면, 다음과 같은 오류를 발생한다.

Native Image 실행 에러

NGEN은 각 EXE/DLL 단위로 IL코드를 Native Image로 변환한다. 즉, 한 프로그램이 1개의 EXE와 10개의 DLL로 구성되어 있는 경우 모두 NGEN하면 11개의 ni 파일들을 생성하게 된다. C# 빌드에서 생성된 프로그램을 실행할 때, CLR은 먼저 해당 Native Image가 GAC에 있는지 체크하고 있으면 IL을 JIT 하지 않고 그 Native Image를 로딩하게 된다. 이때 만약 ni 파일에 문제가 있거나 옛 버젼인 경우, 원래의 IL 파일을 Fallback하여 사용하게 된다.

NI 파일은 Copy에 의해 직접 배포할 수 없기 때문에, 보통 Installer가 설치시 NGEN을 내부적으로 실행하여 Native Image를 설치하는 방식을 취한다. 어쨌든 이러한 방식은 옛 회상에 빠진 C# 개발자에게 실망스러운(?) 일이다.

아직 개발단계이긴 하지만 Visual Studio 14 부터 NGEN 보다 더 진화된 방식인 .NET Native 라는 것을 지원할 예정이다. .NET Native는 C# 컴파일러로부터 생생된 IL 코드를 다시 최적화하여 각 배포 환경에 따른 Native 이미지를 만들어 준다. 예를 들어, x86 32bit용 Native Image를 생성해서 x86/32 비트 컴퓨터에서만 사용하는 것이다. VS가 .NET Native를 만들기 시작한 동기는 Windows Store App과 밀접한 관계가 있다. Windows Store App은 Windows 8 이상이 동작하는 모든 디바이스 (PC, 태블릿, 랩탑 등)에서 동작하는 App인데, 이러한 App은 신속한 속도가 무엇보다 중요하게 되었다. 따라서 각 Platform별로 미리 Native Image를 저장해 두고 사용자가 Windows Store로부터 프로그램을 설치할 때 디바이스에 맞는 Native Image를 설치할 수 있도록 한 것이다. .NET Native는 현재 Windows Store App 만 지원하고 있다.

.NET Native는 NGEN과 다르게 모든 Dependent DLL들을 Static Link하여 Native Image를 생성한다. 따라서 .NET Native로 생성된 Native Image는 클라이언트 디바이스에 .NET Framework을 가질 필요가 없으며, 있더라고 그것을 사용하지 않는다. NGEN이 기존 CLR 런타임을 사용하는 반면, .NET Native 보다 Compact한 Mini CLR 런타임(MRT)을 사용하게 된다. 또 다른 주목할 만한 차이점은 NGEN은 JIT Compiler를 사용하여 Native Image를 생성하는 반면, .NET Native는 VC++ 컴파일러를 내부적으로 사용한다는 점이다. 즉, .NET Native는 IL을 단순히 미리 JIT 컴파일한다는 의미를 넘어 VC++ 컴파일러가 할 수 있는 모든 Optimization 기능을 적용할 수 있다는 것을 의미한다.



본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.