[Modern C#]

C# StringBuilder

birdman 2024. 8. 26. 20:47

본 내용은 크로스 플랫폼 개발을 위한 C#10과 .NET 6 Sixth Edition(김현욱 옮김 마크프라이스 지음)의 책을 토대로 공부한 내용을 요약 정리하는 것입니다.

 

 

String 연산에 대해 일반적으로 +나 substring, concat등을 사용하는데 StringBuilder는 본 책을 보고 알게되었다. 

 

본 페이지에서는 앞서 언급한 +, concat등의 방법과 StringBuilder를 사용한 방법중 어떤 방법이 더 효율적인지 메모리 측면에서 비교해본 내용이다.

 

본 페이지에서는 경과시간과 리소스 사용량을 쉽게 모니터링 할 수 있는 Recorder 클래스를 구현을 먼저한다.

 

Recorder class를 구현하기 위해 Stopwatch, Process 클래스를 이용한다.

 

본 책에 Stopwatch와 Process 클래스의 다음 멤버를 소개한다.

 

Stopwatch
Restart 시간을 0으로 리셋, 타이머를 시작
Stop 타이머 정지
Elapsed 경과시간을 TimeSpan 포맷(hours:minutes:seconds)로 저장
ElapsedMiliseconds 경과시간을 int64 형식의 ms값으로 저장

 

Process
VirtualMemorySize64 프로세스에 할당된 가상 메모리를 바이트로 표현
WorkingSet64 프로세스에 할당된 물리 메모리를 바이트로 표시

 

 

Recorder클래스는 다음과 같이 구현할 수 있다.

 

- 클래스를 일반 함수와 같이 사용하기 위해 Static으로 선언했다.

- Start함수가 시작되면 물리메모리와 가상메모리를 각각 bytesPhysicalBefore, bytesVirtualBefore 에 저장한다.

- Stop함수가 시작되면 그 시점의 물리, 가상메모리를 bytesPhysicalAfter, bytesVirtual After에 저장한다. 

- 그리고 After - before로 사용된 메모리를 측정하여 플랏한다.

using System.Diagnostics; // Stopwatch

using static System.Console;
using static System.Diagnostics.Process; // GetCurrentProcess()

namespace Packt.Shared;

public static class Recorder
{
  private static Stopwatch timer = new();

  private static long bytesPhysicalBefore = 0;
  private static long bytesVirtualBefore = 0;

  public static void Start()
  {
    // force two garbage collections to release memory that is no
    // longer referenced but has not been released yet
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    // store the current physical and virtual memory use
    bytesPhysicalBefore = GetCurrentProcess().WorkingSet64;
    bytesVirtualBefore = GetCurrentProcess().VirtualMemorySize64;
    timer.Restart();
  }

  public static void Stop()
  {
    timer.Stop();

    long bytesPhysicalAfter = GetCurrentProcess().WorkingSet64;

    long bytesVirtualAfter = GetCurrentProcess().VirtualMemorySize64;

    WriteLine("{0:N0} physical bytes used.",
      bytesPhysicalAfter - bytesPhysicalBefore);

    WriteLine("{0:N0} virtual bytes used.",
      bytesVirtualAfter - bytesVirtualBefore);

    WriteLine("{0} time span elapsed.", timer.Elapsed);

    WriteLine("{0:N0} total milliseconds elapsed.",
      timer.ElapsedMilliseconds);
  }
}

 

 

 

진입점인 Program.cs에 다음의 코드를 작성한다.

+를 사용하여 int를 string으로 연결하는것과 StringBuilder를 이용하여 연결하는 차이다.

int[] numbers = Enumerable.Range(
  start: 1, count: 50_000).ToArray();

WriteLine("Using string with +");
Recorder.Start();
string s = string.Empty; // i.e. "";
for (int i = 0; i < numbers.Length; i++)
{
  s += numbers[i] + ", ";
}
Recorder.Stop();

WriteLine("Using StringBuilder");
Recorder.Start();
System.Text.StringBuilder builder = new();
for (int i = 0; i < numbers.Length; i++)
{
  builder.Append(numbers[i]); 
  builder.Append(", ");
}
Recorder.Stop();

 

 

결과적으로 하기가 표시된다.

사용된 물리 메모리 및 가상메모리, 소요시간을 비교 시 StringBuilder가 압도적으로 좋다. 

왜 몰랐지???