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]

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13917정성태4/30/202563VS.NET IDE: 199. Directory.Build.props에 정의한 속성에 대해 Condition 제약으로 값을 변경하는 방법
13916정성태4/23/2025463디버깅 기술: 221. WinDbg 분석 사례 - ASP.NET HttpCookieCollection을 다중 스레드에서 사용할 경우 무한 루프 현상 - 두 번째 이야기
13915정성태4/13/20251685닷넷: 2331. C# - 실행 시에 메서드 가로채기 (.NET 9)파일 다운로드1
13914정성태4/11/20251987디버깅 기술: 220. windbg 분석 사례 - x86 ASP.NET 웹 응용 프로그램의 CPU 100% 현상 (4)
13913정성태4/10/20251214오류 유형: 950. Process Explorer - 64비트 윈도우에서 32비트 프로세스의 덤프를 뜰 때 "Error writing dump file: Access is denied." 오류
13912정성태4/9/2025872닷넷: 2330. C# - 실행 시에 메서드 가로채기 (.NET 5 ~ .NET 8)파일 다운로드1
13911정성태4/8/20251115오류 유형: 949. WinDbg - .NET Core/5+ 응용 프로그램 디버깅 시 sos 확장을 자동으로 로드하지 못하는 문제
13910정성태4/8/20251266디버깅 기술: 219. WinDbg - 명령어 내에서 환경 변수 사용법
13909정성태4/7/20251771닷넷: 2329. C# - 실행 시에 메서드 가로채기 (.NET Framework 4.8)파일 다운로드1
13908정성태4/2/20252182닷넷: 2328. C# - MailKit: SMTP, POP3, IMAP 지원 라이브러리
13907정성태3/29/20251988VS.NET IDE: 198. (OneDrive, Dropbox 등의 공유 디렉터리에 있는) C# 프로젝트의 출력 경로 변경하기
13906정성태3/27/20252258닷넷: 2327. C# - 초기화되지 않은 메모리에 접근하는 버그?파일 다운로드1
13905정성태3/26/20252296Windows: 281. C++ - Windows / Critical Section의 안정화를 위해 도입된 "Keyed Event"파일 다운로드1
13904정성태3/25/20251916디버깅 기술: 218. Windbg로 살펴보는 Win32 Critical Section파일 다운로드1
13903정성태3/24/20251536VS.NET IDE: 197. (OneDrive, Dropbox 등의 공유 디렉터리에 있는) C++ 프로젝트의 출력 경로 변경하기
13902정성태3/24/20251748개발 환경 구성: 742. Oracle - 테스트용 hr 계정 및 데이터 생성파일 다운로드1
13901정성태3/9/20252138Windows: 280. Hyper-V의 3가지 Thread Scheduler (Classic, Core, Root)
13900정성태3/8/20252361스크립트: 72. 파이썬 - SQLAlchemy + oracledb 연동
13899정성태3/7/20251834스크립트: 71. 파이썬 - asyncio의 ContextVar 전달
13898정성태3/5/20252154오류 유형: 948. Visual Studio - Proxy Authentication Required: dotnetfeed.blob.core.windows.net
13897정성태3/5/20252380닷넷: 2326. C# - PowerShell과 연동하는 방법 (두 번째 이야기)파일 다운로드1
13896정성태3/5/20252204Windows: 279. Hyper-V Manager - VM 목록의 CPU Usage 항목이 항상 0%로 나오는 문제
13895정성태3/4/20252248Linux: 117. eBPF / bpf2go - Map에 추가된 요소의 개수를 확인하는 방법
13894정성태2/28/20252284Linux: 116. eBPF / bpf2go - BTF Style Maps 정의 구문과 데이터 정렬 문제
13893정성태2/27/20252225Linux: 115. eBPF (bpf2go) - ARRAY / HASH map 기본 사용법
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...