Microsoft MVP성태의 닷넷 이야기
.NET Framework: 2069. .NET 7 - AOT(ahead-of-time) 컴파일 [링크 복사], [링크+제목 복사],
조회: 15899
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 4개 있습니다.)
(시리즈 글이 7개 있습니다.)
개발 환경 구성: 386. .NET Framework Native compiler 프리뷰 버전 사용법
; https://www.sysnet.pe.kr/2/0/11563

.NET Framework: 2069. .NET 7 - AOT(ahead-of-time) 컴파일
; https://www.sysnet.pe.kr/2/0/13162

닷넷: 2175. C# - DllImport 메서드의 AOT 지원을 위한 LibraryImport 옵션
; https://www.sysnet.pe.kr/2/0/13466

닷넷: 2184. C# - 하나의 resource 파일을 여러 프로그램에서 (AOT 시에도) 사용하는 방법
; https://www.sysnet.pe.kr/2/0/13483

개발 환경 구성: 696. C# - 리눅스용 AOT 빌드를 docker에서 수행
; https://www.sysnet.pe.kr/2/0/13487

닷넷: 2202. C# - PublishAot의 glibc에 대한 정적 링킹하는 방법
; https://www.sysnet.pe.kr/2/0/13529

오류 유형: 913. C# - AOT StaticExecutable 정적 링킹 시 빌드 오류
; https://www.sysnet.pe.kr/2/0/13669




.NET 7 - AOT(ahead-of-time) 컴파일

여기서 잠깐, 닷넷 AOT 역사에 대해 잠깐 훑어볼까요? ^^

Conversation about crossgen2
; https://devblogs.microsoft.com/dotnet/conversation-about-crossgen2/

가장 처음에, ".NET Framework"의 NGen이 있었습니다.

Ngen.exe (Native Image Generator)
; https://learn.microsoft.com/en-us/dotnet/framework/tools/ngen-exe-native-image-generator

NGen은 윈도우 플랫폼 종속적이었고, 비록 native 코드를 생성하긴 했지만 그래도 managed 모듈을 필요로 했습니다.

이후 "Windows Phone"이 나와 크로스-플랫폼을 지원해야 하면서 별도로 AOT 코드 생성기가 개발됩니다. 얼마 안 돼 윈도우 폰은 사라졌지만, 다행히 그 역사를 .NET Core의 CrossGen으로 세대를 이어갑니다. 이후 닷넷 6에 포함된 CrossGen2는 이전 CrossGen을 (몇몇 기능 향상과 함께) C# 언어로 포팅한 것에 해당합니다.

하지만, NGen부터 CrossGen2까지의 공통점이 있다면, 바로 native + managed 모듈이 함께 필요하다는 점입니다.




자, 그리고 이제 .NET 7에서 진정한 네이티브 코드 생성기인 AOT 컴파일 기능이 공식적으로 포함됐습니다.

Native AOT Deployment
; https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/

실제로 테스트를 해볼까요? ^^

간단한 닷넷 7 콘솔 애플리케이션을 만들고, csproj에 PublishAot 옵션을 추가합니다.

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net7.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>

        <PublishAot>true</PublishAot>

        <IsAotCompatible>true</IsAotCompatible> 
        <!-- IsTrimmable, EnableTrimAnalyzer, EnableSingleFileAnalyzer, EnableAotAnalyzer 4개의 값을 true로 설정한 효과 (.NET 8부터 지원) ---->
        
    </PropertyGroup>

</Project>

이후 "dotnet publish" 해주면 ".\bin\Debug\net7.0\win-x64\publish" 디렉터리에 실행 파일과 PDB 파일이 생성됩니다.

// 디버그 빌드 기준
net7_pubone_sample.exe, 약 4.69MB
net7_pubone_sample.pdb, 약 15.9MB

// 릴리스 빌드 기준
net7_pubone_sample.exe, 약 2.85MB
net7_pubone_sample.pdb, 약 12.1MB

당연히, net7_pubone_sample.exe만 (닷넷 런타임이 설치되지 않은) 다른 PC에 복사해 실행할 수 있습니다.




이렇게 완전히 AOT로 빌드된 모듈은 역어셈블이 가능할까요?

C# - PublishSingleFile로 배포한 이미지의 역어셈블 가능 여부 (난독화 필요성)
; https://www.sysnet.pe.kr/2/0/13161

위의 글에서 살펴본 .NET 6와는 달리, 이제 Windbg는 mscordaccore.dll을 로드하려고 해도,

// c:\temp\mscordaccore.dll이 있는 경우,

0:004> .cordll -lp c:\temp
CLR DLL status: No load attempts

실패합니다. 즉, 이제는 대상 프로세스를 .NET 프로세스로 볼 수 없는 것입니다. 비록 관리 Heap을 사용해 GC는 발생하겠지만 그 외의 모든 면에서 일반 native 프로세스와 동일한데요, 달리 말하면, 그냥 여러분들이 C/C++로 GC 기능을 구현한 다음 그것을 활용해 코딩한 것과 같은 것입니다.

따라서, AOT로 빌드한 경우라면 이제 native 모드로 디버깅을 해야 하는데,

0:005> ~
   0  Id: 2df8.1424 Suspend: 1 Teb: 00000036`a2077000 Unfrozen
   1  Id: 2df8.2144 Suspend: 1 Teb: 00000036`a2079000 Unfrozen
   2  Id: 2df8.21c8 Suspend: 1 Teb: 00000036`a207b000 Unfrozen
   3  Id: 2df8.2210 Suspend: 1 Teb: 00000036`a207d000 Unfrozen
   4  Id: 2df8.2e44 Suspend: 1 Teb: 00000036`a207f000 Unfrozen
.  5  Id: 2df8.1ddc Suspend: 1 Teb: 00000036`a2081000 Unfrozen

0:005> ~0s
rax=0000000000000006 rbx=00000036a22ff748 rcx=0000000000000050
rdx=0000000000000000 rsi=0000000000000050 rdi=0000000000000000
rip=00007ffabe40d184 rsp=00000036a22ff478 rbp=00000036a22ff5c0
 r8=0000000000000020  r9=0000000000000001 r10=0000000000000000
r11=00000188b1aeffe0 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244
ntdll!NtReadFile+0x14:
00007ffa`be40d184 c3              ret

0:000> k
 # Child-SP          RetAddr               Call Site
00 00000036`a22ff478 00007ffa`bbaf5593     ntdll!NtReadFile+0x14
01 00000036`a22ff480 00007ff6`5f259a0d     KERNELBASE!ReadFile+0x73
02 00000036`a22ff500 00007ff6`5f25953f     net7_pubone_sample!System_Console_Interop_Kernel32___ReadFile_g____PInvoke_43_0+0x7d
03 00000036`a22ff5d0 00007ff6`5f25ad36     net7_pubone_sample!System_Console_Interop_Kernel32__ReadFile+0x5f [/_/src/libraries/System.Console/src/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs @ 412] 
04 00000036`a22ff640 00007ff6`5f25ab6b     net7_pubone_sample!System_Console_System_ConsolePal_WindowsConsoleStream__ReadFileNative+0xc6 [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1156] 
05 00000036`a22ff6f0 00007ff6`5f25e86c     net7_pubone_sample!System_Console_System_ConsolePal_WindowsConsoleStream__Read+0x6b [/_/src/libraries/System.Console/src/System/ConsolePal.Windows.cs @ 1114] 
06 00000036`a22ff770 00007ff6`5f1dcbef     net7_pubone_sample!System_Console_System_IO_ConsoleStream__Read+0x8c [/_/src/libraries/System.Console/src/System/IO/ConsoleStream.cs @ 76] 
07 00000036`a22ff7e0 00007ff6`5f1dcd6d     net7_pubone_sample!S_P_CoreLib_System_IO_StreamReader__ReadBuffer+0x1af [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 616] 
08 00000036`a22ff8a0 00007ff6`5f25ee63     net7_pubone_sample!S_P_CoreLib_System_IO_StreamReader__ReadLine+0x7d [/_/src/libraries/System.Private.CoreLib/src/System/IO/StreamReader.cs @ 785] 
09 00000036`a22ff960 00007ff6`5f25a1f5     net7_pubone_sample!System_Console_System_IO_SyncTextReader__ReadLine+0x63 [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77] 
0a 00000036`a22ff9c0 00007ff6`5f25f688     net7_pubone_sample!System_Console_System_Console__ReadLine+0x35 [/_/src/libraries/System.Console/src/System/Console.cs @ 722] 
0b 00000036`a22ffa10 00007ff6`5f2ceaa7     net7_pubone_sample!net7_pubone_sample_net7_pubone_sample_Program__Main+0x28 [E:\net7_pubone_sample\net7_pubone_sample\Program.cs @ 8] 
0c 00000036`a22ffa50 00007ff6`5f2ceb3a     net7_pubone_sample!net7_pubone_sample__Module___MainMethodWrapper+0x17
0d 00000036`a22ffa80 00007ff6`5f0ceb6e     net7_pubone_sample!net7_pubone_sample__Module___StartupCodeMain+0x8a
0e 00000036`a22ffae0 00007ff6`5f0bdf54     net7_pubone_sample!wmain+0xae [D:\a\_work\1\s\src\coreclr\nativeaot\Bootstrap\main.cpp @ 205] 
0f (Inline Function) --------`--------     net7_pubone_sample!invoke_main+0x22 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 90] 
10 00000036`a22ffb30 00007ffa`bcba74b4     net7_pubone_sample!__scrt_common_main_seh+0x10c [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 
11 00000036`a22ffb70 00007ffa`be3c26a1     KERNEL32!BaseThreadInitThunk+0x14
12 00000036`a22ffba0 00000000`00000000     ntdll!RtlUserThreadStart+0x21

sos 확장의 도움 없이 닷넷 프로그램을 문제 분석하는 경험은... 그다지 유쾌하진 않을 것입니다. ^^ 참고로, dotnet-dump 역시 Native AOT로 빌드된 프로세스는 인식하지 못합니다.

c:\temp> dotnet-dump ps
No supported .NET processes were found

(그래도, GC Heap 관련 정보는 디버깅할 수 있는 방법을 제공해줘야 하지 않을까요? ^^;)




문서에 나오지만,

Limitations of Native AOT deployment

  • No dynamic loading (for example, Assembly.LoadFile)
  • No runtime code generation (for example, System.Reflection.Emit)
  • No C++/CLI
  • No built-in COM (only applies to Windows)
  • Requires trimming, which has limitations
  • Implies compilation into a single file, which has known incompatibilities
  • Apps include required runtime libraries (just like self-contained apps, increasing their size, as compared to framework-dependent apps)

당연히 .NET Reflection과 같은 기능들은 AOT 지원이 안 됩니다. 그리고 .NET 7의 BCL 중에서도 위와 같은 조건에 걸리는 타입들이 많기 때문에 아직은 AOT가 WPF/WinForm 등의 응용 프로그램은 지원하지 못합니다. 사실 저런 조건들이 AOT 입장에서 제약이지, WPF/WinForm 등의 개발 프레임워크를 만들 때는 유용하게 쓸 수 있는 기술들이어서 제법 광범위하게 사용했던 것입니다. 그런 이유에서, Native AOT가 과연 "아직" WPF/WinForm을 지원하지 못한 것인지, "영원히" 지원하지 못할 것인지는 두고 봐야 알 수 있을 것입니다. ^^

결국 현재의 닷넷 7 AOT는 "Console Application"을 대상으로 한 프로젝트만 지원하고 WPF/WinForm 등의 프로젝트에서 PublishAot 옵션을 주면 "error NETSDK1168: WPF is not supported or recommended with trimming enabled. Please go to https://aka.ms/dotnet-illink/wpf for more details." 빌드 오류가 발생합니다.




참고로, AOT 빌드는 UnmanagedCallersOnlyAttribute 특성과 어우러져 Native API(윈도우의 경우 Win32 DLL)로 동작하는 라이브러리를 만들기에는 아주 이상적인 환경을 제공합니다.

Build native libraries
; https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/#build-native-libraries

관련 예제 코드
; https://github.com/dotnet/samples/tree/main/core/nativeaot/NativeLibrary




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 12/22/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2024-12-06 09시50분
How Much Memory Do You Need in 2024 to Run 1 Million Concurrent Tasks?
; https://hez2010.github.io/async-runtimes-benchmarks-2024/

100만 개 테스트에서 오히려 rust보다 (AOT 무관하게) C#의 메모리 사용이 더 적었다고. ^^
정성태

... 106  107  108  109  110  111  112  113  114  115  [116]  117  118  119  120  ...
NoWriterDateCnt.TitleFile(s)
11025정성태8/12/201622339개발 환경 구성: 294. .NET Core 프로젝트에서 "Copy to Output Directory" 처리 [1]
11024정성태8/12/201621650오류 유형: 350. "nProtect GameMon" 실행 중에는 Visual Studio 디버깅이 안됩니다! [1]
11023정성태8/10/201623151개발 환경 구성: 293. Azure 구독 후 PaaS 서비스 만들어 보기
11022정성태8/10/201623816개발 환경 구성: 292. Azure Cloud Service 배포시 사용자 정의 작업을 추가하는 방법
11021정성태8/10/201620872오류 유형: 349. System.Runtime.Remoting.RemotingException - Type '..., ..., Version=..., Culture=neutral, PublicKeyToken=null' is not registered for activation [2]
11020정성태8/10/201623605VC++: 98. 원본과 대상 버퍼가 같은 경우 memcpy, wmemcpy 주의점
11019정성태8/10/201640278기타: 60. 도서: 시작하세요! C# 6.0 프로그래밍: 기본 문법부터 실전 예제까지 (2쇄 정오표)
11018정성태8/9/201624744.NET Framework: 600. 단일 메서드 내에서의 할당으로 알아보는 자바와 닷넷의 GC 차이점 [1]
11017정성태8/9/201626800웹: 33. HTTP 쿠키에 한글 값을 설정하는 방법
11016정성태8/7/201624006개발 환경 구성: 291. Windows Server Containers 소개
11015정성태8/7/201622267오류 유형: 348. Windows Server 2016 TP5에서 Windows Containers의 docker run 실행 시 encountered an error during Start failed in Win32
11014정성태8/6/201623053오류 유형: 347. Hyper-V Virtual Machine Management service Account does not have permission to open attachment
11013정성태8/6/201633823개발 환경 구성: 290. Windows 10에서 경험해 보는 Windows Containers와 docker [4]
11012정성태8/6/201623872오류 유형: 346. Windows 10에서 Windows Containers의 docker run 실행 시 encountered an error during CreateContainer failed in Win32 발생
11011정성태8/6/201625489기타: 59. outlook.live.com 메일 서비스의 아웃룩 POP3 설정하는 방법
11010정성태8/6/201622880기타: 58. Outlook에 설정한 SMTP/POP3(예:천리안 메일) 계정 암호를 잊어버린 경우
11009정성태8/3/201628070개발 환경 구성: 289. 2016-08-02부터 시작된 윈도우 10 1주년 업데이트에서 Bash Shell 사용 [8]
11008정성태8/1/201621872오류 유형: 345. 2의 30승 이상의 원소를 갖는 경우 버그가 발생하는 이진 검색(Binary Search) 코드
11007정성태8/1/201623576오류 유형: 344. RDP ActiveX 컨트롤로 특정 PC에 연결할 수 없을 때, 오류 상황을 해결하기 위한 팁파일 다운로드1
11006정성태7/22/201626579개발 환경 구성: 288. SSL 인증서를 Azure Cloud Service에 적용하는 방법
11005정성태7/22/201625216개발 환경 구성: 287. Let's Encrypt 인증서 업데이트 주기: 90일
11004정성태7/22/201620079오류 유형: 343. Invalid service definition or service configuration. Please see the Error List for more details.
11003정성태7/20/201627353VS.NET IDE: 110. Visual Studio 2015에서 .NET Core 응용 프로그램 개발 [1]
11002정성태7/20/201620836개발 환경 구성: 286. Microsoft Azure 서비스의 구독은 반드시 IE로!
11001정성태7/19/201631889.NET Framework: 599. .NET Core/SDK 설치 및 기본 사용법 [6]
11000정성태7/16/201620606오류 유형: 342. Microsoft Visual Studio 2010 Tools for Office Runtime (x86 and x64) 설치 시 오류
... 106  107  108  109  110  111  112  113  114  115  [116]  117  118  119  120  ...