성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - sealed 클래스의 메서드를 callback 호출했을 때 인라인 처리가 될까요?</h1> <p> 닷넷 데브에 재미있는 글이 하나 떴군요, ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 매우 작은 메서드의 주입 ; <a target='tab' href='https://forum.dotnetdev.kr/t/topic/7592'>https://forum.dotnetdev.kr/t/topic/7592</a> </pre> <br /> 간단한 예를 들어, 우리는 보통 정렬 알고리즘을 호출할 때 값의 비교를 위한 delegate 또는 인터페이스를 전달하게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > List<int> list = new List<int>() { 5, 4, 3, 2, 1 }; list.Sort(<span style='color: blue; font-weight: bold'>(item1, item2) => item1.CompareTo(item2)</span>); list.ForEach((elem) => Console.Write($"{elem} ")); // 출력 결과: 1, 2, 3, 4, 5 </pre> <br /> 위에서 전달한 delegate 코드 조각은 사실 굉장히 간단한 코드인데요, 여기서 문제는? 저것이 List.Sort 메서드 내에서는 매번 메서드 호출로 처리하는 부하가 발생한다는 점입니다.<br /> <br /> 그런데, 사실 이게 문제일까요? 질문하신 분은, 이에 대한 해법으로 "다른 언어(C++)"을 쓴다"라고 했지만 이것은 약간 잘못된 인식입니다. C++이라고 해서 외부에서 전달한 함수 포인터를 call이 아닌 런타임 시에 inline 시켜 실행하는 재주는 없기 때문입니다.<br /> <br /> 오히려, 전달받은 함수 포인터를 call 대신 inline 시킬 수 있는 환경은 런타임에 JIT를 할 수 있는 VM 계열의 환경에서 가능할 수 있습니다. 하지만, 그게 가능하다고 해도 전달받은 코드 조각에 따라 내부 코드를 인라인시켜버리면 (그 부분만 다른) 동일한 메서드의 기계어 코드 묶음이 다중으로 만들어지는 것이기 때문에 아마도 닷넷 JIT 컴파일러가 거기까지 신경쓰지는 않았을 것으로 보입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 정말 그럴까요? 혹시나 그래도 만에 하나 닷넷 JIT 컴파일러가 sealed 유형의 메서드를 인라인시켜주지는 않을까요? 전에도 함수 인라인에 대해 설명한 적이 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - 인라인 메서드(inline methods) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13063'>https://www.sysnet.pe.kr/2/0/13063</a> C# - JIT 컴파일러의 인라인 메서드 처리 유무 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/13064'>https://www.sysnet.pe.kr/2/0/13064</a> </pre> <br /> 그런 의미에서 봤을 때, 함수가 인라인되었다는 사실을 확인하는 것이 약간 번거롭습니다. "<a target='tab' href='https://forum.dotnetdev.kr/t/topic/7592'>매우 작은 메서드의 주입</a>" 글에서 질문하신 분이 나중에 덧글로 sealed가 적용된 경우에는 인라인이 된다고 했는데... 음... 어떻게 확인해서 그런 결론을 낸 것인지는 모르겠지만 적어도 제가 위에서 설명한 글의 의미에 해당하는 인라인은 아닙니다.<br /> <br /> 아마도, 단순히 실행 속도가 근소하게 빨라진 것에서 인라인되었다고 판단한 것으로 보이지만, 그것은 순전히 sealed 메서드의 호출이 다른 호출보다 가벼워서 그런 것일 뿐 엄밀한 의미에서의 인라인은 아닙니다.<br /> <br /> 확인을 위해 다음과 같이 간단한 코드를 만들고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // .NET 7 + x64 + Release 빌드 namespace ConsoleApp2; public interface ITestType { public int Increment(int x); } <span style='color: blue; font-weight: bold'>public sealed class TestType</span> : ITestType { public int Increment(int x) { return (x + 1); } } internal class Program { static void Main(string[] args) { TestType instance = new TestType(); <span style='color: blue; font-weight: bold'>TestFuncPtr(instance);</span> Console.ReadLine(); <span style='color: blue; font-weight: bold'>// JIT 완료 후 디버거 연결을 위해!</span> } public static void TestFuncPtr(ITestType test) { <span style='color: blue; font-weight: bold'>test.Increment(5);</span> } } </pre> <br /> Release 빌드로 (실행 전이 아닌, 이미) 실행된 프로세스에 WinDbg를 연결한 다음 JIT 컴파일러가 생성해 놓은 기계어 코드를 봐야 합니다.<br /> <br /> 따라서 이렇게 TestFuncPtr의 JIT 코드 위치를 알아내고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:005> <span style='color: blue; font-weight: bold'>!name2ee ConsoleApp2!Program.TestFuncPtr</span> Module: 00007ffd1c42cf48 Assembly: ConsoleApp2.dll Token: 000000000600000A MethodDesc: 00007ffd1c42ef30 Name: ConsoleApp2.Program.TestFuncPtr(ConsoleApp2.ITestType) <span style='color: blue; font-weight: bold'>JITTED Code Address: 00007ffd1c360750</span> </pre> <br /> 최종적으로 기계어 코드에 call 메서드가 있는지 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:005> <span style='color: blue; font-weight: bold'>!U /d 00007ffd1c360750</span> Normal JIT generated code ConsoleApp2.Program.TestFuncPtr(ConsoleApp2.ITestType) ilAddr is 00000178D0ED20BC pImport is 0000025E7AC8C340 Begin 00007FFD1C360750, size 2b C:\temp\ConsoleApp1\ConsoleApp2\Program.cs @ 28: >>> 00007ffd`1c360750 55 push rbp 00007ffd`1c360751 4883ec20 sub rsp,20h 00007ffd`1c360755 488d6c2420 lea rbp,[rsp+20h] 00007ffd`1c36075a 48894d10 mov qword ptr [rbp+10h],rcx 00007ffd`1c36075e 488b4d10 mov rcx,qword ptr [rbp+10h] 00007ffd`1c360762 49bb08001b1cfd7f0000 mov r11,7FFD1C1B0008h 00007ffd`1c36076c ba05000000 <span style='color: blue; font-weight: bold'>mov edx,5</span> 00007ffd`1c360771 41ff13 <span style='color: blue; font-weight: bold'>call qword ptr [r11]</span> C:\temp\ConsoleApp1\ConsoleApp2\Program.cs @ 29: 00007ffd`1c360774 90 nop 00007ffd`1c360775 4883c420 add rsp,20h 00007ffd`1c360779 5d pop rbp 00007ffd`1c36077a c3 ret </pre> <br /> 보다시피 인라인되지 않았습니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> [2023-07-15 업데이트] C/C++의 template을 잊고 있었군요. ^^ 아래는 트친의 의견입니다.<br /> <blockquote class="twitter-tweet"><p lang="ko" dir="ltr">하지만 C++ 에서 기본적으로 제공하는 sort는 컴파일이 끝난 다음에 함수 포인터를 넘기는 케이스로 사용 하는 경우는 거의 없고, 로 functor 나 lambda 를 넘겨서 사용하게 되며, 이 경우 sort 의 template 이 컴파일 되는 시점에 넘겨받은 functor 나 lambda 가 inline 화 되어 들어가게 됩니다.</p>— Lyn T Heo (@lyntohno) <a href="https://twitter.com/lyntohno/status/1679792624860016640?ref_src=twsrc%5Etfw">July 14, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1886
(왼쪽의 숫자를 입력해야 합니다.)