比較してみた
オレ的直感型予想: キャストが最も複雑な処理をするために遅く、Generics 経由が一番シンプルで早い。
ちなみに、つぶやいた後で push/pop って IL は ld やん…と思ったが、push/pop のほうがわかりやすいからいいや、と訂正しなかった(笑
static class Program { static void Main(string[] args) { foreach (var i in Dup(1, 2, 3)) Console.WriteLine(i); } public static IEnumerable<T> Dup<T>(IEnumerable<T> source) { return source.Select(it => Enumerable.Repeat(it, 2)) .SelectMany(_ => _); } #if false public static IEnumerable<T> Dup<T>(params T[] items) { return Dup(items.AsEnumerable()); } #elif false public static IEnumerable<T> Dup<T>(params T[] items) { return Dup((IEnumerable<T>) items); } #elif true public static IEnumerable<T> Dup<T>(params T[] items) { return Dup(items as IEnumerable<T>); } #endif
; return Dup(items.AsEnumerable()); ; IL ldarg.0 call class IEnumerable<T> AsEnumerable<T>(IEnumerable<T>) call class IEnumerable<T> Dup<T>(IEnumerable<T>) ret ; IA32 MOV ECX, DWORD PTR [ebp-4] MOV EAX, ECX MOV DWORD PTR [ebp-8], EAX MOV ECX, DWORD PTR [ebp-8] CALL DWORD PTR [Dup]
; return Dup((IEnumerable<T>) items); ; IL ldarg.0 castclass IEnumerable<T> call class IEnumerable<T> Dup<T>(IEnumerable<T>) ret ; IA32 MOV EDX, DWORD PTR [ebp-4] MOV ECX, [IEnumerable<T>] CALL DWORD PTR [CastClass] MOV DWORD PTR [ebp-8], EAX MOV ECX, EAX CALL DWORD PTR [Dup]
; return Dup(items as IEnumerable<T>); ; IL ldarg.0 call class IEnumerable<T> Dup<T>(IEnumerable<T>) ret ; IA32 MOV ECX, DWORD PTR [ebp-4] CALL DWORD PTR [Dup]
結果、既知の型に対する as 演算子が C# コンパイラによる最適化で消えちゃって最速でした。*1 Generics を使った型変換も無駄なスタックの消費があるだけなのでまずまず期待通りのパフォーマンスとなりそうです。キャスト演算は非常に複雑な機能をもっている変換処理を呼び出すことになるためもっとも時間がかかることが予想されます。もちろん、as 演算子と同じで実装済みのインターフェースに対する変換処理を呼び出すだけですので、実行コストはかなり低くて高速に動作することも期待できます*2が、他の2つの記述に比べた相対的な評価としては桁いくつぐらいの速度差になるかな?ってところでしょうね…。
C# しか読めねーって人のために、疑似コードも。EAX は戻り値、ECX, EDX は引数になるのだけど、あえて代入文として ret, p1, p2 とか書いてみた。
public static IEnumerable<T> Dup<T>(params T[] items) { //return Dup(items.AsEnumerable()); var p1 = items; var ret = p1; var tmp = ret var p1 = tmp; return Dup(p1); } public static IEnumerable<T> Dup<T>(params T[] items) { //return Dup((IEnumerable<T>) items); var p1 = items; var p2 = typeof(T) var ret = CastClass(p1, p2); var p1 = ret; return Dup(p1); } public static IEnumerable<T> Dup<T>(params T[] items) { //return Dup(items as IEnumerable<T>); var p1 = items; return Dup(p1); }