[C# 문법] C# StringBuilder 성능 개선

참조


목적

  • 회사에서 특정 라이브러리 성능 개선을 하던 중, 특정 코드에 유독 StringBuilder 클래스를 자주 사용하는 것을 보게 되었습니다.
  • StringBuilder 를 보다 빠르게 사용하는? 방법이 없을까 인터넷을 찾던 중, StringBuilderCache 클래스를 사용하면 성능적으로 더 빨라진다고 하여 실제로 그러한지 벤치마크를 통해 알아 보았습니다.

개발 환경

  • IDE : Visual Studio 2022
  • .NET Version : .NET 6

StringBuilderCache.cs

  • 참고로, StringBuilderCache.cs 는 C# 에서 따로 클래스를 추가해서 사용해야 합니다.
  • .NET 자체의 Internal 클래스로써, 일반적으로 using 구문을 통해서는 사용하지 못합니다.
  • 때문에 저는 micfosoft git 사이트에서 StringBuilderCache.cs 클래스를 똑같이 구현하여 테스트 진행 하였습니다.

StringBuilderCache.cs

  • StringBuilderCache 클래스 내용은 다음과 같습니다.
using System.Threading;

namespace System.Text
{
    internal static class StringBuilderCache
    {
        // The value 360 was chosen in discussion with performance experts as a compromise between using
        // as litle memory (per thread) as possible and still covering a large part of short-lived
        // StringBuilder creations on the startup path of VS designers.
        private const int MAX_BUILDER_SIZE = 360;

        [ThreadStatic]
        private static StringBuilder CachedInstance;

        public static StringBuilder Acquire(int capacity = StringBuilder.DefaultCapacity)
        {
            if(capacity <= MAX_BUILDER_SIZE)
            {
                StringBuilder sb = StringBuilderCache.CachedInstance;
                if (sb != null)
                {
                    // Avoid stringbuilder block fragmentation by getting a new StringBuilder
                    // when the requested size is larger than the current capacity
                    if(capacity <= sb.Capacity)
                    {
                        StringBuilderCache.CachedInstance = null;
                        sb.Clear();
                        return sb;
                    }
                }
            }
            return new StringBuilder(capacity);
        }

        public static void Release(StringBuilder sb)
        {
            if (sb.Capacity <= MAX_BUILDER_SIZE)
            {
                StringBuilderCache.CachedInstance = sb;
            }
        }

        public static string GetStringAndRelease(StringBuilder sb)
        {
            string result = sb.ToString();
            Release(sb);
            return result;
        }
    }
}

벤치마크 코드

  • 벤치 마크 코드는 다음과 같습니다.
  • 벤치마크는 참고로 BenchMarkDotNet NuGet Package 를 설치하여 사용하였습니다.
  • StringBuilder, StringBuilderCache 2개의 클래스를 비교하는 메서드를 생성하였고, 반복문은 각각 10번, 100번, 1000번, 2000번, 10000번, 100000번, 1000000번 테스트 진행 하였습니다.

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Text;


var summary = BenchmarkRunner.Run<BenchMarkTest>();

[MemoryDiagnoser]
public class BenchMarkTest
{
    [Params(10, 100, 1000, 2000, 10000, 100000, 1000000)]
    public int Count { get; set; }


    [Benchmark]
    public void AppendStringUsingStringBuilder()
    {
        for(int index = 0; index < Count; index++)
        {
            var stringBuilder = new StringBuilder();
            stringBuilder.Append("First String");
            stringBuilder.Append("Second String");
            stringBuilder.Append("Third String");
            string value =  stringBuilder.ToString();
        }
    }

    [Benchmark]
    public void AppendStringUsingStringBuilderCache()
    {
        StaticClass.AppendStringUsingStringBuilderCache(Count);
    }
}

public static class StaticClass
{
    [Benchmark]
    public static void AppendStringUsingStringBuilderCache(int Count)
    {
        for (int index = 0; index < Count; index++)
        {
            StringBuilder stringBuilder = StringBuilderCache.Acquire();
            stringBuilder.Append("First String");
            stringBuilder.Append("Second String");
            stringBuilder.Append("Third String");
            string value = StringBuilderCache.GetStringAndRelease(stringBuilder);
        }
    }
}

실행 결과

  • 벤치마크 실행 결과, StringBuilder 보다 StringBuilderCahce 클래스를 사용했을 때 속도가 2배이상 줄어든 것을 확인할 수 있습니다.

728x90

이 글을 공유하기

댓글

Designed by JB FACTORY