はじめに
Unityを触っていて、なんだかArray.Reverse
が遅く、Enumerable.Reverse
にしてみたら速くなったという不思議な現象に出会いました。
https://docs.microsoft.com/ja-jp/dotnet/api/system.linq.enumerable.reverse?view=net-6.0
https://docs.microsoft.com/ja-jp/dotnet/api/system.array.reverse?view=net-6.0
環境
- Unity2020.3.32f1
- macOS Monterey
私の認識だと一般的にArray.Reverse
の方がEnumerable.Reverse
よりも処理速度が速いと思っていたのですが、案外そうではないんですかね。
というわけで調べてみました。
Unity2020での検証
環境
- Unity2020.3.32f1
- .NET Standard2.0
- Mono
- macOS Monterey
private void Start() { var array = Enumerable.Range(0, 100000000) .ToArray(); var sw = new Stopwatch(); // Array.Reverse sw.Start(); Array.Reverse(array); sw.Stop(); Debug.Log($"Array.Reverse : {sw.ElapsedMilliseconds}ms"); // 同じ実験対象にするために順番を戻しておく array = array.Reverse().ToArray(); // Enumerable.Reverse sw.Reset(); sw.Start(); array = array.Reverse().ToArray(); sw.Stop(); Debug.Log($"Enumerable.Reverse : {sw.ElapsedMilliseconds}ms"); }
メソッド | 処理速度 |
---|---|
Array.Reverse | 100952ms |
Enumerable.Reverse | 515ms |
Array.Reverse
がやたら遅いですね。ちなみに実行する順番を変えたりしても同じ結果になりました。
.Net Framework v4.8のConsoleアプリケーション
今度はUnity
を利用せずに、Riderを利用してConsole
アプリを作成して計測してみます。
環境
- macOS Monterey
- .Net Framework v4.8
- Rider2020.3.4
public static void Main(string[] args) { var array = Enumerable.Range(0, 100000000) .ToArray(); var sw = new Stopwatch(); // Array.Reverse sw.Start(); Array.Reverse(array); sw.Stop(); Console.WriteLine($"Array.Reverse : {sw.ElapsedMilliseconds}ms"); // 同じ実験対象にするために順番を戻しておく array = array.Reverse().ToArray(); // Enumerable.Reverse sw.Reset(); sw.Start(); array = array.Reverse().ToArray(); sw.Stop(); Console.WriteLine($"Enumerable.Reverse : {sw.ElapsedMilliseconds}ms"); }
メソッド | 処理速度 |
---|---|
Array.Reverse | 63058ms |
Enumerable.Reverse | 534ms |
Unityを利用しなくてもArray.Reverse
が極端に遅いですね。うーん。
.Net Core 3.1のコンソールアプリケーション
なんかこのまま終わるのが癪なので、.Net Core
を利用し、Visual Studio
でやってみます。
環境
- macOS Monterey
- .Net Core 3.1
- VisualStudio for Mac v17.0.5
メソッド | 処理速度 |
---|---|
Array.Reverse | 53ms |
Enumerable.Reverse | 288ms |
まさかの結果になってしまいました。圧倒的にArray.Reverse
の方が早いですね。
.Net 5.0のコンソールアプリケーション
次は.NET 5.0
。
環境
- macOS Monterey
- .Net 5.0
- VisualStudio for Mac v17.0.5
メソッド | 処理速度 |
---|---|
Array.Reverse | 59ms |
Enumerable.Reverse | 351ms |
これもArray.Reverse
の勝利です。
Unity2021での検証
環境
- Unity2021.3.0f1
- .NET Standard2.1
- macOS Monterey
メソッド | 処理速度 |
---|---|
Array.Reverse | 63ms |
Enumerable.Reverse | 443ms |
普通にArray.Reverse
が勝ちました。2021
で.Net Standard2.1
に対応したのでこれが原因なのでしょうか。
Unity2019
環境
- Unity2019.4.0f1
- .NET Standard2.0
- Mono
- macOS Monterey
メソッド | 処理速度 |
---|---|
Array.Reverse | 128206ms |
Enumerable.Reverse | 484ms |
- Unity2019.4.0f1
- .NET 4.x
- Mono
- macOS Monterey
メソッド | 処理速度 |
---|---|
Array.Reverse | 125947ms |
Enumerable.Reverse | 440ms |
ひとこと
Unity2021以上か未満かで速度にかなりの違いがでてきていました。
おそらく.NET
のバージョンによる違い?なのかなとも思いますが、他の要因なのかもしれません。
Array.Reverse
はO(n)
なはずですし、なにかが悪さしている気がします。
何か知っている方がいましたらコメント等で教えていただけますと幸いです。
追記
.NET Fxでも元々 Mono が遅くて(純正はネイティブ側対応が入っているからか大抵速い)、.NET Standard 2.1 / .NET Core 2.0 以降から入った Array.Reverse<T> を含む新しい実装が Unity / Mono側にも入ったので改善されたということっぽい。 https://t.co/GJVa6LjmZA
— Mayuki Sawatari (@mayuki) July 28, 2022