Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

C# - 지연 실행이 꼭 필요한 상황이 아니라면 singleton 패턴에서 DCLP보다는 static 초기화를 권장

전에도 이에 대해 다룬 적이 있지만,

C# Singleton 인스턴스 생성
; https://www.sysnet.pe.kr/2/0/896

이번엔 간단한 성능 테스트를 해보겠습니다. DCLP(Double Checked Locking Pattern)로는 이렇게 코딩하고,

public class DCLP
{
    static DCLP _instance = null;
    static object _lock = new ();

    public int Add(int a, int b)
    {
        return a + b;
    }

    public static DCLP Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lock)
                {
                    if (_instance == null)
                    {
                        _instance = new();
                    }
                }
            }

            return _instance;
        }
    }
}

static 초기화 처리는 이런 식으로 할 텐데요,

public class CCtor
{
    static CCtor _instance = new CCtor();

    public int Add(int a, int b)
    {
        return a + b;
    }

    public static CCtor Instance
    {
        get
        {
            return _instance;
        }
    }
}

DCLP의 경우 코드도 길기 때문에 그냥 봐도 속도 면에서 불리해 보입니다. 게다가 static 초기화의 경우 단순한 인스턴스 반환이므로 JIT 컴파일 시 최적화로 인해 in-line 처리를 하므로 사실상 메서드 호출이 아닌, 값을 직접 사용하는 식으로 처리하기 때문에 비교 불가의 성능을 보입니다.

아래는 그 테스트 결과입니다.

int count = 1;
action(1, "touch-JIT", DCLPLoop, count);
action(1, "touch-JIT", CCtorLoop, count);

count = 500;
action(100000, "DCLPLoop", DCLPLoop, count);
action(100000, "CCtorLoop", CCtorLoop, count);

/* 출력 결과
touch-JIT : 0
touch-JIT : 0

DCLPLoop : 658
CCtorLoop : 30
*/

(첨부 파일은 이 글의 예제 코드를 포함합니다.)

의미상으로 보면, DCLP의 경우에도 if 문 처리 정도의 부하만 있을 듯하지만, 인라인 시킬 수 없다는 단점으로 인해 메서드 호출의 prologue/epilogue 코드 실행을 동반하면서 적어도 다음의 굵은 폰트 영역에 해당하는 코드는 매번 실행되는 부하를 갖게 됩니다.

public static DCLP Instance
    82:         {
    83:             get
    84:             {
    85:                 if (_instance == null)
00F00CA0 55                   push        ebp  
00F00CA1 8B EC                mov         ebp,esp  
00F00CA3 57                   push        edi  
00F00CA4 83 EC 20             sub         esp,20h  
00F00CA7 8D 7D DC             lea         edi,[ebp-24h]  
00F00CAA B9 07 00 00 00       mov         ecx,7  
00F00CAF 33 C0                xor         eax,eax  
00F00CB1 F3 AB                rep stos    dword ptr es:[edi]  
00F00CB3 83 3D F0 42 DE 00 00 cmp         dword ptr ds:[0DE42F0h],0  
00F00CBA 74 05                je          ConsoleApp2.DCLP.get_Instance()+021h (0F00CC1h)  
00F00CBC E8 BF F0 7C 73       call        746CFD80  
00F00CC1 33 D2                xor         edx,edx  
00F00CC3 89 55 E0             mov         dword ptr [ebp-20h],edx  
00F00CC6 83 3D 7C 35 A9 03 00 cmp         dword ptr ds:[3A9357Ch],0  
00F00CCD 75 6E                jne         ConsoleApp2.DCLP.get_Instance()+09Dh (0F00D3Dh)  
    86:                 {
    87:                     lock (_lock)
00F00CCF A1 80 35 A9 03       mov         eax,dword ptr ds:[03A93580h]  
00F00CD4 89 45 E0             mov         dword ptr [ebp-20h],eax  
00F00CD7 33 D2                xor         edx,edx  
00F00CD9 89 55 E4             mov         dword ptr [ebp-1Ch],edx  
00F00CDC 8D 55 E4             lea         edx,[ebp-1Ch]  
00F00CDF 8B 4D E0             mov         ecx,dword ptr [ebp-20h]  
00F00CE2 E8 69 77 C2 71       call        System.Threading.Monitor.Enter(System.Object, Boolean ByRef) (72B28450h)  
    88:                     {
    89:                         if (_instance == null)
00F00CE7 83 3D 7C 35 A9 03 00 cmp         dword ptr ds:[3A9357Ch],0  
00F00CEE 75 24                jne         ConsoleApp2.DCLP.get_Instance()+074h (0F00D14h)  
    90:                         {
    91:                             _instance = new();
00F00CF0 B9 9C 61 DE 00       mov         ecx,0DE619Ch  
00F00CF5 E8 FA 23 ED FF       call        CORINFO_HELP_NEWSFAST (0DD30F4h)  
00F00CFA 89 45 DC             mov         dword ptr [ebp-24h],eax  
00F00CFD 8B 4D DC             mov         ecx,dword ptr [ebp-24h]  
00F00D00 FF 15 DC 61 DE 00    call        dword ptr [Pointer to: CLRStub[MethodDescPrestub]@cdbb715500f004e5 (0DE61DCh)]  
00F00D06 8B 45 DC             mov         eax,dword ptr [ebp-24h]  
00F00D09 8D 15 7C 35 A9 03    lea         edx,ds:[3A9357Ch]  
00F00D0F E8 EC DE 42 73       call        7432EC00  
    92:                         }
    93:                     }
00F00D14 90                   nop  
00F00D15 C7 45 EC 00 00 00 00 mov         dword ptr [ebp-14h],offset ConsoleApp2.DCLP.get_Instance()+078h (00h)  
00F00D1C C7 45 F0 FC 00 00 00 mov         dword ptr [ebp-10h],0FCh  
00F00D23 68 48 0D F0 00       push        offset ConsoleApp2.DCLP.get_Instance()+0A8h (0F00D48h)  
00F00D28 EB 00                jmp         ConsoleApp2.DCLP.get_Instance()+08Ah (0F00D2Ah)  
00F00D2A 0F B6 45 E4          movzx       eax,byte ptr [ebp-1Ch]  
00F00D2E 85 C0                test        eax,eax  
00F00D30 74 08                je          ConsoleApp2.DCLP.get_Instance()+09Ah (0F00D3Ah)  
00F00D32 8B 4D E0             mov         ecx,dword ptr [ebp-20h]  
00F00D35 E8 6D DD 42 73       call        7432EAA7  
00F00D3A 58                   pop         eax  
00F00D3B FF E0                jmp         eax  
    94:                 }
    95: 
    96:                 return _instance;
00F00D3D A1 7C 35 A9 03       mov         eax,dword ptr ds:[03A9357Ch]  
00F00D42 8D 65 FC             lea         esp,[ebp-4]  
00F00D45 5F                   pop         edi  
00F00D46 5D                   pop         ebp  
00F00D47 C3                   ret  

이런 면에서 볼 때, '지연 처리'가 절실하게 필요한 상황이 아니라면 static 초기화를 이용한 singleton 처리가 여러모로 장점을 갖습니다.




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







[최초 등록일: ]
[최종 수정일: 12/30/2020]

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

비밀번호

댓글 작성자
 



2024-06-13 09시26분
Reordering on an Alpha processor
; https://www.cs.umd.edu/~pugh/java/memoryModel/AlphaReordering.html

The "Double-Checked Locking is Broken" Declaration
; https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Lock-free algorithms: The singleton constructor
; https://devblogs.microsoft.com/oldnewthing/20110406-00/?p=11023

Lock-free reference-counting a TLS slot using atomics, part 1
; https://devblogs.microsoft.com/oldnewthing/20240612-00/?p=109887

Lock-free reference-counting a TLS slot using atomics, part 2
; https://devblogs.microsoft.com/oldnewthing/20240613-00/?p=109892

Lock-free reference-counting a TLS slot using atomics, part 3
; https://devblogs.microsoft.com/oldnewthing/20240614-00/?p=109902
정성태

1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13704정성태8/2/20249584닷넷: 2290. C# - 간이 dotnet-dump 프로그램 만들기파일 다운로드1
13703정성태8/1/20248146닷넷: 2289. "dotnet-dump ps" 명령어가 닷넷 프로세스를 찾는 방법
13702정성태7/31/20249133닷넷: 2288. Collection 식을 지원하는 사용자 정의 타입을 CollectionBuilder 특성으로 성능 보완파일 다운로드1
13701정성태7/30/20249510닷넷: 2287. C# 13 - (4) Indexer를 이용한 개체 초기화 구문에서 System.Index 연산자 허용파일 다운로드1
13700정성태7/29/20249430디버깅 기술: 200. DLL Export/Import의 Hint 의미
13699정성태7/27/20249475닷넷: 2286. C# 13 - (3) Monitor를 대체할 Lock 타입파일 다운로드1
13698정성태7/27/20249290닷넷: 2285. C# - async 메서드에서의 System.Threading.Lock 잠금 처리파일 다운로드1
13697정성태7/26/20248605닷넷: 2284. C# - async 메서드에서의 lock/Monitor.Enter/Exit 잠금 처리파일 다운로드1
13696정성태7/26/20248329오류 유형: 920. dotnet publish - error NETSDK1047: Assets file '...\obj\project.assets.json' doesn't have a target for '...'
13695정성태7/25/20248625닷넷: 2283. C# - Lock / Wait 상태에서도 STA COM 메서드 호출 처리파일 다운로드1
13694정성태7/25/20248791닷넷: 2282. C# - ASP.NET Core Web App의 Request 용량 상한값 (Kestrel, IIS)
13693정성태7/24/20248002개발 환경 구성: 717. Visual Studio - C# 프로젝트에서 레지스트리에 등록하지 않은 COM 개체 참조 및 사용 방법파일 다운로드1
13692정성태7/24/20249190디버깅 기술: 199. Windbg - 리눅스에서 뜬 닷넷 응용 프로그램 덤프 파일에 포함된 DLL의 Export Directory 탐색
13691정성태7/23/20248173디버깅 기술: 198. Windbg - 스레드의 Win32 Message Queue 정보 조회
13690정성태7/23/20247660오류 유형: 919. Visual C++ 리눅스 프로젝트 - error : ‘u8’ was not declared in this scope
13689정성태7/22/20249895디버깅 기술: 197. Windbg - PE 포맷의 Export Directory 탐색
13688정성태7/21/20248387닷넷: 2281. C# - Lock / Wait 상태에서도 일부 Win32 메시지 처리파일 다운로드1
13687정성태7/19/20249213닷넷: 2280. C# - PostThreadMessage로 보낸 메시지를 Windows Forms에서 수신하는 방법파일 다운로드1
13686정성태7/19/20248868오류 유형: 918. Visual Studio - ATL Simple Object 추가 시 error C2065: 'IDR_...': undeclared identifier
13685정성태7/19/20248722스크립트: 66. Windows 디렉터리 경로를 WSL의 /mnt 포맷으로 구하는 방법 - 두 번째 이야기
13684정성태7/19/20249345닷넷: 2279. C# - 문자열 보간식 사례 (예: 조건 연산자 사용)
13683정성태7/18/20248439오류 유형: 917. ClrMD - Linux 환경의 .NET 5 덤프 분석 시 hang 현상
13682정성태7/18/20248788닷넷: 2278. WPF - 스레드에 종속되는 DependencyObject파일 다운로드1
13681정성태7/17/20248164닷넷: 2277. C# 13 - (2) 메서드 그룹의 자연 타입 개선 (메서드 추론 개선)파일 다운로드1
13680정성태7/16/20249170닷넷: 2276. C# - Method Group, Natural Type, function_type파일 다운로드1
13679정성태7/16/20247558Linux: 76. Linux - C++ (getaddrinfo 등을 담고 있는) libnss 정적 링크
1  2  3  4  5  6  7  8  9  10  [11]  12  13  14  15  ...