Cysharp/ZString を試してみた

github.com

Cygames の子会社 Cy# さんが出してる C#OSSライブラリ。メモリ消費量が少なく、早い(?)

tech.cygames.co.jp

細かい内容は上の Cy# さんのブログに書かれています。

試したコード

using System;
using System.IO;
using Cysharp.Text;

namespace ZStringTestDotnetCore
{
    class Program
    {
        static void Main(string[] args)
        {
            var ts1 = MeasureTask(ZStringConcat);
            var ts2 = MeasureTask(MemoryStreamStringFormat);
            var ts3 = MeasureTask(ZStringFormatUtf8);
            var ts4 = MeasureTask(ZStringFormatUtf16);
            var ts5 = MeasureTask(MemoryStreamZStringFormat);

            Console.Write($"- ZString(Concat)   {ts1}\n");
            Console.Write($"- Memory Stream   1 {ts2}\n");
            Console.Write($"- ZString(Format) A {ts3}\n");
            Console.Write($"- ZString(Format) B {ts4}\n");
            Console.Write($"- Memory Stream   2 {ts5}\n");
        }

        static TimeSpan MeasureTask(Action func)
        {
            var sw = new System.Diagnostics.Stopwatch();

            sw.Reset();
            sw.Start();
            func();
            sw.Stop();
            return sw.Elapsed;
        }

        static void ZStringConcat()
        {
            int i = 0,j = 0,k = 0;
            using (var sb = ZString.CreateUtf8StringBuilder())
            {
                while (i < 300000)
                {
                    var now = DateTime.Now;
                    sb.Append(ZString.Concat("i:", i, ",j:", j , ",k:", k, "\n"));
                    i++;
                    j += 2;
                    k += 3;
                }
                sb.WriteToAsync(Console.OpenStandardOutput()).Wait();
            }
        }

        static void MemoryStreamStringFormat()
        {
            int i = 0,j = 0,k = 0;
            using(var ms = new MemoryStream())
            {
                using (var wr = new StreamWriter(ms))
                {
                    while (i < 300000)
                    {
                        var now = DateTime.Now;
                        wr.Write(string.Format("i:{0},j:{1},k:{2}\n", i, j, k));
                        i++;
                        j += 2;
                        k += 3;
                    }
                    wr.Flush();
                    ms.WriteTo(Console.OpenStandardOutput());
                }
            }
        }

        static void ZStringFormatUtf8()
        {
            int i = 0, j = 0, k = 0;
            using (var sb = ZString.CreateUtf8StringBuilder())
            {
                while (i < 300000)
                {
                    var now = DateTime.Now;
                    sb.Append(ZString.Format("i:{0},j:{1},k:{2}\n", i, j, k));
                    i++;
                    j += 2;
                    k += 3;
                }
                sb.WriteToAsync(Console.OpenStandardOutput()).Wait();
            }
        }


        static void ZStringFormatUtf16()
        {
            int i = 0, j = 0, k = 0;
            using (var sb = ZString.CreateStringBuilder())
            {
                while (i < 300000)
                {
                    var now = DateTime.Now;
                    sb.Append(ZString.Format("i:{0},j:{1},k:{2}\n", i, j, k));
                    i++;
                    j += 2;
                    k += 3;
                }
                Console.WriteLine(sb.ToString());
            }
        }

        static void MemoryStreamZStringFormat()
        {
            int i = 0, j = 0, k = 0;
            using (var ms = new MemoryStream())
            {
                using (var wr = new StreamWriter(ms))
                {
                    while (i < 300000)
                    {
                        var now = DateTime.Now;
                        wr.Write(ZString.Format("i:{0},j:{1},k:{2}\n", i, j, k));
                        i++;
                        j += 2;
                        k += 3;
                    }
                    wr.Flush();
                    ms.WriteTo(Console.OpenStandardOutput());
                }
            }
        }
    }
}

結果

- ZString(Concat)   00:00:02.9890532
- Memory Stream   1 00:00:02.6363530
- ZString(Format) A 00:00:02.6669643
- ZString(Format) B 00:00:03.3741640
- Memory Stream   2 00:00:02.6118988

私の試したコードではあまり早くなりませんでした。MemoryStream + StreamWriter のほうがちょっと早いかなという感じ。ここでは記載していませんが、メモリ消費量もあまり違いがない感じでした。もしかすると、ZString は Unity との親和性に重きを置いているので、もしかするとこういうコードではあまり恩恵は受けれないのかもです。

ただ、StringBuilder と比較すると WriteToAsync() メソッドがあって便利ですし、 UTF8用のBuilderもあるので、そちらも便利そうです。