14

大規模なデータセット間で多くの照合を行うWPFアプリケーションがあり、現在、C#とLINQを使用してPOCOを照合し、グリッドに表示しています。含まれるデータセットの数が増え、データの量が増えるにつれて、パフォーマンスの問題を調べるように求められました。今晩私がテストしていた仮定の1つは、コードの一部をC++CLIに変換する場合に実質的な違いがあるかどうかでした。List<>そのために、5,000,000個のアイテムを作成し、簡単なマッチングを行う簡単なテストを作成しました。基本的なオブジェクト構造は次のとおりです。

public class CsClassWithProps
{
    public CsClassWithProps()
    {
        CreateDate = DateTime.Now;
    }

    public long Id { get; set; }
    public string Name { get; set; }
    public DateTime CreateDate { get; set; }
}

私が気づいたことの1つは、平均して、リストを作成し、偶数IDを持つすべてのオブジェクトのサブリストを作成するという単純なテストでは、C ++ / CLIコードが開発マシン(64ビットWin8)で約8%遅いことでした。 、8GBのRAM)。たとえば、C#オブジェクトが作成およびフィルタリングされる場合は最大7秒かかりましたが、C ++/CLIコードは平均で最大8秒かかりました。なぜそうなるのか知りたくて、ILDASMを使用して、内部で何が起こっているのかを確認しました。C++/CLIコードにコンストラクターに余分なステップがあることに驚きました。最初にテストコード:

static void CreateCppObjectWithMembers()
{
    List<CppClassWithMembers> results = new List<CppClassWithMembers>();

    Stopwatch sw = new Stopwatch();

    sw.Start();

    for (int i = 0; i < Iterations; i++)
    {
        results.Add(new CppClassWithMembers() { Id = i, Name = string.Format("Name {0}", i) });
    }

    var halfResults = results.Where(x => x.Id % 2 == 0).ToList();

    sw.Stop();

    Console.WriteLine("Took {0} total seconds to execute", sw.Elapsed.TotalSeconds);
}

C#クラスは上にあります。C++クラスは次のように定義されます。

public ref class CppClassWithMembers
{
public:
    long long Id;
    System::DateTime CreateDateTime;
    System::String^ Name;

    CppClassWithMembers()
    {
        this->CreateDateTime = System::DateTime::Now;
    }
};

両方のクラスのコンストラクターのILを抽出すると、これが得られます。最初にC#:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       21 (0x15)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  nop
  IL_0007:  nop
  IL_0008:  ldarg.0
  IL_0009:  call       valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
  IL_000e:  stfld      valuetype [mscorlib]System.DateTime CsLibWithMembers.CsClassWithMembers::CreateDate
  IL_0013:  nop
  IL_0014:  ret
} // end of method CsClassWithMembers::.ctor

そしてC++:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       25 (0x19)
  .maxstack  2
  .locals ([0] valuetype [mscorlib]System.DateTime V_0)
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  call       valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
  IL_000b:  stloc.0
  IL_000c:  ldarg.0
  IL_000d:  ldloc.0
  IL_000e:  box        [mscorlib]System.DateTime
  IL_0013:  stfld      class [mscorlib]System.ValueType modopt([mscorlib]System.DateTime) modopt([mscorlib]System.Runtime.CompilerServices.IsBoxed) CppLibWithMembers.CppClassWithMembers::CreateDateTime
  IL_0018:  ret
} // end of method CppClassWithMembers::.ctor

私の質問は、C ++コードがローカルを使用してからの呼び出しの値を格納するのはなぜDateTime.Nowですか?これが発生するC++固有の理由はありますか、それともコンパイラの実装を選択した方法ですか?

パフォーマンスを向上させる方法は他にもたくさんあることはすでに知っています。私はウサギの穴からかなり離れていることを知っていますが、誰かがこれに光を当てることができるかどうか知りたいと思いました。私がC++を実行してから長い時間が経ち、Windows 8の登場と、MicrosoftのC ++への新たな焦点により、更新するのは良いことだと思いました。それもこの演習の動機の一部でしたが、 2つのコンパイラ出力の違いが私の目に留まりました。

4

2 に答える 2

6
System::DateTime CreateDateTime;

これはトリックの質問のように聞こえます。あなたが投稿したILは、あなたが投稿したスニペットによって生成されることはほとんどありません。CreateDateTimeメンバーの実際の宣言は次のとおりです。

System::DateTime^ CreateDateTime;

投稿したILにはっきりと表示されます。値型の値を参照オブジェクトに変換するためのボクシング変換を生成しました。これはC++/ CLIで非常によくある間違いであり、誤って帽子を入力するのは非常に簡単です。コンパイラが実際に警告を生成する必要があるが、生成しないもの。そして、はい、それはコードを台無しにします、ボクシングの変換は無料で来ません。

C ++ / CLIを使用してコードを高速化しようとすると、それ以外の場合は失われた原因になります。C ++ / CLIでマネージコードを記述している限り、C#コンパイラが生成するのと同じ種類のILを取得できます。C ++ / CLIの価値は、アンマネージコードを非常に簡単かつ安価に呼び出すことができることです。ただし、このようなコードでも良い結果が得られる可能性は低いです。呼び出すアンマネージコードは「実質的」である必要があります。これにより、マネージコードからアンマネージコードの実行に切り替えることで発生するペナルティは無視できます。そのコストは、データ変換を必要としない単純な移行のために、少数のCPUサイクルの間で変動します。ピン配列や文字列の変換などを行う必要がある場合は、数百サイクルになります。

于 2012-12-27T19:10:40.313 に答える
2

C#コンパイラが行うことに近い(そして高価なものを取り除くbox)C ++バージョンは次のようになります:

public ref class CppClassWithMembers
{
public:
    long long Id;
    System::DateTime CreateDateTime;
    System::String^ Name;

    CppClassWithMembers() : CreateDateTime(System::DateTime::Now) { }
};
于 2012-12-27T18:16:45.217 に答える