3

次のような C++ メソッド シグネチャがあります。

    static extern void ImageProcessing(
        [MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
        [MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
        int inYSize, int inXSize);

内部と外部の両方のタイミング メソッドで関数をラップしました。内部的には、関数は 0.24 秒で実行されています。外部的には、関数は 2.8 秒で実行され、約 12 倍遅くなります。どうしたの?マーシャリングは私をそんなに遅くしていますか?もしそうなら、どうすればそれを回避できますか?安全でないコードに移動して、ポインターなどを使用する必要がありますか? 余分な時間のコストがどこから来ているのか、私はちょっと当惑しています。

4

3 に答える 3

4

この記事を見てください。コンパクト フレームワークに焦点を当てていますが、一般原則はデスクトップにも当てはまります。分析セクションからの関連する引用は次のとおりです。

マネージ呼び出しは、ネイティブ メソッドを直接呼び出しません。代わりに、GC Preemption ステータスを判断するための呼び出しなどのオーバーヘッド ルーチンを実行する必要がある JITted スタブ メソッドを呼び出します (GC が保留中であり、待機する必要があるかどうかを判断するため)。一部のマーシャリング コードがスタブに JIT される可能性もあります。これにはすべて時間がかかります。

編集: JITted コードのパフォーマンスに関するこのブログ記事も読む価値があります。これも CF 固有ですが、それでも関連性があります。呼び出しスタックの深さと perf への影響をカバーする記事もありますが、これはおそらく CF 固有のものです (デスクトップではテストされていません)。

于 2009-01-19T20:54:47.137 に答える
3

2 つの配列パラメーターを IntPtr に切り替えてみましたか? PInvoke は、マーシャリング シグネチャのすべての型が blittable である場合に、絶対的に最速になります。これは、Pinvoke が単なる memcpy でデータをやり取りできることを意味します。

私のチームでは、PInvoke レイヤーを管理する最もパフォーマンスの高い方法を見つけました。

  1. マーシャルされているすべてが blittable であることを保証する
  2. 必要に応じて IntPtr クラスを操作して、配列などの型を手動でマーシャリングする代償を払います。多くのラッパーメソッド/クラスがあるため、これは非常に簡単です。

「これはより高速になります」という回答と同様に、これが独自のコードベースであることをプロファイルする必要があります。いくつかの方法を検討してプロファイリングした後、このソリューションにたどり着きました。

于 2009-01-19T21:06:01.483 に答える
1

悲しいことに、答えはこれらの提案よりもはるかに平凡ですが、それらは役に立ちます。基本的に、私は自分がどのようにタイミングをとっていたかを台無しにしました。

私が使用していたタイミングコードは次のとおりです。

Ipp32s timer;
ippGetCpuFreqMhz(&timer);
Ipp64u globalStart = ippGetCpuClocks();
globalStart = ippGetCpuClocks() *2 - globalStart; //use this method to get rid of the overhead of getting clock ticks

      //do some stuff

Ipp64u globalEnd = ippGetCpuClocks(); 
globalEnd = ippGetCpuClocks() *2 - globalEnd;
std::cout << "total runtime: " << ((Ipp64f)globalEnd - (Ipp64f)globalStart)/((Ipp64f)timer *1000000.0f) << " seconds" << std::endl;

このコードはインテル®コンパイラーに固有のものであり、非常に正確な時間測定を行うように設計されています。残念ながら、その極端な精度は、実行ごとに約2.5秒のコストを意味します。タイミングコードを削除すると、その時間の制約がなくなりました。

ただし、実行時間の遅延はまだあるようです。コードは、そのタイミングコードをオンにすると0.24秒を報告し、現在は約0.35秒のタイミングを報告しています。これは、約50%の速度コストがあることを意味します。

コードを次のように変更します。

  static extern void ImageProcessing(
     IntPtr inImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] inImage,
     IntPtr outImage, //[MarshalAs(UnmanagedType.LPArray)]ushort[] outImage,
     int inYSize, int inXSize);

そして次のように呼ばれます:

        unsafe {
            fixed (ushort* inImagePtr = theInputImage.DataArray){
                fixed (ushort* outImagePtr = theResult){
                    ImageProcessing((IntPtr)inImagePtr,//theInputImage.DataArray,
                        (IntPtr)outImagePtr,//theResult,
                        ysize,
                        xsize);
                }
            }
        }

実行可能時間を0.3秒に短縮します(3回の実行の平均)。私の好みにはまだ遅すぎますが、10倍の速度の向上は確かに私の上司の許容範囲内です。

于 2009-01-19T22:55:26.487 に答える