DllImportの復権

http://d.hatena.ne.jp/akiramei/20040417#p1にて

P/Invoke、結構遅いぞ!ILを使うと半分くらいに出来ますが、ここまで頑張らないと駄目なんでしょうか?

とか言いましたが、私が悪かったようです。DllImport経由でUnmanaged関数を呼び出す場合、デフォルトではコール毎にセキュリティチェックが走るのですが、これを最初の一回だけに変更することが出来ました。以下、実験君。

using System;
using System.Runtime.InteropServices;
using System.Security;

class App {
  const int LOOP = 10000000;

  // 通常のDllImport
  [DllImport("msvcrt.dll", CallingConvention=CallingConvention.Cdecl)]
  public extern static int time(IntPtr t);

  // アクセス許可のチェックの最適化
  [SuppressUnmanagedCodeSecurity]
  [DllImport("msvcrt.dll", EntryPoint="time", CallingConvention=CallingConvention.Cdecl)]
  public extern static int time_suppress(IntPtr t);

  public static void Main() {
    DateTime t;

    t = DateTime.Now;
    for (int i = 0; i < LOOP; ++i)
      time(IntPtr.Zero);
    Console.WriteLine(DateTime.Now - t);

    t = DateTime.Now;
    for (int i = 0; i < LOOP; ++i)
      time_suppress(IntPtr.Zero);
    Console.WriteLine(DateTime.Now - t);
  }
}

/*
00:00:02.2187500
00:00:00.8437500 */

ご覧の通り、25倍くらい速くなってます。勿論、呼び出す関数が重くなれば、オーバーヘッドの割合も小さくなりますので、これほど極端な結果にはならないと思います。とは言っても、これは結構な違いなのでパフォーマンスが必要で信頼できるUnmanagedコードを呼び出す場合には、選択肢に入れるのもアリではないでしょうか?
遅いなんて言ってごめんなさーい>DllImport(^^;

技術的詳細については、こちらが参考になると思います。

(追記) SuppressUnmanagedCodeSecurityを使う場合は、CallingConventionをきっちり指定しないと上手く行かない気がします。通常は自動判定してくれますが、このカスタム属性を付けるとそんな処理すら削られるのかも知れません。