2019-04-18に更新

文字列連結処理を比較してみた②

Unityのバージョン:2019.1.0f2

はじめに

前回の記事で(「文字列連結処理を比較してみた」)定数だと最適化されるよ、連結数が違うと結果が変わるよ、とコメントを頂いたので、条件を変えて再度比較してみました。

比較内容

比べた文字連結方法は以下の通りです。

エントリーNo.1:+

string s = "a" + "b";

エントリーNo.2:StringBuilder

StringBuilder builder = new StringBuilder();
for(int i = 0; i < n; ++i)  {
    builder.Clear();
    builder.Append(”a”).Append("b");
    string s = builder.ToString();
}

エントリーNo.3:Concat

string s = string.Concat("a", "b");

エントリーNo.4:Format

string s = string.Format("{0}{1}", "a", "b");

エントリーNo.5:$""

string a = "a", b = "b";
string s = $"{a}{b}";

エントリーNo.6:StringBuilderTemporary

string str = StrOpe.i +"a" + "b";

※参考
【Unity】string の連結を StringBuilder に置き換えてパフォーマンスを改善できる「StringBuilderTemporary」紹介

エントリーNo.7:FastString

FastString fast = new FastString(64);
for(int i = 0; i < n; ++i) {
    fast.Clear();
    fast.Append(”a”).Append("b");
    string s = fast.ToString();
}

※参考
【Unity】string や StringBuilder よりもメモリ割り当てが少なく高速な文字列クラス「FastString」紹介

エントリーNo.8:UtilsFormat

string str = StringUtils.Format("{0}{1}", "a", "b");

※参考
【Unity】ボックス化をなるべく回避して GC の発生を抑える string.Format「StringUtils」

パターン1:5個の文字列連結をN回繰り返す

検証コード

using System;
using System.Text;
using UnityEngine;
using UnityEngine.Profiling;

using StrOpe = StringOperationUtil.OptimizedStringOperation;

public class Test : MonoBehaviour
{
    const int repeat = 100000;  // N
    string tmp1 = "a";
    string tmp2 = "b";
    string tmp3 = "c";
    string tmp4 = "d";
    string tmp5 = "e";

    StringBuilder _builder;
    FastString _fast = new FastString(64);

    void Start()
    {
        _builder = new StringBuilder();

        PlayTest(Plus, "+");
        PlayTest(StringBuilder, "StringBuilder");
        PlayTest(Concat, "Concat");
        PlayTest(Format, "Format"); 
        PlayTest(Interpolation, "$\"\"");
        PlayTest(StringBuilderTemporary, "StringBuilderTemporary");
        PlayTest(FastString, "FastString");
        PlayTest(UtilsFormat, "UtilsFormat");
    }

    void PlayTest(Func<string> func, string title)
    {
        Profiler.BeginSample(title);

        string s = string.Empty;
        for (int i = 0; i < repeat; ++i)
        {
            s = func();
        }

        Profiler.EndSample();
    }

    string Plus()
    {
        string str = tmp1 + tmp2 + tmp3 + tmp4 + tmp5;
        return str;
    }

    string StringBuilder()
    {
        _builder.Clear();
        _builder.Append(tmp1).Append(tmp2).Append(tmp3).Append(tmp4).Append(tmp5);
        string str = _builder.ToString();

        return str;
    }

    string Concat()
    {
        string str = string.Concat(tmp1, tmp2, tmp3, tmp4, tmp5);
        return str;
    }

    string Format()
    {
        string str = string.Format("{0}{1}{2}{3}{4}", tmp1, tmp2, tmp3, tmp4, tmp5);
        return str;
    }

    string Interpolation()
    {
        string str = $"{tmp1}{tmp2}{tmp3}{tmp4}{tmp5}";
        return str;
    }

    string StringBuilderTemporary()
    {
        string str = StrOpe.i + tmp1 + tmp2 + tmp3 + tmp4 + tmp5;
        return str;
    }

    string FastString()
    {
        _fast.Clear();
        _fast.Append(tmp1).Append(tmp2).Append(tmp3).Append(tmp4).Append(tmp5);
        string str = _fast.ToString();

        return str;
    }

    string UtilsFormat()
    {
        string str = StringUtils.Format("{0}{1}{2}{3}{4}", tmp1, tmp2, tmp3, tmp4, tmp5);
        return str;
    }
}

検証結果

N=10

5_10.PNG

N=50

5_50.PNG

N=100

5_100.PNG

N=1000

5_1000.PNG

パターン2:10個の文字列連結をN回繰り返す

検証結果

N=10

10_10.PNG

N=50

10_50.PNG

N=100

10_100.PNG

N=1000

10_1000.PNG

パターン2:N個の文字列を連結する

検証コード

using System;
using System.Text;
using UnityEngine;
using UnityEngine.Profiling;

using StrOpe = StringOperationUtil.OptimizedStringOperation;

public class Test : MonoBehaviour
{
    const int repeat = 100000;  // N
    string tmp = "a";

    StringBuilder _builder;
    FastString _fast = new FastString(repeat + 1);

    void Start()
    {
        _builder = new StringBuilder();

        PlayTest(Plus, "+");
        PlayTest(StringBuilder, "StringBuilder");
        PlayTest(Concat, "Concat");
        PlayTest(Format, "Format");
        PlayTest(Interpolation, "$\"\"");
        PlayTest(StringBuilderTemporary, "StringBuilderTemporary");
        PlayTest(FastString, "FastString");
        PlayTest(UtilsFormat, "UtilsFormat");
    }

    void PlayTest(Func<string> func, string title)
    {
        Profiler.BeginSample(title);

        string s = func();

        Profiler.EndSample();
    }

    string Plus()
    {
        string str = string.Empty;

        for (int i = 0; i < repeat; ++i)
            str += tmp;

        return str;
    }

    string StringBuilder()
    {
        _builder.Clear();

        for (int i = 0; i < repeat; ++i)
            _builder.Append(tmp);

        string str = _builder.ToString();

        return str;
    }

    string Concat()
    {
        string str = string.Empty;

        for (int i = 0; i < repeat; ++i)
            str = string.Concat(str, tmp);

        return str;
    }

    string Format()
    {
        string str = string.Empty;

        for (int i = 0; i < repeat; ++i)
            str = string.Format("{0}{1}", str, tmp);

        return str;
    }

    string Interpolation()
    {
        string str = string.Empty;

        for (int i = 0; i < repeat; ++i)
            str = $"{str}{tmp}";

        return str;
    }

    string StringBuilderTemporary()
    {
        string str = string.Empty;

        for (int i = 0; i < repeat; ++i)
            str = StrOpe.i + str + tmp;

        return str;
    }

    string FastString()
    {
        for (int i = 0; i < repeat; ++i)
            _fast.Append(tmp);

        string str = _fast.ToString();

        return str;
    }

    string UtilsFormat()
    {
        string str = string.Empty;

        for (int i = 0; i < repeat; ++i)
            str = StringUtils.Format("{0}{1}", str, tmp);

        return str;
    }
}

N=10

10.PNG

N=50

50.PNG

N=100

100.PNG

N=1000

1000.PNG

結論

正確なしきい値は出していませんが、数が少ない連結(50個くらいまで)は「$""」、それ以上はStringBuilderを使えばいいのかな。

ツイッターでシェア
みんなに共有、忘れないようにメモ

長野別荘

福岡の片隅で友人と何か作ってます。 Steamでマッチ3パズルゲーム「TAVERN GUARDIANS: BANQUET」配信中。

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント