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
정성태

... 76  77  78  79  80  81  82  83  84  85  86  87  88  [89]  90  ...
NoWriterDateCnt.TitleFile(s)
11709정성태9/30/201820327개발 환경 구성: 402. .NET Core 콘솔 응용 프로그램을 docker로 실행/디버깅하는 방법 [1]
11708정성태9/30/201822456개발 환경 구성: 401. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 [2]파일 다운로드1
11707정성태9/30/201823823오류 유형: 489. ASP.NET Core를 docker에서 실행 시 "Failed with a critical error." 오류 발생 [1]
11706정성태9/29/201820009개발 환경 구성: 400. Synology NAS(DS216+II)에서 실행한 gcc의 Segmentation fault [2]
11705정성태9/29/201820828개발 환경 구성: 399. Synology NAS(DS216+II)에 gcc 컴파일러 설치
11704정성태9/29/201824876기타: 73. Synology NAS 신호음(beep) 끄기 [1]파일 다운로드1
11703정성태9/27/201819629개발 환경 구성: 398. Blazor 환경 구성 후 빌드 속도가 너무 느리다면? [2]
11702정성태9/26/201816832사물인터넷: 44. 넷두이노(Netduino)의 네트워크 설정 방법
11701정성태9/26/201822583개발 환경 구성: 397. 공유기를 일반 허브로 활용하는 방법파일 다운로드1
11700정성태9/21/201820664Graphics: 25. Unity - shader의 직교 투영(Orthographic projection) 행렬(UNITY_MATRIX_P)을 수작업으로 구성
11699정성태9/21/201819133오류 유형: 488. Add-AzureAccount 실행 시 "No subscriptions are associated with the logged in account in Azure Service Management (RDFE)." 오류
11698정성태9/21/201820321오류 유형: 487. 윈도우 성능 데이터를 원격 SQL에 저장하는 경우 "Call to SQLAllocConnect failed with %1." 오류 발생
11697정성태9/20/201819248Graphics: 24. Unity - unity_CameraWorldClipPlanes 내장 변수 의미
11696정성태9/19/201820176.NET Framework: 793. C# - REST API를 이용해 NuGet 저장소 제어파일 다운로드1
11695정성태9/19/201825298Graphics: 23. Unity - shader의 원근 투영(Perspective projection) 행렬(UNITY_MATRIX_P)을 수작업으로 구성
11694정성태9/17/201819557오류 유형: 486. nuget push 호출 시 405 Method Not Allowed 오류 발생
11693정성태9/16/201822722VS.NET IDE: 128. Unity - shader 코드 디버깅 방법
11692정성태9/13/201823042Graphics: 22. Unity - shader의 Camera matrix(UNITY_MATRIX_V)를 수작업으로 구성
11691정성태9/13/201819997VS.NET IDE: 127. Visual C++ / x64 환경에서 inline-assembly를 매크로 어셈블리로 대체하는 방법 - 두 번째 이야기
11690정성태9/13/201822937사물인터넷: 43. 555 타이머의 단안정 모드파일 다운로드1
11689정성태9/13/201822247VS.NET IDE: 126. 디컴파일된 소스에 탐색을 사용하도록 설정(Enable navigation to decompiled sources)
11688정성태9/11/201817570오류 유형: 485. iisreset - The data is invalid. (2147942413, 8007000d) 오류 발생
11687정성태9/11/201819456사물인터넷: 42. 사물인터넷 - 트랜지스터 다중 전압 테스트파일 다운로드1
11686정성태9/8/201818517사물인터넷: 41. 다중 전원의 소스를 가진 회로파일 다운로드1
11685정성태9/6/201818473사물인터넷: 40. 이어폰 소리를 capacitor로 필터링파일 다운로드1
11684정성태9/6/201821100개발 환경 구성: 396. pagefile.sys를 비활성화시켰는데도 working set 메모리가 줄어드는 이유파일 다운로드1
... 76  77  78  79  80  81  82  83  84  85  86  87  88  [89]  90  ...