C ++とC#のパフォーマンスの比較

C ++およびC#のパフォーマンスに関しては、さまざまな意見があります。



たとえば、JITコンパイル中のプラットフォームの最適化により、C#コードがより高速に動作することを主張するのは困難です。 または、.Net Frameworkコア自体が非常によく最適化されているという事実があります。



一方、強力な引数は、C ++がマシンコードに直接コンパイルされ、最小限の数のヘルパーとレイヤーで動作することです。



また、マイクロレベルはマクロレベルでのパフォーマンスを特徴付けていないため、コードのパフォーマンスが正しくないという意見もあります。 (マクロレベルでパフォーマンスが損なわれる可能性があることは確かに同意しますが、マクロレベルでのパフォーマンスがマイクロレベルでのパフォーマンスにならないことにほとんど同意しません)



C ++コードはC#コードよりも約10倍高速であるという主張がありました。



このように相反するさまざまな意見はすべて、最も同一かつ単純なコードを一方の言語と他方の言語で記述し、実行時間を比較する必要があるという考えにつながります。 それが私がしたことです。





この記事で実行したテスト



私は、最も基本的なテストを実行したかったのです。これは、ミクロレベルで言語間の違いを示します。 テストでは、コンテナの作成、入力、処理、削除、つまりデータ操作の全サイクルを実行します。 通常のアプリケーションで。

処理を可能な限り同一にするために、 int型のデータを使用します。 Visual Studio 2010を使用して、デフォルト構成のリリースビルドのみを比較します。



コードは次のことを行います。

1.配列\コンテナの割り当て

2.配列\コンテナを昇順で埋める

3.バブルメソッドを使用して配列\コンテナを降順で並べ替えます(並べ替えメソッドを比較するのではなく、実装方法を比較するため、最も単純なメソッドが選択されます)

4.アレイ\コンテナの削除



コードは、さまざまなタイプのコンテナとそれらの割り当て方法が異なるいくつかの代替方法で作成されました。 記事自体では、原則として、各言語で可能な限り迅速に機能するコード例を示します。 実行速度を計算するための挿入を含む残りの例は、 ここで完全に見ることができます



テストコード

C ++ HeapArray C# HeapArray固定tmp


ご覧のとおり、コードは非常にシンプルでほとんど同じです。 ランタイムを測定するC#で明示的に削除を実行することはできないため、 items = null;を使用します。 GC.Collect(); コンテナ(この例全体)以外を作成しなかった場合、 GC.Collectもコンテナのみを削除する必要があるため、これはdelete []アイテムのかなり適切な代替品だと思います



int tmpの宣言 C#の場合のサイクルの後、時間が節約されるため、C#の場合のテストのこのバリエーションが考慮されます。



さまざまなマシンで、このテストのさまざまな結果が得られました(明らかにアーキテクチャの違いによる)が、測定結果によりコードパフォーマンスの違いを評価できます。



測定では、コード実行時間を計算するために、 QueryPerformanceCounterが使用され、テストプラットフォームフォームでの作成、入力、並べ替え、および削除の「時間」が測定され、次の結果が得られました。







表は次のことを示しています。

1.最速のC#実装は、最速のC ++実装よりも30〜60%遅い(プラットフォームに依存)

2.最も速いC ++実装と最も遅いC ++実装との間広がりは1〜65%です (プラットフォームに依存)

3. C#で最も遅い(もちろん考慮される)実装で、最も遅いC ++実装よりも約4倍遅い

4.ソート段階に最も時間がかかります(これにより、今後さらに詳細に検討します)



std :: vectorは、古いプラットフォームでは低速のコンテナですが、現代のプラットフォームでは非常に高速であるという事実に注意する価値があります。 また、最初の.Netテストの場合の「削除」時間がわずかに長くなるという事実も、テストデータに加えて他のエンティティが削除されるという事実によるものと思われます。



C ++とC#コードのパフォーマンスの違いの理由



それぞれの場合にプロセッサによって実行されるコードを見てみましょう。 これを行うには、最速の例から並べ替えコードを取得し、コンパイル先を確認します。VisualStudio 2010デバッガーと逆アセンブリモードを使用して調べます。その結果、並べ替えの次のコードが表示されます。

C ++ C#
for(int i = 0; i <10000; i ++)

00F71051 xor ebx、ebx

00F71053 mov esi、edi

for(int j = i; j <10000; j ++)

00F71055 mov eax、ebx

00F71057 cmp ebx、2710h

00F7105D jge HeapArray + 76h(0F71076h)

00F7105F nop

{

if(アイテム[i] <アイテム[j])

00F71060 mov ecx、dword ptr [edi + eax * 4]

00F71063 mov edx、dword ptr [esi]

00F71065 cmp edx、ecx

00F71067 jge HeapArray + 6Eh(0F7106Eh)

{

int tmp = items [j];

アイテム[j] =アイテム[i];

00F71069 mov dword ptr [edi + eax * 4]、edx

アイテム[i] = tmp;

00F7106C mov dword ptr [esi]、ecx

for(int j = i; j <10000; j ++)

00F7106E inc eax

00F7106F cmp eax、2710h

00F71074 jl HeapArray + 60h(0F71060h)

for(int i = 0; i <10000; i ++)

00F71076 inc ebx

00F71077 esiを追加、4

00F7107A cmp ebx、2710h

00F71080 jl HeapArray + 55h(0F71055h)

}

}



int tmp;

for(int i = 0; i <10000; i ++)

00000076 xor edx、edx

00000078 mov dword ptr [ebp-38h]、edx

for(int j = i; j <10000; j ++)

0000007b mov ebx、dword ptr [ebp-38h]

0000007e cmp ebx、2710h

00000084 jge 000000BB

00000086 mov esi、dword ptr [edi + 4]

{

if(アイテム[i] <アイテム[j])

00000089 mov eax、dword ptr [ebp-38h]

0000008c cmp eax、esi

0000008e jae 000001C2

00000094 mov edx、dword ptr [edi + eax * 4 + 8]

00000098 cmp ebx、esi

0000009a jae 000001C2

000000a0 mov ecx、dword ptr [edi + ebx * 4 + 8]

000000a4 cmp edx、ecx

000000a6 jge 000000B0

000000a8 mov dword ptr [edi + ebx * 4 + 8]、edx

アイテム[i] = tmp;

000000ac mov dword ptr [edi + eax * 4 + 8]、ecx

for(int j = i; j <10000; j ++)

000000b0はebxを追加、1

000000b3 cmp ebx、2710h

000000b9 jl 00000089

for(int i = 0; i <10000; i ++)

000000bb inc dword ptr [ebp-38h]

000000be cmp dword ptr [ebp-38h]、2710h

000000c5 jl 0000007B

}

}





ここで何が見えますか?

C#の19のC ++命令と23の場合、違いはそれほど大きくありませんが、他の最適化を備えたコンパートメントでは、C#コードの実行時間が長くなる理由を説明できると思います。



C#の実装には、いくつかの質問もあります

移行を実行するjae 000001C2

000001c2で731661B1を呼び出す

これは明らかにランタイムの違いにも影響し、追加の遅延をもたらします。



その他の性能比較



C ++およびC#のパフォーマンスを測定した他の記事があることに注意してください。 私に出会ったもののうち、最も意味のあるものと思われたのは、直接的なベンチマークでした:C ++ vs .NET



この記事の著者は、一部のテストでC#を「再生」し、C ++でのSSE2の使用を禁止しました。したがって、フローティングスチールでのC ++テストの結果は、SSE2を有効にした場合よりも約2倍遅くなりました。 この記事では、著者の方法論に対する別の批判を見つけることができます。その中には、C ++でのテスト用のコンテナーの非常に主観的な選択があります。



ただし、SSE2なしの浮動小数点テストを考慮せず、テスト方法論の他の多くの機能を調整する場合、この記事で得られた結果は検討する価値があります。



測定結果に基づいて、多くの興味深い結論を出すことができます。

1. C ++セールスビルドはリリースビルドよりも著しく遅くなりますが、C#セールスとリリースビルドの違いはそれほど重要ではありません。

2. .Net FrameworkでのC#パフォーマンスMonoでのパフォーマンスよりも著しく(2倍以上)高い

3. C ++の場合、C#の同様のコンテナーよりも実行速度が遅いコンテナーを見つけることは十分に可能であり、別のコンテナーを使用する以外、最適化はそれを克服するのに役立ちません。

4. C ++の一部のファイル操作は、C#の対応するものよりも著しく遅くなりますが、代替手段は、C#の対応するものよりも顕著に高速です。



Windowsを要約して説明するために、この記事では同様の結果を出しました。C#コードはC ++コードよりも遅く、約10〜80%です。



-10 ..- 80%はいくらですか?

たとえば、C#で開発する場合は、常に最適なソリューションを使用します。これには、非常に優れたスキルが必要です。 そして、10..80%の生産性のパフォーマンス損失に収まると仮定します。 これはどのように私たちを脅かしますか? これらの割合を他のパフォーマンス指標と比較してみましょう。



たとえば、1990年から2000年にかけて、シングルスレッドプロセッサのパフォーマンスは年間で約50%向上しました。 また、2004年以降、プロセッサパフォーマンスの成長率は低下しており、少なくとも2011年までは年間21%に過ぎませんでした。

画像

シングルスレッドCPUパフォーマンスの振り返り



期待されるパフォーマンスの向上は非常にあいまいです。 2013年と2014年に 21%を超える成長率が示された可能性は低く、さらに、将来の成長率はさらに低くなると予想されます。 少なくとも、新しいテクノロジーを開発するインテルの計画は毎年控えめになっています...



評価の別の方向は、エネルギー効率と鉄の低コストです。 たとえば、 ここでは、トップエンドハードウェア+ 50%のシングルスレッドパフォーマンスと言えば、プロセッサのコストが2〜3倍になることがわかります。



エネルギー効率とノイズの観点から-パッシブ冷却を備えた経済的なPCを組み立てることは非常に現実的ですが、パフォーマンスを犠牲にする必要があり、この犠牲は食いしん坊で熱いが生産的な鉄に比べて約50%以上のパフォーマンスになる可能性があります。



プロセッサのパフォーマンスがどのように成長するかは正確にはわかっていませんが、年間生産性が21%向上した場合、C ++アプリケーションと比較してC#アプリケーションのパフォーマンスは0.5〜4年遅れる可能性があります。 たとえば、10%の成長の場合、ラグはすでに1〜8年です。 ただし、実際のアプリケーションははるかに遅れることがあります。以下で理由を検討します。



開発を節約するために、10..80%の生産性という被害者の収益性を評価することは想定していません。 明らかに、この収益性は、これらの10..80%を他の方法で獲得するコストに依存します(つまり、鉄による)。 ただし、新たな傾向は、鉄のパフォーマンスの後続の各割合が前の割合よりも高くなることを示しています。これは、遅かれ早かれ、コードを最適化することで追加のパフォーマンスを得ることが安くなる状況につながる可能性が高いです。



実際の評価とは何ですか?



一方で、常に最大のパフォーマンスを発揮するほど最適な記述をすることはほとんどありません。



しかし、他方、より重要なことは、プログラムの実行時間(実行時)がコードによって占有され、システムコードはどれくらいかということです。



たとえば、コードがアプリケーションまたはサービスの実行時間の1%を要する場合、このコードのパフォーマンスが10倍低下しても、アプリケーションの速度に大きな影響はなく、パフォーマンスへの影響は約10%にすぎません。



しかし、アプリケーションの実行時間の約100%がOSコードではなくコードを使用する場合は、まったく別の問題です。 この場合、-80%と大きなパフォーマンス損失の両方を簡単に得ることができます。



結論

もちろん、上記のすべてから、C#からC ++に緊急に切り替える必要があるということにはなりません。 第1に、C#での開発は安価であり、第2に、いくつかのタスクでは、最新のプロセッサーのパフォーマンスが過剰であり、C#内での最適化さえ必要ありません。 しかし、オーバーヘッドコスト、つまり管理された環境を使用するための料金、およびこれらのコストの評価に注意を払うことは私にとって重要だと思われます。 明らかに、市場の状況と新たな課題に応じて、この手数料は多額になることがあります。 C#とC ++を比較する他の側面については、前回の記事「 C ++とC#の選択」を参照してください



All Articles