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

C# - 문자열 연결 시 string.Create를 이용한 GC 할당 최소화

이번 글은 아래의 트윗 내용을 옮겨봅니다. ^^



예전에도 "C# 10 - (12) 문자열 보간 성능 개선" 글에서 string.Create를 스치듯 다룬 적이 있었습니다. ^^

어쨌든 중요한 것은, string 자체는 참조 타입이라서 GC Heap을 쓸 수밖에 없다는 점입니다. 하지만, string을 연결하는 과정에서 가능한 stack을 활용해 GC 힙의 사용을 최소화하는 노력은 할 수 있습니다.

위의 트윗에서 나온 코드를 실습해 보면,

namespace ConsoleApp1;

internal class Program
{
    static void Main(string[] args)
    {
        {
            string text = StringCreate(); // JIT
            Console.WriteLine(text.Length * 2);
        }

        {
            long old = GC.GetAllocatedBytesForCurrentThread();
            string text = StringCreate();
            Console.WriteLine(text);
            long now = GC.GetAllocatedBytesForCurrentThread();
            Console.WriteLine(now - old);
        }
    }

    static private string title = "Mr.";
    static private string first = "David";
    static private string middle = "Patrick";
    static private string last = "Callan";

    static public string StringCreate()
    {
        string text = string.Create(title.Length + first.Length + middle.Length + last.Length + 3,
            (title, first, middle, last),
            (span, state) =>
            {
                state.title.AsSpan().CopyTo(span);
                span = span[state.title.Length..];
                span[0] = ' ';
                span = span[1..];

                state.first.AsSpan().CopyTo(span);
                span = span[state.first.Length..];
                span[0] = ' ';
                span = span[1..];

                state.middle.AsSpan().CopyTo(span);
                span = span[state.middle.Length..];
                span[0] = ' ';
                span = span[1..];

                state.last.AsSpan().CopyTo(span);
            }
            );

        return text;
    }
}

화면에는 이런 출력을 얻게 됩니다.

48
Mr. David Patrick Callan
72

StringCreate를 실행했을 때 GC Heap을 72바이트 소비하는 것으로, 문자열 길이가 48바이트이므로 null 2바이트를 포함하면 50바이트, 그래도 22바이트가 더 소비되긴 했습니다. 어떻게 소비된 것인지 다음의 글에 따라 계산해 보면,

windbg - .NET string의 x86/x64 메모리 할당 구조
; https://www.sysnet.pe.kr/2/0/11336

  • Object Header: 8바이트
  • MethodTable 주소: 8바이트
  • m_stringLength: 4바이트
  • ...[문자열 48바이트]...
  • null 2바이트
  • 8바이트 정렬로 인해 2바이트

모두 더해 정확히 72바이트입니다. ^^ 그러니까 결국 Span을 이용한 string.Create의 사용은 대상 문자열로 인한 GC 힙의 사용 외에는 나머지 할당을 완전히 없앤 것입니다.




^^ 눈치채신 분이 있겠지만, 사실 위와 같이 코딩하는 것은 아래와 같이 바꿔쓸 수 있습니다.

// C# 10+, .NET 6+

{
    string text = $"{title} {first} {middle} {last}";
}

{
    long old = GC.GetAllocatedBytesForCurrentThread();
    string text = $"{title} {first} {middle} {last}";
    long now = GC.GetAllocatedBytesForCurrentThread();
    Console.WriteLine(now - old); // 출력 결과: 72
}

위의 코드 역시 72바이트만을 소비하는데, "C# 10 - (12) 문자열 보간 성능 개선"에서 설명한 대로 이미 DefaultInterpolatedStringHandler가 내부적으로 string.Create를 이용한 문자열 연결을 하고 있기 때문입니다.




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







[최초 등록일: ]
[최종 수정일: 7/23/2023]

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

비밀번호

댓글 작성자
 




... [31]  32  33  34  35  36  37  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12865정성태12/6/20216902개발 환경 구성: 611. 파이썬 - PyPI 패키지 만들기 (2) long_description, cmdclass 옵션
12864정성태12/6/20215333Linux: 46. WSL 환경에서 find 명령을 사용해 파일을 찾는 방법
12863정성태12/4/20217283개발 환경 구성: 610. 파이썬 - PyPI 패키지 만들기
12862정성태12/3/20215985오류 유형: 768. Golang - 빌드 시 "cmd/go: unsupported GOOS/GOARCH pair linux /amd64" 오류
12861정성태12/3/20218218개발 환경 구성: 609. 파이썬 - "Windows embeddable package"로 개발 환경 구성하는 방법
12860정성태12/1/20216313오류 유형: 767. SQL Server - 127.0.0.1로 접속하는 경우 "Access is denied"가 발생한다면?
12859정성태12/1/202112491개발 환경 구성: 608. Hyper-V 가상 머신에 Console 모드로 로그인하는 방법
12858정성태11/30/20219751개발 환경 구성: 607. 로컬의 USB 장치를 원격 머신에 제공하는 방법 - usbip-win
12857정성태11/24/20217194개발 환경 구성: 606. WSL Ubuntu 20.04에서 파이썬을 위한 uwsgi 설치 방법
12856정성태11/23/20219011.NET Framework: 1121. C# - 동일한 IP:Port로 바인딩 가능한 서버 소켓 [2]
12855정성태11/13/20216291개발 환경 구성: 605. Azure App Service - Kudu SSH 환경에서 FTP를 이용한 파일 전송
12854정성태11/13/20217933개발 환경 구성: 604. Azure - 윈도우 VM에서 FTP 여는 방법
12853정성태11/10/20216259오류 유형: 766. Azure App Service - JBoss 호스팅 생성 시 "This region has quota of 0 PremiumV3 instances for your subscription. Try selecting different region or SKU."
12851정성태11/1/20217677스크립트: 34. 파이썬 - MySQLdb 기본 예제 코드
12850정성태10/27/20218871오류 유형: 765. 우분투에서 pip install mysqlclient 실행 시 "OSError: mysql_config not found" 오류
12849정성태10/17/20217974스크립트: 33. JavaScript와 C#의 시간 변환 [1]
12848정성태10/17/20218923스크립트: 32. 파이썬 - sqlite3 기본 예제 코드 [1]
12847정성태10/14/20218774스크립트: 31. 파이썬 gunicorn - WORKER TIMEOUT 오류 발생
12846정성태10/7/20218539스크립트: 30. 파이썬 __debug__ 플래그 변수에 따른 코드 실행 제어
12845정성태10/6/20218381.NET Framework: 1120. C# - BufferBlock<T> 사용 예제 [5]파일 다운로드1
12844정성태10/3/20216346오류 유형: 764. MSI 설치 시 "... is accessible and not read-only." 오류 메시지
12843정성태10/3/20216802스크립트: 29. 파이썬 - fork 시 기존 클라이언트 소켓 및 스레드의 동작파일 다운로드1
12842정성태10/1/202125283오류 유형: 763. 파이썬 오류 - AttributeError: type object '...' has no attribute '...'
12841정성태10/1/20218656스크립트: 28. 모든 파이썬 프로세스에 올라오는 특별한 파일 - sitecustomize.py
12840정성태9/30/20218713.NET Framework: 1119. Entity Framework의 Join 사용 시 다중 칼럼에 대한 OR 조건 쿼리파일 다운로드1
12839정성태9/15/20219826.NET Framework: 1118. C# 11 - 제네릭 타입의 특성 적용파일 다운로드1
... [31]  32  33  34  35  36  37  38  39  40  41  42  43  44  45  ...