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

... 31  32  33  34  35  36  37  38  39  40  [41]  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12911정성태1/11/202212990VS.NET IDE: 171. 비주얼 스튜디오 - 더 이상 만들 수 없는 "ASP.NET Core 3.1 Web Application (.NET Framework)" 프로젝트
12910정성태1/10/202213704제니퍼 .NET: 30. 제니퍼 닷넷 적용 사례 (8) - CPU high와 DB 쿼리 성능에 문제가 함께 있는 사이트
12909정성태1/10/202215045오류 유형: 782. Visual Studio 2022 설치 시 "Couldn't install Microsoft.VisualCpp.Redist.14.Latest"
12908정성태1/10/202212363.NET Framework: 1132. C# - ref/out 매개변수의 IL 코드 처리
12907정성태1/9/202213934오류 유형: 781. (youtube-dl.exe) 실행 시 "This app can't run on your PC" / "Access is denied." 오류 발생
12906정성태1/9/202215230.NET Framework: 1131. C# - 네임스페이스까지 동일한 타입을 2개의 DLL에서 제공하는 경우 충돌을 우회하는 방법 [1]파일 다운로드1
12905정성태1/8/202214397오류 유형: 780. Could not load file or assembly 'Microsoft.VisualStudio.TextTemplating.VSHost.15.0, Version=16.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.
12904정성태1/8/202216389개발 환경 구성: 623. Visual Studio 2022 빌드 환경을 위한 github Actions 설정 [1]
12903정성태1/7/202215365.NET Framework: 1130. C# - ELEMENT_TYPE_INTERNAL 유형의 사용 예
12902정성태1/7/202215117오류 유형: 779. SQL 서버 로그인 에러 - provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.
12901정성태1/5/202215482오류 유형: 778. C# - .NET 5+에서 warning CA1416: This call site is reachable on all platforms. '...' is only supported on: 'windows' 경고 발생
12900정성태1/5/202217376개발 환경 구성: 622. vcpkg로 ffmpeg를 빌드하는 경우 생성될 구성 요소 제어하는 방법
12899정성태1/3/202216845개발 환경 구성: 621. windbg에서 python 스크립트 실행하는 방법 - pykd (2)
12898정성태1/2/202217585.NET Framework: 1129. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 비디오 인코딩 예제(encode_video.c) [1]파일 다운로드1
12897정성태1/2/202215521.NET Framework: 1128. C# - 화면 캡처한 이미지를 ffmpeg(FFmpeg.AutoGen)로 동영상 처리 [4]파일 다운로드1
12896정성태1/1/202220738.NET Framework: 1127. C# - FFmpeg.AutoGen 라이브러리를 이용한 기본 프로젝트 구성파일 다운로드1
12895정성태12/31/202117603.NET Framework: 1126. C# - snagit처럼 화면 캡처를 연속으로 수행해 동영상 제작 [1]파일 다운로드1
12894정성태12/30/202115616.NET Framework: 1125. C# - DefaultObjectPool<T>의 IDisposable 개체에 대한 풀링 문제 [3]파일 다운로드1
12893정성태12/27/202117361.NET Framework: 1124. C# - .NET Platform Extension의 ObjectPool<T> 사용법 소개파일 다운로드1
12892정성태12/26/202114231기타: 83. unsigned 형의 이전 값이 최댓값을 넘어 0을 지난 경우, 값의 차이를 계산하는 방법
12891정성태12/23/202114873스크립트: 38. 파이썬 - uwsgi의 --master 옵션
12890정성태12/23/202115147VC++: 152. Golang - (문자가 아닌) 바이트 위치를 반환하는 strings.IndexRune 함수
12889정성태12/22/202117981.NET Framework: 1123. C# - (SharpDX + DXGI) 화면 캡처한 이미지를 빠르게 JPG로 변환하는 방법파일 다운로드1
12888정성태12/21/202115114.NET Framework: 1122. C# - ImageCodecInfo 사용 시 System.Drawing.Image와 System.Drawing.Bitmap에 따른 Save 성능 차이파일 다운로드1
12887정성태12/21/202118631오류 유형: 777. OpenCVSharp4를 사용한 프로그램 실행 시 "The type initializer for 'OpenCvSharp.Internal.NativeMethods' threw an exception." 예외 발생
12886정성태12/20/202114668스크립트: 37. 파이썬 - uwsgi의 --enable-threads 옵션 [2]
... 31  32  33  34  35  36  37  38  39  40  [41]  42  43  44  45  ...