BCL은 어떻게 그렇게 빠르게 호출되나요?

Base Class Library는 네이티브로 구현되어있는 걸로 아는데,

C#에서 Native DLL을 호출할 때는 비용이 꽤 크잖아요?(P/Invoke)

근데 BCL은 어떤 식으로 되어있길래 그리 빠른 걸까요…?

너무 초보적인 질문일수도 있지만… 구글링 해봐도 답을 찾기가 어려워서 질문 올립니다.

3개의 좋아요

저희의 갓지피티 형님이

image

라고 하네요… 맞는지는 모르겠지만 ㅎㅎㅎ

5개의 좋아요

뭐 그냥 느낌적인 느낌으로 찍는 거긴한데…관리메모리 / 비관리 메모리의 영역이 있을 것 같다고 추측합니다…

5개의 좋아요

아마 머신 코드로 컴파일되었기 때문일 것입니다.

닷넷 런타임은 JIT 컴파일된 코드를 두번 컴파일하지 않는다는 특징이 있습니다. 즉, 한번 컴파일된 머신코드를 저장했다가 다음 실행시에는 JIT 컴파일을 생략하고, 컴파일된 머신코드를 직접 실행하는 것이죠.

그래서, 닷넷 프로그램은 초반에는 굼떠도 실행 시간이 길어질 수록 성능이 좋아진다고 하네요.
BCL은 애초부터 머신코드로 컴파되었기 때문에, JIT 컴파일 지연이 없어서, 다른 코드보다 빠를 것입니다.

참고로, 아래와 같은 삽질도 해보기도 했죠. ^^

        Stopwatch sw = Stopwatch.StartNew();

// 한번 JIT 컴파일 후 실행하고, 머신코드로 바뀌겠지?
        _ = Add(1, 0);

// 그 다음 호출은 머신코드가 실행되기 때문에, 속도가 빠를 거야.
        for (int i = 1; i < 10000_0000; i++) {
            _ = Add (1, i);
        }
        Console.WriteLine(sw.ElapsedTicks);
        Console.ReadKey();
        Stopwatch sw = Stopwatch.StartNew();

//  왠지 느릴 듯.
        for (int i = 0; i < 10000_0000; i++) {
            _ = Add (1, i);
        }
        Console.WriteLine(sw.ElapsedTicks);
        Console.ReadKey();

느낌적인 느낌 상, 전자가 빠르긴 한거 같아요. ^^

4개의 좋아요

우선 Base Class Library가 모두 네이티브로 구현 되어 있지는 않습니다.
닷넷의 관리 코드로 작성 되어 있고 그중 일부 클래스에서 네이티브dll을 사용 합니다.

위 질문은

이미 공식 MS Doc를 보시면 간략한 한줄로 설명 되어 있습니다.
프레임워크 라이브러리 | Microsoft Learn

BCL은 일반적으로 고성능으로 설계되었으며

하지만 자세하게 어떻게 고성능으로 설계 하였는지 정보는 알 수 없는데
기본적으로 JIT 컴파일 최적화를 통해
(코드 인라인, 불필요 연산 제거, 분기문을 상황에 따라 if or switch로 변환 캐시 처리 등)
성능을 향상 시킬 수 있습니다.

이것은 BCL 뿐 아니라 사용자가 작성한 코드 또한 컴파일 시점에 마찬가지로 반영 되어 집니다.

7개의 좋아요

저도 그렇게 생각합니다. 스스로의 생각이라 명쾌하지 않다는 게 문제지만…

3개의 좋아요

네이티브로 구현된 게 아니었군요. 일부 OS 관련 함수들만 네이티브 DLL을 호출하는 거고…
답변 감사합니다.

3개의 좋아요

반복문 밖에서 한 번 더 호출 해야만 최적화가 되는 걸까요?
그것과 무관하게 아래의 케이스도 JIT 컴파일이 될 거 같은데… 아닌가요

4개의 좋아요

이미지가 보이지 않네요 ㅠㅠ…

3개의 좋아요

갓지피티형님이 가라사대

BCL (Base Class Library)은 .NET Framework 또는 .NET Core와 같은 .NET 런타임에서 실행 됩니다. 이러한 런타임은 메모리 관리, 스레딩 및 코드 실행 등의 기능을 처리하므로 BCL에 서 제공하는 클래스와 메서드는 .NET 런타임에서 직접 실행됩니다.

즉, BCL은 네이티브 코드가 아닌 .NET 런타임에서 직접 실행되기 때문에 P/Invoke 비용이 발생하지 않습니다. 또한, .NET 런타임은 Just-In-Time (JIT) 컴파일러를 사용하여 실행 중인 코드를 최적화하기 때문에 BCL에서 제공하는 메서드는 가능한 빠르게 실행됩니다.

따라서 BCL은 .NET 런타임과 밀접하게 통합되어 있으므로 네이티브 DLL을 호출하는 것보 다 더 빠르고 안정적으로 작동합니다. 그러나 BCL의 일부 기능은 네이티브 코드와 상호 작 용해야 하기 때문에 .NET 런타임에서 P/Invoke를 사용하기도 합니다. 이 경우에는 P/Invoke 비용이 추가됩니다.

라고 했습니다.

4개의 좋아요

저도 정확한 정확한 근거를 찾을 수 없으니, 저런 삽질도 해봤겠죠? ^^

삽질 코드는 "Jit 컴파일은 스택에 들어갔다 나올 때 발생한다"는 기대 내지 선입견에 기반한 것입니다.

그래서, 첫번째 코드는 Add 스택을 한번 들어갔다 나왔으니, 반복기가 Add 스택을 돌릴 때에는 머신코드가 실행될 것 같고, 두번째 코드는 아예 처음부터 반복기가 Add 스택을 돌리기 때문에, 반복기가 끝난, 그 다음 시점부터 머신코드가 호출될 것이라는 가정을 한 것입니다.

물론, 맞을 수도 안 맞을 수도 있겠죠.
마법 상자의 뚜껑을 따 볼수 없는 답답함에 한 뻘짓일 뿐입니다.

결과적으로, 가장 빠른 시간은 첫번째 코드에서 나왔고, 가장 느린 시간은 두번째 코드에서 나왔지만, stop watch 의 오차가 너무 크기 - 최대 시간 : 최소 시간이 1: 5 이상 납니다 - 때문에 신뢰할 수 없는 결론입니다.

4개의 좋아요

runtime/corelib.md at main · dotnet/runtime (github.com)

관련된 내용인 것 같습니다. (FCall, InternalCall)

구동 중인 CLR 상에 내장된 네이티브 코드를 GC같은 제약사항 없이 직접 액세스해서 빠르고 효율적으로 구동된다고 하네요.

7개의 좋아요

감사합니다. 운영체제 API같은 게 그런 식으로 제공되는 거겠군요.
다음과 같은 비슷한 질문도 있네요.

3개의 좋아요