比較してみた

オレ的直感型予想: キャストが最も複雑な処理をするために遅く、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);
        }

*1:互換性のない型に対する as 演算子は isinst に変換され、それなりにコストがあります。

*2:インターフェース型への変換演算子は作成できないため、実装リストの確認しかしないはず