Microsoft MVP성태의 닷넷 이야기
.NET Framework: 157. C# 4.0 - dynamic 키워드 [링크 복사], [링크+제목 복사]
조회: 17153
글쓴 사람
정성태 (kevin13@chol.net)
홈페이지
첨부 파일

C# 4.0 - dynamic 키워드



아래의 재미있는 글이 있네요. ^^

Welcome to Dynamic C#(4) - 극과극 비교체험.
; http://vsts2010.net/114

Reflection을 조금 깊게 아시는 분들이라면, 왠지 실험 결과가 그다지 공평한 것 같진 않음을 눈치채셨을 것입니다.

그래서, 저도 한번 테스트해보기로 했습니다.
제가 구성한 VPC에서 위의 소스 코드에서 제시한 데로 실행했을 때 다음과 같은 결과가 나왔습니다.

Ctrl + F5 DEBUG빌드
 
Normal Time : 00:00:00.1815792
Dynamic Time : 00:00:00.4464240
Reflection Time : 00:00:11.3110800

자, 이제 Reflection 부분을 개선해 보겠습니다. 우선, Dynamic 메서드 호출 부분을 Reflector로 분석해 보면 다음과 같이 구성되어 있는 것을 확인할 수 있습니다.

dynamic test = new Test();
test.FivePlusFive();

==> 컴파일러에 의해서 다음과 같이 변경되어 출력

public void DynamicCall()
{
    object test = new Test();
    if (<DynamicCall>o__SiteContainer0.<>p__Site1 == null)
    {
        <DynamicCall>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(Binder.InvokeMember(0, "FivePlusFive", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(0, null) }));
    }
    <DynamicCall>o__SiteContainer0.<>p__Site1.Target.Invoke(<DynamicCall>o__SiteContainer0.<>p__Site1, test);
}

가만히 보면, 별도의 dynamic 호출을 래퍼해주는 클래스를 컴파일 시에 생성해 주고 static 으로 인스턴스를 보관해 두었다가 실행하고 있습니다.

오호... 거기서 벌써 ReflectionCall 이 손해를 보고 있지요. 그래서 ReflectionCall도 다음과 같이 보정해 줄 수 있습니다.

static MethodInfo methodInfo;
public void ReflectionCall()
{
    Test test = new Test();
    if (methodInfo == null)
    {
        Type type = test.GetType();
        methodInfo = type.GetMethod("FivePlusFive");
    }

    methodInfo.Invoke(test, 
        BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public,
        null, new object[] { }, CultureInfo.InvariantCulture);
}

이렇게 해서 실행한 결과는?

Normal Time : 00:00:00.2387616
Dynamic Time : 00:00:00.4895616
Reflection Time : 00:00:06.9832752

오호... 거의 절반으로 줄었습니다. 그래도 성능이 dynamic에 비하면 한참 떨어집니다. 개선의 여지가 있을까요?

조금 더 자세히 보면, dynamic method 호출은 그에 해당하는 다음과 같은 private static class를 만들어 주는 것을 확인할 수 있습니다.

[CompilerGenerated]
private static class <DynamicCall>o__SiteContainer0
{
    // Fields
    public static CallSite<Action<CallSite, object>> <>p__Site1;
}

System.Runtime.CompilerServices.CallSite가 관여하는 것을 보면, 아마도 컴파일러에 의한 동적 코드 생성이 관여되지 않을까 예상이 됩니다. 요즘 많이들 사용하는 Unity나 Spring.NET 같은 류의 IoC컨테이너들이 Reflection으로 로드한 타입의 메서드 호출 속도를 빠르게 하기 위하여 동적으로 코드를 생성하는 것과 같은 방식이 사용되지 않을까 싶은데요.

이를 위해 .NET 2.0 에 이미 소개되어 있는 Lightweight Code Generator(이하, LCG)를 사용해서 Reflection API를 좀더 공평하게 개선을 해보겠습니다. (압니다... 알아요. LCG 사용하면 코드가 더 복잡해진다는 거. ^^;)

delegate int FivePlusFiveDelegate(object Instance);
static FivePlusFiveDelegate funcFivePlusFIve;

public void ReflectionCall()
{
    Test test = new Test();

    if (funcFivePlusFIve == null)
    {
        MethodInfo myMethodHandle = typeof(Test).GetMethod("FivePlusFive");

        DynamicMethod dm = new DynamicMethod(
            Guid.NewGuid().ToString(),
            typeof(int), new Type[]{ typeof(object) },
            typeof(Program), false);
            
        ILGenerator il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Call, myMethodHandle);
        il.Emit(OpCodes.Ret);

        funcFivePlusFIve = (FivePlusFiveDelegate)
                dm.CreateDelegate(typeof(FivePlusFiveDelegate));
    }

    funcFivePlusFIve(test);
}

결과는?

Normal Time : 00:00:00.1484736
Dynamic Time : 00:00:00.4454208
Reflection Time : 00:00:00.2798928

이번엔, 오히려 dynamic 키워드가 2배 가까이 느린 것을 볼 수 있습니다.

엄밀히, LCG는 Reflection 범주라기 보다는 동적 코드 생성에 가깝지요. 그렇긴 하지만, dynamic 키워드가 .NET 2.0에 소개된 LCG를 사용한 호출보다는 느리다는 것을 알 수 있습니다.

그래서... 결론이 바뀌죠? "Welcome to Dynamic C#(4) - 극과극 비교체험."글에서는 결론이 다음과 같이 나왔지요.


다이나믹이 리플렉션에 비해서는 월등히 빠르군요. 아마도 DLR의 힘을 빌려서 리플렉션 보다 훨씬 빠른 방식을 이용하는 것 같습니다. 이 부분에 대해서는 좀 조사를 해봐야 할거 같네요.



반면에, 제가 말하고자 하는 결론은 오히려,


dynamic 키워드가 DLR과의 호환을 위해 속도에서 손해를 보는 것일 뿐, DLR의 힘을 빌려서 리플렉션보다 훨씬 빠른 방식이 가능한 것은 아니라는 것입니다. 단순히 리플렉션의 속도를 개선하고자 하는 것이었다면, C# 컴파일러가 컴파일 시에 System.Runtime.CompilerServices.CallSite등의 코드를 삽입하기 보다는 오히려 LCG에 해당하는 코드를 넣었을 것입니다.



*** 첨부한 코드는 제가 변경한 프로젝트 파일입니다. (물론, VS 2010에서 열어야 합니다.)



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





[최초 등록일: ]
[최종 수정일: 8/23/2009 ]

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

비밀번호

댓글 쓴 사람
 



2009-08-24 03시31분
[워너비] 좋은 지적 감사합니다~. ㅋ
[손님]
2009-08-24 06시29분
워너비님 강좌 잘 보고 있습니다. ^^ 웹 상에서 서로들 그렇게 독립적으로 공부한 자료를 올려주시니 제 시간도 많이 절약되고.
제 글에 별 신경쓰지 마시고 앞으로도 많은 정보 공유 부탁드립니다. 저야 뭐... 이런 기회에 글 한번 올리는 거죠. ^^
kevin25
2009-08-27 01시16분
[[손님]] 헉.. 대단한 반전인데요.
정말 좋은 내용입니다. 리플렉션사용을 개선하기 위한 기초 자료로서 손색이 없군요.
저도 한번 따라서 코딩 해봐야 겠네요.
[손님]
2009-09-10 10시19분
[맨날맑음] 재미있는 포스트 잘 보았습니다..!!
문득.. '뛰는놈위에 나는놈있다' 라는 속담이 생각난건 저뿐일까요^^
(워너비님의 실력이 낮다고 하는 말은 아닙니다ㅜ)
여러 사람의 토론속에 좀더 낳은 코드를 배우는군요 감사합니다,.
[손님]

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
11803정성태1/10/2019153디버깅 기술: 121. windbg - 닷넷 Finalizer 스레드가 멈춰있는 현상
11802정성태1/7/2019209.NET Framework: 805. 두 개의 윈도우를 각각 실행하는 방법(Windows Forms, WPF)파일 다운로드1
11801정성태1/1/2019270개발 환경 구성: 427. Netsh의 네트워크 모니터링 기능
11800정성태12/28/2018179오류 유형: 509. WCF 호출 오류 메시지 - System.ServiceModel.CommunicationException: Internal Server Error
11799정성태12/19/2018382.NET Framework: 804. WPF(또는 WinForm)에서 UWP UI 구성 요소 사용하는 방법 [2]파일 다운로드1
11798정성태12/19/2018277개발 환경 구성: 426. vcpkg - "Building vcpkg.exe failed. Please ensure you have installed Visual Studio with the Desktop C++ workload and the Windows SDK for Desktop C++"
11797정성태12/19/2018201개발 환경 구성: 425. vcpkg - CMake Error: Problem with archive_write_header(): Can't create '' 빌드 오류
11796정성태12/19/2018224개발 환경 구성: 424. vcpkg - "File does not have expected hash" 오류를 무시하는 방법
11795정성태12/19/2018226Windows: 154. PowerShell - Zone 별로 DNS 레코드 유형 정보 조회
11794정성태12/17/2018233오류 유형: 508. Get-AzureWebsite : Request to a downlevel service failed.
11793정성태12/16/2018293개발 환경 구성: 423. NuGet 패키지 제작 - Native와 Managed DLL을 분리하는 방법
11792정성태12/11/2018332Graphics: 34. .NET으로 구현하는 OpenGL (11) - Per-Pixel Lighting파일 다운로드1
11791정성태12/11/2018245VS.NET IDE: 130. C/C++ 프로젝트의 시작 프로그램으로 .NET Core EXE를 지정하는 경우 닷넷 디버깅이 안 되는 문제
11790정성태12/11/2018259오류 유형: 507. Could not save daemon configuration to C:\ProgramData\Docker\config\daemon.json: Access to the path 'C:\ProgramData\Docker\config' is denied.
11789정성태12/10/2018343Windows: 153. C# - USB 장치의 연결 및 해제 알림을 위한 WM_DEVICECHANGE 메시지 처리파일 다운로드1
11788정성태12/4/2018288오류 유형: 506. SqlClient - Value was either too large or too small for an Int32.Couldn't store <2151292191> in ... Column
11787정성태11/29/2018605Graphics: 33. .NET으로 구현하는 OpenGL (9), (10) - OBJ File Format, Loading 3D Models파일 다운로드1
11786정성태11/29/2018312오류 유형: 505. OpenGL.NET 예제 실행 시 "Managed Debugging Assistant 'CallbackOnCollectedDelegate'" 예외 발생
11785정성태11/21/2018682디버깅 기술: 120. windbg 분석 사례 - ODP.NET 사용 시 Finalizer에서 System.AccessViolationException 예외 발생으로 인한 비정상 종료
11784정성태11/18/2018514Graphics: 32. .NET으로 구현하는 OpenGL (7), (8) - Matrices and Uniform Variables, Model, View & Projection Matrices파일 다운로드1
11783정성태11/18/2018420오류 유형: 504. 윈도우 환경에서 docker가 설치된 컴퓨터 간의 ping IP 주소 풀이 오류
11782정성태11/18/2018453Windows: 152. 윈도우 10에서 사라진 "Adapters and Bindings" 네트워크 우선순위 조정 기능 - 두 번째 이야기
11781정성태11/17/2018459개발 환경 구성: 422. SFML.NET 라이브러리 설정 방법파일 다운로드1
11780정성태11/17/2018367오류 유형: 503. vcpkg install bzip2 빌드 에러 - "Error: Building package bzip2:x86-windows failed with: BUILD_FAILED"
11779정성태11/17/2018458개발 환경 구성: 421. vcpkg 업데이트
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...