Native AOT는 정확한 설정 방법과 활용 방법을 안다면 어렵지 않게 시작할 수 있습니다. 하지만, 사전에 AOT 사용을 위해서 준비해야 하는 세레모니가 많고 복잡하기에 SDK 스타일 패키지로 이를 간소화할 수 있다면 좋겠다고 생각해서 리서치해보고 nuget으로 패키징해본 것을 공유합니다.
.NET 8 이상부터 사용 가능하며, .NET 10에서 File-based App과 함께 쓰시면 더욱 좋습니다. 예를 들어, 아래와 같이 코드를 작성한 후 복잡한 명령어를 주지 않고, dotnet publish app.cs 로 실행하기만 하면 곧바로 Native AOT가 입혀진 네이티브 실행 파일이 만들어지게 됩니다. 이 때, 기본 SDK로 Microsoft.NET.Sdk 를 상속받게 되어있으니 .NET 8, 9, 10 혹은 그 이후 버전을 사용하더라도 자동으로 새 버전을 따라가도록 설계되어 있습니다.
#:sdk NativeSdk@0.5.1
unsafe
{
const int ArraySize = 10;
// stackalloc으로 스택에 메모리 할당
int* arr = stackalloc int[ArraySize];
// 랜덤 값으로 배열 초기화
Random rand = new(42);
Console.WriteLine("Before Sort:");
for (int i = 0; i < ArraySize; i++)
{
arr[i] = rand.Next(1, 100);
Console.Write($"{arr[i]} ");
}
Console.WriteLine();
// QuickSort 실행
QuickSort(arr, 0, ArraySize - 1);
// 정렬 결과 출력
Console.WriteLine("\nAfter Sort:");
for (int i = 0; i < ArraySize; i++)
{
Console.Write($"{arr[i]} ");
}
Console.WriteLine();
static void QuickSort(int* arr, int left, int right)
{
if (left >= right) return;
int pivotIndex = Partition(arr, left, right);
QuickSort(arr, left, pivotIndex - 1);
QuickSort(arr, pivotIndex + 1, right);
}
static int Partition(int* arr, int left, int right)
{
int pivot = arr[right];
int i = left - 1;
for (int j = left; j < right; j++)
{
if (arr[j] <= pivot)
{
i++;
(arr[i], arr[j]) = (arr[j], arr[i]); // 튜플 스왑
}
}
(arr[i + 1], arr[right]) = (arr[right], arr[i + 1]);
return i + 1;
}
}
다만 약간 아쉬운 것은, unsafe 키워드를 MSBuild 프로퍼티 레벨에서 전역적으로 지정하는 기능은 아직 제공되지 않고 있는 것이 아쉽습니다. 추후 닷넷 11이나 이후 버전에서 관련된 기능이 들어간다면, File-based App 포맷을 유지하면서도 곧바로 포인터를 쓸 수 있는 네이티브 프로그래밍이나 C 언어용 DLL Export 코드를 더 간결하게 만들 수도 있을 것입니다.