Microsoft는 2026년 9월 15일 .NET Blog에 게시한 글에서 .NET 11에 적용된 수백 가지 개선 사항을 소개하며, 이를 JIT(Just-In-Time) 컴파일러와 런타임, 라이브러리에 걸친 장기적인 작업의 누적 결과로 설명합니다. 핵심은 성능을 두 배로 높이는 단일 기능이 존재한다는 것이 아니라, 더 이상 필요하지 않은 경계 검사, 제거된 메모리 할당, 회피된 잠금이나 시스템 호출, 더 적은 프로세서 사이클로 실행되는 루프처럼 작지만 반복적인 이점을 제거하는 데 있습니다.
이러한 개선 사항이 중요한 이유는 상당 부분이 애플리케이션 코드를 수정하거나 애플리케이션을 다시 설계할 필요 없이 적용되기 때문입니다. JIT 컴파일러는 일반적으로 C#, F#, Visual Basic에서 생성된 중간 언어 IL을 프로세서가 실행하는 네이티브 명령어로 변환합니다. 특정 가상 호출을 특정 형식으로 연결할 수 있거나 어떤 검사가 실패하지 않는다는 사실을 입증하면, 더 짧은 명령어를 생성하고 함수를 서로 인라인할 가능성을 높일 수 있습니다.
추상화 제거 개선
자료에서 두드러지는 부분은 Microsoft가 추상화 제거라고 부르는 작업에 초점을 맞춥니다. 이는 개발자가 프로그램 설계에서 필요로 하는 일부 추상화의 실행 비용을 런타임이 우회할 수 있도록 하는 것입니다. 예를 들어 인터페이스 호출과 가상 메서드 호출에서는 기존 실행 방식이 여러 포인터를 로드하고 간접 호출을 수행해야 할 수 있으며, 이 때문에 호출된 메서드를 현재 메서드에 인라인하기 어려워집니다.
.NET 11은 “Guarded Devirtualization” 또는 보호된 비가상화라는 기법을 계속 발전시킵니다. JIT는 실행 중 가장 일반적인 형식을 식별해 해당 형식을 직접 호출하는 빠른 경로를 만들고, 나중에 다른 형식이 나타나도 실행이 올바르게 동작하도록 가상 경로를 유지합니다. 이로 인해 메서드 인라이닝이 가능해지면 상수 전파, 분기 제거, 경계 검사 제거와 같은 추가 최적화도 수행할 수 있습니다.
이 작업은 일반 가상 메서드, ReadyToRun 및 NativeAOT 작업, 인터페이스의 기본 가상 구현까지 확장됩니다. 자료는 직접 호출이 더 많은 인라이닝으로 이어질 경우 이러한 개선 사항이 코드 크기를 늘릴 수 있지만, 프로그램의 로직을 최적화기가 더 잘 파악할 수 있게 한다고 설명합니다.
할당 감소와 가비지 수집기에 미치는 영향 완화
.NET 11은 또한 객체가 함수 내부에서 생성된 뒤 함수의 범위를 벗어나는지를 확인하려는 Escape Analysis(이스케이프 분석)를 계속 확장합니다. 객체가 해당 범위를 벗어나지 않는다는 사실이 입증되면 JIT는 객체를 관리 힙에 배치하는 작업을 피하거나 일부 경우 할당 자체를 제거할 수 있으며, 이를 통해 가비지 수집기에 가해지는 부담을 줄입니다.
원문은 Nullable boxing 개선과 컬렉션 열거에서의 조건부 이스케이프 분석 사례를 제시합니다. 한 측정에서는 인스턴스 필드로 구성된 컬렉션을 열거할 때 32바이트 할당이 사라졌고, 실행 시간이 .NET 10의 13.874나노초에서 .NET 11의 2.674나노초로 감소했습니다. 또한 제네릭 값이나 인터페이스 호출을 사용하는 일부 경우에는 24바이트 크기의 임시 할당을 피할 수 있게 되었습니다.
이 수치는 매우 작은 작업에 해당하므로 모든 애플리케이션의 전반적인 성능이 일정하게 향상된다고 해석해서는 안 됩니다. 주요 가치는 런타임이 최적화할 수 있는 코드 패턴을 보여준다는 데 있으며, 실제 효과는 애플리케이션의 특성과 핫 경로에 따라 달라집니다.
델리게이트와 비동기 런타임
.NET 11에는 CoreCLR의 델리게이트 표현을 변경한 내용도 포함되어 있습니다. 그중 하나는 64비트 프로세스에서 모든 델리게이트 객체에서 포인터 하나 크기의 필드를 제거하는 것으로, 자료에 따르면 델리게이트당 8바이트를 절약합니다. NativeAOT에서도 델리게이트 표현의 필드 순서가 다시 배치되었으며, Arm64와 같은 아키텍처에서 접근을 개선하기 위해 함께 사용되는 일부 값이 메모리상에 가까이 배치되었습니다.
이 블로그 글은 또한 “runtime async”라는 이름의 새로운 구조를 소개합니다. 이 구조는 async/await 메서드 변환의 일부 책임을 C# 컴파일러에서 JIT와 런타임으로 옮깁니다. 기존 모델에서 컴파일러는 매개변수, 지역 값, await 대상, 실행 상태와 같이 재개에 필요한 필드를 포함하는 상태 머신을 생성합니다. 반면 새로운 모델은 IL에서 더 작은 계약을 사용한 뒤, 일시 중단 지점에서 살아 있는 값, continuation 객체의 배치 방식, 외부에 표시되는 Task 또는 ValueTask의 생성과 관련된 결정을 런타임과 JIT에 맡깁니다.
실제로 무엇이 달라지는가?
이번 개선 사항의 편집적 의미는 .NET 11이 새로운 API를 추가하는 것만큼 기존 코드의 개선에도 중점을 둔다는 데 있습니다. 인터페이스, 제네릭 메서드, 델리게이트, 열거, 비동기 작업에 크게 의존하는 애플리케이션은 직접적인 소스 코드 변경 없이도 혜택을 받을 수 있지만, 향상 폭은 프로세서, 운영 체제, 런타임 설정, 작업 부하의 특성에 따라 달라집니다.
Microsoft는 BenchmarkDotNet을 사용해 결과를 테스트하고, .NET 10과 .NET 11을 모두 설치한 뒤 동일한 코드를 두 버전에서 실행할 것을 권장합니다. 또한 공개된 측정은 매우 정밀한 테스트이므로 하드웨어, 다른 프로세스, 환경 설정의 영향을 받을 수 있다고 경고합니다. 따라서 마이크로벤치마크 결과만으로 업그레이드를 결정하거나 전반적인 성능 향상을 입증할 수는 없으며, 더 광범위한 결론을 내리기 전에 실제 애플리케이션 부하를 측정해야 합니다.