文字列とテキスト
Unity ドキュメントベストプラクティスガイド( Best practice guides )の Strings and text の内容を日本語で紹介。
Unity - Manual: Strings and text
https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity5.html
- 文字列連結は StringBuilder を使う
※これは Unity に限らず C# プログラムでよく言われる事ですね。 s += "abc" などとすると内部的には新しい string が生成される処理が走るのでコストが増えるのでダメってやつです。
ロケール強制と順序比較
- String.Equals() はロケールを考慮した処理が行われるので注意
例えば以下の様に pe の部分が微妙に異なる文字列を比較していますがロケールが US-English の場合には true を返しますがヨーロッパのロケールが設定されている場合には false を返すそうです。
※つまり内部で複雑な処理が行われている。
String.Equals("encyclopedia", “encyclopædia”);
注: Unity はバージョン 5.3 と 5.4 の時点で、 Unity のスクリプティングランタイムは常に英語(US-en)ロケールで実行される。
ほとんどの文字列比較では C や C++ と同様の比較で問題無い場合が多い。その様な比較は StringComparison.Ordinal で実現可能。
myString.Equals(otherString, StringComparison.Ordinal);
【文字列検索処理のベンチマーク結果】
Method | Time (ms) for 100k short strings |
---|---|
String.StartsWith, default culture | 137 |
String.EndsWith, default culture | 542 |
String.StartsWith, ordinal | 115 |
String.EndsWith, ordinal | 34 |
Custom StartsWith replacement | 4.5 |
Custom EndsWith replacement | 4.5 |
culture(カルチャー、ロケール)を使った時が一番遅く、使わなかった時がそこそこ、Custom(自前で準備した場合)が一番高速という結果。
EndsWith と StartsWith のカスタムメソッドの作例。
public static bool CustomEndsWith(string a, string b) { int ap = a.Length - 1; int bp = b.Length - 1; while (ap >= 0 && bp >= 0 && a [ap] == b [bp]) { ap--; bp--; } return (bp < 0 && a.Length >= b.Length) || (ap < 0 && b.Length >= a.Length); } public static bool CustomStartsWith(string a, string b) { int aLen = a.Length; int bLen = b.Length; int ap = 0; int bp = 0; while (ap < aLen && bp < bLen && a [ap] == b [bp]) { ap++; bp++; } return (bp == bLen && aLen >= bLen) || (ap == aLen && bLen >= aLen); }
正規表現
- シンプルな IsMatch ですら内部処理で大きなデータ割当てが走る為、初期化中を除いて許容できないと考えるべき。
- Regex.Match や Regex.Replace などの静的メソッドはオンザフライで正規表現をコンパイルし、生成されたオブジェクトもキャッシュしないので強く非推奨。
例)
Regex.Match(myString, "foo");
これは実行するたびに5KBのゴミ(ガベージコレクションの対象になるもの)が発生。
var myRegExp = new Regex("foo");
myRegExp.Match(myString);
この場合は320バイトのゴミが発生。
XML、JSON、その他の長文テキストの解析
- 多くのサードパーティーのパーサーは、リフレクションに基づいて構築されている。リフレクションは開発中に優れた選択肢ですが(パーサがデータレイアウトの変更に迅速に対応できるため)、遅い。
- Unity はその解決策として JSONUtility API を準備。
代替案は以下の3つ
1:ビルド時に解析する
データを ScriptableObject に移動し、 AssetBundle を介して配布する。
この辺りの話については以下の動画(英語)を参照
この戦略は可能な限り最良のパフォーマンスを提供するが、動的に生成する必要のないデータにのみ適している。ゲームデザインのパラメータやその他のコンテンツに最適。
2:スプリットおよび遅延ロード
解析すべきデータを小さな単位に分割し、必要になった時点でロードさせる。
3:スレッド
完全に C# のオブジェクトしか使用せず、 Unity API を使用しない場合には、解析操作をワーカースレッドに移す事ができる。
スレッドを使用する場合に使用するクラスは Thread や ThreadPool 。
Thread クラス (System.Threading)
https://msdn.microsoft.com/ja-jp/library/system.threading.thread%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
ThreadPool クラス (System.Threading)
https://msdn.microsoft.com/ja-jp/library/system.threading.threadpool(v=vs.110).aspx
方法 : スレッドを作成および終了する (C# プログラミング ガイド)
https://msdn.microsoft.com/ja-jp/library/7a2f3ay4(v=vs.90).aspx