どうもKutoです。
ということで、「ZStringを読もう」の第一回です。
今回は実際のコードを読解していく前に、まずはZStringがどういったものなのか。その概要を簡単に整理しておきたいと思います。
ZStringとは?
ZStringは、C#におけるゼロアロケーション文字列操作を実現するライブラリです。Cysharpの@neuecc氏によって開発されました。
主な特徴
- ゼロアロケーション: 文字列操作時のヒープアロケーションを極力削減
-
高パフォーマンス:
StringBuilderや文字列補間と比較して、大幅な性能向上を実現 -
スタックベース:
Utf16ValueStringBuilderやUtf8ValueStringBuilderなどのスタックベースの文字列ビルダーを提供 -
使いやすいAPI:
ZString.FormatやZString.Concatなど、直感的なAPIを提供 - .NET Standard 2.0/2.1対応: 幅広い.NETプラットフォームで利用可能
なぜZStringが必要なのか?
C#では通常の文字列操作(string.Format、文字列補間$"..."、+演算子による連結など)が非常に便利ですが、内部的には多くのヒープアロケーションが発生します。これはGC(ガベージコレクション)の負荷を増やし、特にゲームやリアルタイムシステムなど、パフォーマンスが重要なアプリケーションでは大きな問題となります。
C#はUnity等でよく扱われるので、アロケーションによるこのパフォーマンスの問題は重要です。
ZStringはSpan<T>やstackallocなどの最新の.NET機能を活用し、アロケーションを最小限に抑えながら高速な文字列操作を実現します。
ZStringの開発元であるCySharpは他にもゼロアロケーション系ライブラリを作成しており、そのシリーズの一つがこのZStringです。
ゼロアロケーションとは?
ゼロアロケーションとは、プログラムの実行中にヒープメモリの動的な確保(アロケーション)を行わない、または最小限に抑えることを指します。
C#では、new演算子でオブジェクトを生成したり文字列を連結したりすると、通常はヒープ領域にメモリが確保されます。このヒープアロケーションには以下のようなコストがかかります:
- メモリ確保のコスト: メモリを割り当てる処理自体に時間がかかる
-
GC(ガベージコレクション)のコスト: 使用済みメモリを回収する際に処理が一時停止する
a. この一時的な処理不可をスパイクと呼ぶ - メモリ断片化: 頻繁なアロケーションによりメモリが断片化し、効率が低下する
ゼロアロケーションを実現するには、スタックメモリを活用します。スタックはヒープよりも高速で、スコープを抜けると自動的にメモリが解放されるため、GCの負荷もかかりません。ZStringはSpan<T>やstackallocを使ってスタック上で文字列操作を行うことで、これを実現しています。
基本的な使い方
1. ZString.Format - 基本的なフォーマット
using Cysharp.Text;
// 通常のstring.Formatと同じように使える
var result = ZString.Format("Hello, {0}! You are {1} years old.", "Alice", 25);
Console.WriteLine(result); // "Hello, Alice! You are 25 years old."
2. ZString.Concat - 文字列連結
// 複数の値を効率的に連結
var result = ZString.Concat("Score: ", 1000, ", Level: ", 5);
Console.WriteLine(result); // "Score: 1000, Level: 5"
3. Utf16ValueStringBuilder - スタックベースのビルダー
// using構文で使用することで、自動的にリソースが解放される
using (var sb = ZString.CreateStringBuilder())
{
sb.Append("Player: ");
sb.Append("Bob");
sb.Append(", HP: ");
sb.Append(100);
sb.Append("/");
sb.Append(100);
var result = sb.ToString();
Console.WriteLine(result); // "Player: Bob, HP: 100/100"
}
4. ZString.Format with IBufferWriter - ゼロアロケーション
// IBufferWriter<byte>に直接書き込むことで、完全なゼロアロケーションを実現
var buffer = new ArrayBufferWriter<byte>();
ZString.Utf8Format(buffer, "Time: {0:F2}s, FPS: {1}", 1.234f, 60);
// bufferの内容を使用
var utf8String = buffer.WrittenSpan;
まとめ
このようにZStringはstring型のパフォーマンスの向上に非常に役立つライブラリです。
string型は多くの場所で使われます。よって一つ一つのパフォーマンス向上はわずかでも、総じて大きなパフォーマンス向上に貢献できる可能性があるのです。
次回からはこのZStringが実際にどのように実装されているのか、具体的にGitHubのソースコードを見ながら読解していきたいと思います。