본문 바로가기
R&D

C#, Performance Of String

by Client. DJ 2022. 1. 17.
반응형

String class 사용하는 방식은 여러가지가 있습니다.

1. String Operator (+, +=)
2. String.Format
3. Interpolation
4. String.Join
5. String.Concat
6. StringBuilder

 

아래의 예제는 모두 같은 결과를 출력합니다.

using System.Text;

public class SingleLineJoin
{
    public string string1 = "a";
    public string string2 = "b";
    public string string3 = "c";
    public string string4 = "d";
    public string string5 = "e";

    public string Interpolation()
    {
        return $"{string1} {string2} {string3} {string4} {string5}";
    }

    public string PlusOperator()
    {
        return string1 + " " + string2 + " " + string3 + " " + string4 + " " + string5;
    }

    public string StringConcatenate()
    {
        return string.Concat(string1, " ", string2, " ", string3, " ", string4, " ", string5);
    }

    public string StringJoin()
    {
        return string.Join(" ", string1, string2, string3, string4, string5);
    }

    public string StringFormat()
    {
        return string.Format("{0} {1} {2} {3} {4}", string1, string2, string3, string4, string5);
    }

    public string StringBuilderAppend()
    {
        StringBuilder builder = new StringBuilder();
        builder.Append(string1);
        builder.Append(" ");
        builder.Append(string2);
        builder.Append(" ");
        builder.Append(string3);
        builder.Append(" ");
        builder.Append(string4);
        builder.Append(" ");
        builder.Append(string5);
        return builder.ToString();
    }
}
// result
a b c d e

생소한 것도 있지만, 우리가 주로 사용하는 것은 오퍼레이터 방식(+)과 문자열 보간($) 방식, 포맷, StringBuilder를 통한 방식이 있습니다.

 

각 함수의 속도를 확인해보자면 아래와 같습니다.

C#, 커스텀 밴치마크 라이브러리 (함수 테스트 도구)

보간방법(Interpolation; $)과 오퍼레이터 및 이어붙이기(+)방식은 사용하기는 매우 편하지만, 퍼포먼스가 떨어진다는 것을 알 수 있습니다. 꼭 필요하다면 단발성으로 쓰는 경우에만 사용하는 것이 좋겠습니다.

 

퍼포먼스가 떨어지는 경우는 String.Format 방식이 되겠습니다. 나머지는 거의 비슷한 수준입니다. 이처럼 퍼모먼스가 떨어지는데에는 이유가 있습니다.

String.Format의 경우 각 나라별 Localize(통화)와 포맷에 맞게 대입하는 과정이 비용이 많이 들어간다.

위와 같은 이유로 퍼모먼스가 약간 떨어지는 경향이 있습니다. 말 그대로 포맷을 찾아서 대입을 해줘야하기 때문에 속도가 느립니다.


String.Join과 StringBuilder가 있는데 이 둘은 속도면에서는 비슷합니다. 하지만 다섯개 한정이 아닌 500개씩 들어가야한다면 결과는 달라집니다.

테스트 코드

더보기

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApp
{
    class Program
    {
        static List<string> list = new List<string>();
        private static void Main(string[] args)
        {
            for (int i = 0; i < 500; i++)
            {
                list.Add(i.ToString());
            }
            Benchmark.Start(StringJoin);
            Benchmark.Start(StringBuilder);
            Console.WriteLine(Benchmark.GetRecord());
        }

        private static string StringJoin()
        {
            return string.Join(" ", list);
        }

        private static string StringBuilder()
        {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < list.Count; i++)
            {
                builder.Append(list[i]);
                builder.Append(" ");
            }
            return builder.ToString();
        }
    }
}

StringBuiler가 좀 더 빠르다는 것을 알 수 있습니다.


메모리

메모리 부분에서도 StringBuilder는 기존 오퍼레이터 방식과는 다르게 메모리 절약에 유리합니다. 내부에 버퍼를 갖고 있고, 문자열에 변경이 있을때마다 새로운 문자열을 생성하지 않고 버퍼의 내용을 변경하기 때문입니다. 하지만 오퍼레이터 방식은 눈으로 보기에는 그냥 더해지나? 이럴 수 있지만, 새로운 문자열을 생성한 후 반환하는 것입니다. 이는 메모리측면에서도 좋지 않은 결과를 초래합니다.

 

꼭 StringBuilder만 사용되어야 하나?

하지만 프로그래밍 내에서도 꼭 StringBuilder가 좋다라고 할 수 없다고 합니다. 오퍼레이터 방식 중 '=+'가 아닌 '+'으로는 짧은 길이의 4개이하의 문자열을 붙일 때는 퍼포먼스가 더욱 좋다고합니다. 컴파일러 단에서 내부적으로 4개까지는 Stirng.Concat으로 변환 처리가 이루진다고 합니다. (이후는 가변형 변수로 추가로 받으면서 반복 처리됨)


요약

1. 대부분의 상황에서는 StringBuilder가 좋다.

2. 짧은 길이 또는 단발성으로 쓰는 경우에는 오퍼레이터 방식과 보간 방식을 사용하여도 좋다.

3. String.Format 방식은 포맷에 맞게 대입하기 때문에 느리다. 또한, 각 나라별 로컬라이징에서도 걸리기 때문에 느리다.

 

참고: https://dotnetcoretutorials.com/2020/02/06/performance-of-string-concatenation-in-c/

참고: https://github.com/microsoft/referencesource/blob/master/mscorlib/system/text/stringbuilder.cs

참고: https://overworks.github.io/unity/2018/08/30/finding-best-string-concatenation.html

반응형

댓글