4

System.Runtime.InteropServices.DllImportAttribute を使用して、C# の dll から C コードを呼び出す速度をテストしていました。C 関数は、カスタム構造体を生成し、それに値を入力し、計算を行い、結果を返します。このプロセスをループで何十万回も繰り返し、ループ前とループ後のティック数を記録しました。次に、まったく同じ関数をストレート C# で作成し、この試行を繰り返しました。直接的な C# メソッドは、アンマネージ DLL を使用するよりもはるかに高速でした。なんで?管理されていないからの速度の向上はないようです。

c2cstest.c

#include <stdio.h>
struct test {
double a;
double b;
double c;
};
_declspec(dllexport) double myCfunction(double input) {
struct test one;
one.a = input;
one.b = one.a * one.a;
one.c = one.b * one.a;
return one.c;
}

cl /LD ccscstest.c runCcode.cs

using System;
using System.Runtime.InteropServices;
class test
{
[DllImport("c2cstest.dll")]
public static extern double myCfunction (double input);
static void Main()
{
double x = 5.25;
double result = 0.0;
long tick1 = DateTime.Now.Ticks;
for(int y = 100000; y > 0; y--)
{
result = myCfunction(x);
}
long tick2 = DateTime.Now.Ticks;
Console.WriteLine("Answer is {0}.  Dllimport took {1} ticks.", result, tick2-tick1);
}
}

出力: 答えは 144.703125 です。Dllimport は 250000 ティックかかりました。RunCScode.cs

using System;
using System.Runtime.InteropServices;
struct test
{
public double a;
public double b;
public double c;
}
class testclass
{
double Mycsfunction (double input)
{
test one;
one.a = input;
one.b = one.a * one.a;
one.c = one.b * one.a;
return one.c;
}
static void Main()
{
double x = 5.25;
double result = 0.0;
testclass ex = new testclass();
long tick1 = DateTime.Now.Ticks;
for(int y = 100000; y > 0; y--)
{
result = ex.Mycsfunction(x);
}
long tick2 = DateTime.Now.Ticks;
Console.WriteLine("Answer is {0}.  Straight CS took {1} ticks.", result, tick2-tick1);
}}

出力: 答えは 144.703125 です。ストレート CS は 50000 ティックかかりました。

追加: さまざまな方法を試した後、この人の Techniques of Calling Unmanaged Codeと同じ結論に達しましたが、彼は私よりも多くの方法を試しました。

結論: 単純な単純な関数呼び出しは価値がありません (特にループしている場合)。アンマネージ関数内にループを配置すると、確かに役立つはずです。非常に大きな関数は価値があります。さまざまな方法を試してみても、マーシャリングは効率的なテクノロジではありません。

4

1 に答える 1

2

ベンチマークを正しく行うのは難しいものです。特にマイクロベンチマークだと思います。1 つには、ストレートな C# テストで自分が考えているものを測定していません。コンパイラ (またはおそらくジッター) は、結果がループ不変であることを認識しているため、ループを 1 回だけ実行しています。

私のマシンでの結果は次のrunCScode.csとおりです。

c:\temp>csc runCScode.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.


c:\temp>runCScode
Answer is 144.703125.  Straight CS took 10001 ticks.

forループ行をコメントアウトした結果は次のとおりです。

c:\temp>csc runCScode.noloop.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.


c:\temp>runCScode.noloop
Answer is 144.703125.  Straight CS took 10001 ticks.

そのコードのビットを一度実行するのに非常に時間がかかるのは奇妙だと思ったので、DateTime何らかの理由で実際に値を取得する際にオーバーヘッドが発生したと推測しました (理由はわかりません - 単なる推測でした)。実際、私はコンパイラが定数値resultを そのため、の初期化後に次の行を追加しましたtick1

tick1 = DateTime.Now.Ticks;

はい、そうです-もう一度リロードtick1DateTime.Now.Ticksました。

次に、テストを再度実行しました。

c:\temp>runCScode.noloop
Answer is 144.703125.  Straight CS took 0 ticks.

(注: 公平を期すために、リロードしなかったベンチマークを実行した時間の約 3 分の 1tick1でカウントが 0 になりました。しかし、ほとんどの実行ではカウントが 10000+/-1 になりました。リロードするバージョンはtick1常に報告されました。 0 ティックのカウント)。

とはいえ、何人かの人々がコメントで指摘したように、C がすべての点で C# よりも大幅に高速になるという期待はなく、C 関数を呼び出す小さな操作では、P/Invoke とパラメーター/結果によってオーバーヘッドが課せられるという期待はありません。マーシャリング。また、ジッターが最適化を実行する機会も失われます。したがって、結論として、.NET アプリケーションのパフォーマンスのために C を使用することについて心配する必要はないと思います。ただし、高速化が必要な領域があり、ネイティブ C または C++ が提供できる何かがあると信じる理由がある場合を除きます。 C# (または C++/CLI) からは取得できません。

その理由は、いくつかの浮動小数点演算ではありません。

最後に、C または C++ DLL を呼び出す主な理由の 1 つは、必ずしもパフォーマンスのためではないということを述べておく必要があります。これは、使用したいネイティブ DLL に存在するライブラリ (または単一の API) があり、その機能が .NET クラスでは利用できないためです。

于 2012-05-03T05:11:43.667 に答える