C#のボックス化のコストを、文字列補間とstring.Formatで計測してみました。
計測環境は以下の3つです。
- .Net6
- .Net Core 3.1
- .Net Framework 4.8
上記3環境で、以下の4パターンを計測しました。
- 文字列補間 – ボックス化あり
- 文字列補間 – ボックス化なし
- string.Format – ボックス化あり
- string.Format – ボックス化なし
ボックス化とは
値型(intなど)をobject型に変換することを指します。
なお、object型から値型に変換することをアンボックス化と言います。
int n = 1;
object obj = n; //ボックス化
int m = (int)obj; //アンボックス化
詳しくは以下の説明されています。
まとめ
まずは結果の概要から。
- .Netの早い順は「.Net 6 > .Net Core 3.1 > .Net Framework 4.8」
- ボックス化ありの場合、文字列補間もstring.Formatもパフォーマンスは変わらない。
- ボックス化なしの場合、文字列補間の方がstring.Formatよりも高速
これは、コンパイラが以下のようにコードを変換しているから。$"A{i.ToString()}B"
↓"A" + i.ToString() + "B"
- .Net Framework 4.8の場合、ボックス化ありの方が遅いが、.Net Core3.1以降だと逆の結果となる。とはいえ、.Net Framework 4.8もたいして速度は変わらないので、あまり気にする必要はなさそう。
計測用コード
1000万回のループに何秒かかるかを計測します。
int cnt = 10000000;
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < cnt; i++) {
string tmp = $"A{i}B";
}
sw.Stop();
Console.WriteLine($"文字列補間 box/unbox化あり : {sw.ElapsedMilliseconds}");
sw.Restart();
for (int i = 0; i < cnt; i++) {
string tmp = $"A{i.ToString()}B";
}
sw.Stop();
Console.WriteLine($"文字列補間 box/unbox化なし : {sw.ElapsedMilliseconds}");
sw.Restart();
for (int i = 0; i < cnt; i++) {
string tmp = string.Format("A{0}B", i);
}
sw.Stop();
Console.WriteLine($"string.format box/unbox化あり : {sw.ElapsedMilliseconds}");
sw.Restart();
for (int i = 0; i < cnt; i++) {
string tmp = string.Format("A{0}B", i.ToString());
}
sw.Stop();
Console.WriteLine($"string.format box/unbox化なし : {sw.ElapsedMilliseconds}");
計測結果
ボックス化 | 文字列補間 | string.Format | |
.Net6 | あり | 393ms | 392ms |
なし | 289ms | 436ms | |
.Net Core 3.1 | あり | 683ms | 664ms |
なし | 429ms | 738ms | |
.Net Framework 4.8 | あり | 1180ms | 1144ms |
なし | 761ms | 1127ms |
終わりに
.Net は進化のたびに、しっかりとパフォーマンスが向上していてすごいなという印象を受ける。
新しく作るなら、新しいバージョンを積極的に導入していきたい。
文字列補間とstring.Formatは、できるだけ文字列補間を選ぶ方がパフォーマンス的には良さそう。可読性もこちらの方が良いし。
ボックス化の有無は、文字列補間とstring.Formatに関しては、事前にToStringをつける必要はなさそう。
コメント