Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)

MEF가 적용된 ASP.NET 웹 사이트를 제니퍼 닷넷으로 모니터링 해본 결과!

지인 한 분이, 이번 참에 MEF(Managed Extensibility Framework)를 공부할 겸 간단하게 prototype 형식의 웹 사이트를 하나 만들었다고 하시면서... MEF 적용 자체는 Biz 계층과 Dac 계층에 모두 적용을 했는데, 속도가 너무 안 나와서 Biz만 MEF를 적용하는 것이 나을지, 아니면 Dac 쪽만 MEF를 적용하는 것이 나을 지에 대해서 제게 의견을 구했습니다.

사실 ^^ 저도 MEF는 자료로 보기만 하고 직접 사용해 본적은 없기 때문에 ... 마침 잘 되었다 싶어서 그 분께 관련 테스트 소스를 요청해서 받았습니다. 그런 후 '제니퍼 닷넷'으로 검사해 보았는데요... 이번 글은 그에 대한 결과를 공유하는 차원에서 써 봅니다. ^^




최대한 재현을 잘 해내기 위해 소스 코드를 요청했지만, 비슷한 뼈대의 구성으로 ASP.NET에 MEF를 적용한 예제가 아래의 글에도 공개되어 있으니 이 글을 읽으시는 분들도 쉽게 재현을 할 수 있을 것입니다.

MEF with ASP.NET - "Hello World!"
; http://www.codeproject.com/KB/aspnet/MEFwithASPNET.aspx

소스 코드를 살펴보면 다음과 같은데요.

protected void Page_Load(object sender, EventArgs e) 
{
    DirectoryCatalog catalog = new DirectoryCatalog(
        System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
    CompositionContainer container = new CompositionContainer(catalog);
    container.ComposeParts(this);
}

지인이 보내주신 코드도 위의 방법과 크게 다르지 않았습니다.

실행하고 곧바로 default 웹 페이지를 방문했는 데, 과연 다음과 같이 3초 가까운 시간이 흐른 것을 확인할 수 있었습니다.

mef_perf_1.png

위의 화면에서 발견할 수 있는 또 다른 문제는 "CPU_T" 열에 있는 2,606ms 수치입니다. 즉, default.aspx 웹 페이지가 응답까지 걸린 시간이 2,789ms가 걸렸는데 그동안 계속해서 CPU가 거의 100% 일을 한 것이나 다름없다는 의미입니다. 보통, 데이터베이스 조회 시에 DB가 무거워서 GAP 시간이 수 초가 넘게 걸려도 웹 서버의 CPU는 50ms 이하로 일을 하는 경우가 대부분이어서 웹 서버 자체는 사실 부하가 심하지 않은 경우가 대부분입니다.

그런데, 위의 경우는 웹 페이지가 방문되는 횟수가 늘어날 수록 CPU는 더욱 심하게 일하게 되어 웹 서버 자체의 운영에 심각한 영향을 줄 수 있음을 보여주는 것입니다.

아쉽게도, 제니퍼 닷넷이 보여주는 '기본 프로파일 정보'에는 DB/WCF/COM+/ASMX/.NET Remoting 이외에는 어떤 부분에서 시간이 걸렸다는 것을 보여주지 않기 때문에 위의 X-View 상세 보기 화면에서는 더 이상의 단서를 발견할 수는 없었습니다.

그래도 ^^ 여기서 이야기가 끝이면 아마 이 글을 쓰지도 않았겠지요.

아직 많은 분들이 모르시지만 ^^ '제니퍼 닷넷'은 사용자 어셈블리의 모든 메서드 호출을 프로파일링 할 수 있는 기능이 추가되어 있습니다. 이 기능을 활성화 하려면 '제니퍼 닷넷 에이전트'가 설치된 폴더의 'profile.ini' 파일에서 제공되는 "application_allmethods" 옵션 값을 "1"로 바꿔주어야 합니다.

[methodprofile]
application_allmethods=1

이렇게 변경하고 웹 사이트를 재생(recycle)해주면 이제 사용자 어셈블리의 (생성자를 제외한) 모든 메서드 호출에 대해서 프로파일링을 남겨줍니다. 아래는 위의 MEF 웹 사이트를 방문했을 때 남겨진 프로파일링 기록입니다.

mef_perf_2.png

훨씬 자세해졌지요. ^^ 보시는 것처럼, 전체적인 ASP.NET 웹 페이지의 ProcessRequest 처리에는 3,655ms가 걸렸고 그 이하 Page_Load와 UserControl의 Page_Load에서 각각 1.5초씩 지연된 것을 볼 수 있습니다.

아하... 결국 MEFManager.Compose가 재귀 호출되면서 한 번 호출될 때마다 약 90ms씩 CPU를 사용하면서 지연이 된 것임을 위의 프로파일 결과로 한 눈에 알 수 있습니다.




정리해 보면, 웹 사이트의 'bin' 폴더에는 총 650KB 용량의 DLL 파일들이 놓여 있었고 MEF가 적용된 웹 페이지는 Page_Load 시마다 그 DLL을 모두 로드하고 .NET Reflection으로 Export 특성이 적용된 타입들을 찾아내는 작업을 하고 있는 것이었습니다.

위와 같은 상황을 전달해 드렸더니, 그 분은 MEF가 적용된 DLL을 별도의 폴더로 분리하고 DirectoryCatalog를 그 폴더로 지정해서 속도 향상을 보았다고 합니다.

그런데, 일단 제 의견은 "(DirectoryCatalog를 쓰는 한) MEF는 ASP.NET에 적용해서는 안된다"입니다. 왜냐하면, 프로젝트가 진행되면서 점점 더 MEF가 적용된 DLL은 늘어갈 것이고 그때 쯤이면 또다시 성능 문제가 불거져 나올 것이기 때문입니다.
(결국,,, 이 글을 통해서 제니퍼 닷넷의 application_allmethods 옵션에 대한 자연스러운 홍보가 된 것 같습니다. ^^)

추가: MEF의 DirectoryCatalog를 cache 해두면 위의 성능 문제를 개선할 수 있습니다.




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 7/10/2021]

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

비밀번호

댓글 작성자
 



2011-12-21 11시52분
[짜두] DirectoryCatalog 를 사용할때 느려지는건 MEF 뿐만 아니라 CAG 에서도 마찬가지 였던거 같아요. DLL 갯수가 많을 수록 그리고 해당 DLL 에 화면이 많을수록 시간이 기하급수적으로 늘어났던거 같은데...
[guest]
2011-12-22 08시33분
그렇죠. ^^ CAG도 DirectoryCatalog 비슷한 형식으로 DLL을 읽어들이는 방식을 지원하고, 대개 그것을 쓰죠. 그런데... CAG를 ASP.NET에 사용하려는 노력은 없었기 때문에 Page_Load 시마다 발생하는 성능 저하 현상은 겪을 수 없었지만, MEF는 ASP.NET에 적용하려고 시도하고 있다는 점이 다릅니다. ^^ (사실... Global.asax 정도에서 처음 한번 느려지는 거라면 ASP.NET에서도 쓸만할 수 있겠지요.)
정성태
2011-12-22 08시44분
[짜두] 이것도 동적로딩 머 이런 방식으로 회피할 수 있는 방법이 먼가 있지 않을까요? 흠 ASP.NET 이라 그것도 좀 애매하긴 하네요. 어떤페이지가 언제 호출될 줄 알고... 저도 Kevin 님 의견에 전적 동의입니다~ㅎ
[guest]
2011-12-22 10시17분
[엄준일] 저도 이와 관련하여 여러 가지 테스트를 해 보니,
문제는 DI Container 가 문제더군요.

웹 사이트에 DI Container나 Injection 과 같은 프레임워크를 쓴다는 것은 성능을 일부 포기할 수 밖에 없더라구요.
CPU 꽤 잡아먹어요.
[guest]
2011-12-22 11시42분
"짜두" 님과 대화하다 보니... 왠지 DirectoryCatalog를 cache하라는 하늘의 메시지가 내려와서... 테스트해 보니 잘 되는군요. ^^ 다음의 글을 참고하세요.

MEF를 ASP.NET에 성능 손실 없이 적용하려면?
; http://www.sysnet.pe.kr/2/0/1204
정성태
2011-12-22 05시03분
[lancers] Page_Load마다 Catalog를 재구성한다는 건 엄청난 오버일 듯...
거기다 그 중에서도 가장 Cost가 쓴 DirectoryCatalog는 더 답이 안 나오겠죠.
그 분이 사용하셨다는 꽁수대로 한다면, 가장 성능을 높일 수 있는 방법은 폴더를 점점 더 잘게 찢어서 궁극적으로는 DLL당 폴더 하나씩 하는 수 밖에 없을 듯..
(그럴바엔 차라리 AssemblyCatalog를 쓰겠지만)

Cache를 하는 것이 당연히 방법인데, 이것마저도 런타임에 매번 최초 Discovery를 하는 것은 상당한 부담이 됩니다.
그래서 예전에 적용했던 방법이 DirectoryCatalog를 구성한 후 이 정보를 바탕으로 XML로 떨구는 툴을 하나 만들었습니다.
말하자면 DirectoryCatalog를 사전에 생성해두는 것과 같은 효과랄까..
그래서 이후 DirectoryCatalog가 아닌 Custom하게 만든 ConfigurationCatalog를 쓰는 형태로 하고..
실제 어셈블리 로딩은 특정 Type이 MEF에서 최초로 요청될 때 일어나는 것으로..

이론상 이렇게 해두고..
정말로 동적인 변경이 필요한 경우에는 새로운 어셈블리를 로딩한 후 Recomposition을 하면 됩니다. ㅋㅋㅋ
[guest]

... 91  92  93  94  95  96  97  98  99  100  101  102  103  [104]  105  ...
NoWriterDateCnt.TitleFile(s)
11326정성태10/15/201718520.NET Framework: 690. coreclr 소스코드로 알아보는 .NET 4.0의 모듈 로딩 함수 [1]
11325정성태10/14/201719405.NET Framework: 689. CLR 4.0 환경에서 DLL 모듈의 로드 주소(Base address) 알아내는 방법
11324정성태10/13/201720983디버깅 기술: 101. windbg - "*** WARNING: Unable to verify checksum for" 경고 없애는 방법
11322정성태10/13/201718407디버깅 기술: 100. windbg - .NET 4.0 응용 프로그램의 Main 메서드에 Breakpoint 걸기
11321정성태10/11/201719926.NET Framework: 688. NGen 모듈과 .NET Profiler
11320정성태10/11/201720784.NET Framework: 687. COR_PRF_USE_PROFILE_IMAGES 옵션과 NGen의 "profiler-enhanced images" [1]
11319정성태10/11/201728297.NET Framework: 686. C# - string 배열을 담은 구조체를 직렬화하는 방법
11318정성태10/7/201721081VS.NET IDE: 122. 비주얼 스튜디오에서 관리자 권한을 요구하는 C# 콘솔 프로그램 제작 [1]
11317정성태10/4/201726192VC++: 120. std::copy 등의 함수 사용 시 _SCL_SECURE_NO_WARNINGS 에러 발생
11316정성태9/30/201724244디버깅 기술: 99. (닷넷) 프로세스(EXE)에 디버거가 연결되어 있는지 아는 방법 [4]
11315정성태9/29/201740258기타: 68. "시작하세요! C# 6.0 프로그래밍: 기본 문법부터 실전 예제까지" 구매하신 분들을 위한 C# 7.0/7.1 추가 문법 PDF [8]
11314정성태9/28/201722087디버깅 기술: 98. windbg - 덤프 파일로부터 닷넷 버전 확인하는 방법
11313정성태9/25/201719365디버깅 기술: 97. windbg - 메모리 덤프로부터 DateTime 형식의 값을 알아내는 방법파일 다운로드1
11312정성태9/25/201722519.NET Framework: 685. C# - 구조체(값 형식)의 필드를 리플렉션을 이용해 값을 바꾸는 방법파일 다운로드1
11311정성태9/20/201716865.NET Framework: 684. System.Diagnostics.Process 객체의 명시적인 해제 권장
11310정성태9/19/201720368.NET Framework: 683. WPF의 Window 객체를 생성했는데 GC 수집 대상이 안 되는 이유 [3]
11309정성태9/13/201718446개발 환경 구성: 335. Octave의 명령 창에서 실행한 결과를 복사하는 방법
11308정성태9/13/201719520VS.NET IDE: 121. 비주얼 스튜디오에서 일부 텍스트 파일을 무조건 메모장으로만 여는 문제파일 다운로드1
11307정성태9/13/201722104오류 유형: 421. System.Runtime.InteropServices.SEHException - 0x80004005
11306정성태9/12/201720185.NET Framework: 682. 아웃룩 사용자를 위한 중국어 스팸 필터 Add-in
11305정성태9/12/201721669개발 환경 구성: 334. 기존 프로젝트를 Visual Studio를 이용해 Github의 신규 생성된 repo에 올리는 방법 [1]
11304정성태9/11/201718743개발 환경 구성: 333. 3ds Max를 Hyper-V VM에서 실행하는 방법
11303정성태9/11/201722016개발 환경 구성: 332. Inno Setup 파일의 관리자 권한을 제거하는 방법
11302정성태9/11/201718280개발 환경 구성: 331. SQL Server Express를 위한 방화벽 설정
11301정성태9/11/201717170오류 유형: 420. SQL Server Express 연결 오류 - A network-related or instance-specific error occurred while establishing a connection to SQL Server.
11300정성태9/10/201721101.NET Framework: 681. dotnet.exe - run, exec, build, restore, publish 차이점 [3]
... 91  92  93  94  95  96  97  98  99  100  101  102  103  [104]  105  ...