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)
13736정성태9/20/2024500오류 유형: 923. Visual Studio Code - Could not establish connection to "...": Port forwarding is disabled.
13735정성태9/20/2024483개발 환경 구성: 726. ARM 플랫폼용 Visual C++ 리눅스 프로젝트 빌드
13734정성태9/19/2024511개발 환경 구성: 725. ssh를 이용한 원격 docker 서비스 사용
13733정성태9/19/2024471VS.NET IDE: 194. Visual Studio - Cross Platform / "Authentication Type: Private Key"로 접속하는 방법
13732정성태9/17/2024616개발 환경 구성: 724. ARM + docker 환경에서 .NET 8 설치
13731정성태9/15/20241018개발 환경 구성: 723. C# / Visual C++ - Control Flow Guard (CFG) 활성화 [1]파일 다운로드2
13730정성태9/10/20241098오류 유형: 922. docker - RULE_APPEND failed (No such file or directory): rule in chain DOCKER
13729정성태9/9/20241364C/C++: 173. Windows / C++ - AllocConsole로 할당한 콘솔과 CRT 함수 연동파일 다운로드1
13728정성태9/7/20241409C/C++: 172. Windows - C 런타임에서 STARTUPINFO의 cbReserved2, lpReserved2 멤버를 사용하는 이유파일 다운로드1
13727정성태9/6/20241681개발 환경 구성: 722. ARM 플랫폼 빌드를 위한 미니 PC(?) - Khadas VIM4 [1]
13726정성태9/5/20241424C/C++: 171. C/C++ - 윈도우 운영체제에서의 file descriptor와 HANDLE파일 다운로드1
13725정성태9/4/20241165디버깅 기술: 201. WinDbg - sos threads 명령어 실행 시 "Failed to request ThreadStore"
13724정성태9/3/20241439닷넷: 2296. Win32/C# - 자식 프로세스로 HANDLE 상속파일 다운로드1
13723정성태9/2/20242863C/C++: 170. Windows - STARTUPINFO의 cbReserved2, lpReserved2 멤버 사용자 정의파일 다운로드2
13722정성태9/2/20241230C/C++: 169. C/C++ - CRT(C Runtime) 함수에 의존성이 없는 프로젝트 생성
13721정성태8/30/20241374C/C++: 168. Visual C++ CRT(C Runtime DLL: msvcr...dll)에 대한 의존성 제거 - 두 번째 이야기
13720정성태8/29/20241238VS.NET IDE: 193. C# - Visual Studio의 자식 프로세스 디버깅
13719정성태8/28/20241283Linux: 79. C++ - pthread_mutexattr_destroy가 없다면 메모리 누수가 발생할까요?
13718정성태8/27/20241933오류 유형: 921. Visual C++ - error C1083: Cannot open include file: 'float.h': No such file or directory [2]
13717정성태8/26/20241457VS.NET IDE: 192. Visual Studio 2022 - Windows XP / 2003용 C/C++ 프로젝트 빌드
13716정성태8/21/20241483C/C++: 167. Visual C++ - 윈도우 환경에서 _execv 동작
13715정성태8/19/20241566Linux: 78. 리눅스 C/C++ - 특정 버전의 glibc 빌드 (docker-glibc-builder)
13714정성태8/19/20241681닷넷: 2295. C# 12 - 기본 생성자(Primary constructors) (책 오타 수정) [3]
13713정성태8/16/20242154개발 환경 구성: 721. WSL 2에서의 Hyper-V Socket 연동
13712정성태8/14/20242117개발 환경 구성: 720. Synology NAS - docker 원격 제어를 위한 TCP 바인딩 추가
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...