パフォーマンスのためにCで作成しますか?[閉まっている]


32

Cは通常C ++よりもパフォーマンスが優れているとよく耳にします。MSVCがCの最新の標準をサポートしていないように思えるまで、それ以外のことは何も考えていませんでしたが、最新のものはC99をサポートしています(私の知る限り)。

OpenGLでレンダリングするためのコードを含むライブラリを作成して、再利用できるようにすることを計画していました。グラフィックに関しては、パフォーマンスの向上を歓迎するため、ライブラリをCで作成することを計画していました。

しかし、それは本当に価値があるでしょうか?ライブラリを使用するコードはおそらくC ++で書かれているため、一般的にはC ++でコーディングすることを好みます。

ただし、パフォーマンスにわずかな違いさえ生じる場合は、Cを使用します。

また、このライブラリは、Windows / OS X / Linux全体で動作するようにするものであり、すべてをネイティブにコンパイルする可能性があります(Windows用のMSVC、OS X用のClangまたはGCC、およびLinux用のGCC)。 。またはおそらくすべてのインテルのコンパイラ)。

私は周りを見回し、いくつかのベンチマークなどを見つけましたが、私が見たものはすべて、MSVCとClangではなくGCCを扱っています。また、ベンチマークでは使用言語の標準については言及していません。誰もこれについて考えていますか?

編集:数年以上の経験を経て、この質問に対する私の見解を共有したかっただけです。C ++でこの質問をしていたプロジェクトを書くことになりました。私は、Cでリンクできるようにするために、できる限り少量のパフォーマンスを引き出したいと考えていたのと同じ頃に、Cで別のプロジェクトを開始しました。数ヶ月前、私は本当にマップと高度な文字列操作。私はC ++標準ライブラリのこの能力を知っていて、最終的に標準ライブラリのそれらの構造は、妥当な時間内にCで実装できるマップや文字列よりもパフォーマンスが高く安定しているという結論に達しました。Cでリンク可能であるという要件は、C ++コードへのCインターフェイスを記述することで簡単に満たされました。これは、不透明(OPAQUE)型で迅速に行われました。C ++でのライブラリの書き換えは、Cでの書き込みよりもはるかに高速であるようで、バグ、特にメモリリークが発生しにくい傾向がありました。プラットフォーム固有の実装を使用するよりもはるかに簡単な標準ライブラリスレッドライブラリを使用することもできました。最終的には、C ++でライブラリを作成することで、パフォーマンスコストをわずかに抑えながら大きなメリットを得ることができたと思います。私はまだC ++バージョンのベンチマークを行っていませんが、私が書いたものよりも標準ライブラリのデータ構造を使用することで、ある程度のパフォーマンスを得た可能性さえあると信じています。ライブラリをC ++で記述すると、パフォーマンスコストはおそらくわずかですが、大きなメリットが得られたと思います。私はまだC ++バージョンのベンチマークを行っていませんが、私が書いたものよりも標準ライブラリのデータ構造を使用することで、ある程度のパフォーマンスを得た可能性さえあると信じています。ライブラリをC ++で記述すると、パフォーマンスコストはおそらくわずかですが、大きなメリットが得られたと思います。私はまだC ++バージョンのベンチマークを行っていませんが、私が書いたものよりも標準ライブラリのデータ構造を使用することで、ある程度のパフォーマンスを得た可能性さえあると信じています。


9
最新のMSVCサポートは、実際にはC89です。
確実に

4
@detly Visual Studio 2013では、C99機能の大部分がサポートされています。完全にサポートされているわけではありませんが、実際にはC99の作成に使用しても構いません。
コンガスボン

4
@ danielu13-CがC ++よりもパフォーマンスが優れていると聞いたのはどこですか?
ラムハウンド

1
@Sebastian-LaurenţiuPlesciucこれらのリンクは実際には役に立たないと思います。最初の質問は、ほぼ同じ質問であるprogrammers.stackexchange.com/q/113295/76444で十分に反論される可能性がありますが、リンクのようにcではなくc ++を支持します。2番目のリンクについては、linus torvaldsの暴言です。彼が本当にC ++を嫌い、棒で触れないことを誰もが知っていることを願っていますが、C ++に関する彼の暴言はほとんど客観的ではなく、個人的な意見と偏見に満ち、言語の現実を実際に反映していません。少なくともそれは私の意見です。
user1942027

1
@RaphaelMiedlまた、これはかなり前の2007年に書かれたもので、C ++コンパイラとC ++言語はそれ以来進化していると述べました。とにかく、使用する言語を選択するのはプログラマー次第です。
セバスチャン・ローレンニウ・プレシウツ

回答:


89

私は、人々は、多くの場合、それはに簡単ですので、Cが速くC ++よりであることを主張すると思う理由 C. C ++でのパフォーマンスについては、本質的に遅いか速いものではなく、特定のC ++コードが隠されたパフォーマンスの低下を不明瞭かもしれません。たとえば、C ++コードの一部を見るとすぐには表示されないコピーや暗黙的な変換が存在する場合があります。

次のステートメントを見てみましょう。

foo->doSomething(a + 5, *c);

さらにdoSomething次のシグネチャがあると仮定しましょう。

void doSomething(int a, long b);

次に、この特定のステートメントのパフォーマンスへの影響の可能性を分析してみましょう。

Cでは、その意味は非常に明確です。foo構造体へのポインタのみでありdoSomething、関数へのポインタでなければなりません。*clongを逆参照し、a + 5整数加算です。唯一の不確実性は、次のタイプに由来しaます。それが整数でない場合、何らかの変換が行われます。しかし、それとは別に、この単一のステートメントのパフォーマンスへの影響を定量化するのは簡単です。

それでは、C ++に切り替えましょう。同じステートメントは、非常に異なるパフォーマンス特性を持つことができます。

  1. doSomethingさらには、非仮想メンバー関数(安い)、仮想メンバー関数(もう少し高価)std::function、ラムダなどです。さらに悪いfooことに、operator->未知の複雑さの操作でオーバーロードするクラス型である可能性があります。したがって、呼び出しのコストを定量化するためにdoSomethingfooandの正確な性質を知る必要がありdoSomethingます。
  2. a整数、整数への参照(追加の間接参照)、またはを実装するクラスタイプを指定できますoperator+(int)。演算子は、暗黙的にに変換可能な別のクラス型を返すこともできintます。繰り返しになりますが、パフォーマンスコストはステートメントだけでは明らかではありません。
  3. cを実装するクラス型である可能性がありますoperator*()long*などへの参照にもなります。

写真が撮れます。C ++の言語機能のためには、それはほかにC.今であるよりも、それは単一の文のパフォーマンスコストを定量化するためにはるかに困難である、などの抽象化はstd::vectorstd::string一般的に自分自身のパフォーマンス特性を持つC ++、および非表示の動的メモリ割り当て(に使用されています@Ianの回答もご覧ください)。

結論として、一般的には、CまたはC ++のどちらを使用しても達成可能なパフォーマンスに違いはありません。しかし、実際にパフォーマンスが重要なコードの場合、隠れたパフォーマンスのペナルティがはるかに少ないため、人々はCの使用を好むことがよくあります。


1
素晴らしい答え。これは私が私の答えでほのめかしたことですが、あなたはそれをはるかによく説明しています。
イアンゴールドビー

4
これは本当に受け入れられた答えであるべきです。「CはC ++より速い」などのステートメントが存在する理由を説明します。CはC ++よりも高速でも低速でもかまいませんが、通常、特定のCコードが高速/低速である理由を理解することは非常に簡単で、通常は最適化も容易になります。
レオ

この素晴らしい答え(私からの+1)からは何も取らないでください。しかし、この比較ではコンパイラーはリンゴとオレンジになります。It / theyは、CとC ++で同じコードを生成する場合としない場合があります。もちろん、同じソース言語の仮定で物理的に同じプログラムをコンパイルする場合でも、2つのコンパイラまたはコンパイラオプションについて同じことが言えます。
Jロバート

4
ほとんどのC ++ランタイムは、同等のCランタイムと比較して大規模であり、メモリに制約がある場合に問題になると付け加えます。
ジェームズアンダーソン

@JamesAndersonもしあなたが本当にそのメモリに制約があるなら、おそらくランタイムはまったく必要ないでしょう:)
Navin

30

特定の種類のタスクでは、C ++で記述されたコードはCよりも高速です。

C ++を好む場合は、C ++を使用します。ソフトウェアのアルゴリズムの決定と比較して、パフォーマンスの問題は重要ではありません。


6
それはより速くすることができますが、同じ理由でより速くないかもしれません。
ロブ

最適化されたCよりも高速な、C ++で記述された最適化されたコードの例をいくつか教えてください。

1
@TomDworzanski:1つの例は、テンプレートを使用することで、コンパイル時にコードパスに関する決定を決定し、最終的にバイナリでハードコーディングすることができます。インライン化による関数呼び出しを回避します。
-whatsisname

23

C ++の設計原則の1つは、使用しない機能に対して料金を支払わないことです。したがって、C ++でコードを記述し、Cに存在しない機能を使用しない場合、結果としてコンパイルされるコードのパフォーマンスは同等になります(ただし、これを測定する必要があります)。

たとえば、構造体や一連の関連関数と比較して、クラスの使用にかかるコストはごくわずかです。仮想機能にはもう少しコストがかかるため、パフォーマンスを測定して、アプリケーションにとって重要かどうかを確認する必要があります。他のC ++言語機能についても同様です。


3
仮想関数のディスパッチのオーバーヘッドは、物事を分解して仮想化することをやりすぎていない限り、ほとんど無視できます。vtablesは他のコードやデータと比較して小さくなり、vtableを介したインデックス付きブランチは各ルーチン呼び出しに数クロックを追加します。ルーチン呼び出し(すべての呼び出し、復帰呼び出し)が数百から数百万クロックになると、vtableブランチはノイズフロアに埋もれます。
ジョンR.ストローム

6
構造体はC ++のクラスです。
右折

2
@rightfold:もちろんですが、thisポインター言語機能を使用しなくても、構造体へのポインターを渡すC ++コードを作成できます。私が言っていたのはそれだけです。
グレッグヒューギル

4
@John実際のコストはインダイレクションではありませんが(これはいくつかのプロセッサーのプリフェッチで多少混乱するでしょうが)最適化。はい、それはパフォーマンスに大きな影響を与える可能性があります。
Voo

2
@Voo公平を期すと、同等のCコード(ランタイムポリモーフィズムを手動でエミュレートするコード)についても同じことが言えます。最大の違いは、コンパイラがC ++でこの関数をインライン化できるかどうかを判断する方が簡単だと思うことです。
トーマスエディング

14

高レベルの言語が遅い場合がある理由の1つは、低レベルの言語よりも多くのメモリ管理を背後で隠すことができるためです。

低レベルの詳細を抽象化する言語(またはライブラリ、APIなど)は、コストのかかる操作を隠す可能性があります。たとえば、一部の言語では、文字列から末尾の空白を削除するだけで、メモリが割り当てられ、文字列のコピーが作成されます。特にメモリの割り当てとコピーは、タイトなループで繰り返し発生する場合、コストが高くなる可能性があります。

この種のコードをCで記述した場合、それは明白に明らかです。C ++では、割り当てとコピーが抽象化されてどこかにあるクラスになる可能性があるため、C ++ではそうではありません。それらは、無邪気に見えるオーバーロードされた演算子またはコピーコンストラクタの背後に隠されている場合もあります。

必要に応じてC ++を使用してください。しかし、抽象化がその下にあるものがわからない場合に、抽象化の便利さのように見えることに惑わされないでください。

もちろん、プロファイラーを使用して、実際にコードの速度が低下しているものを見つけます。


5

価値のあるものとして、拡張機能セット用にC ++ 11でライブラリを書く傾向があります。共有ポインター、例外、汎用プログラミング、その他のC ++のみの機能などを利用できることが好きです。C ++ 11が好きなのは、気になるすべてのプラットフォームでC ++ 11のかなりの部分がサポートされていることがわかったからです。Visual Studio 2013には多くのコア言語機能とライブラリ実装が用意されており、残りの追加に取り組んでいると思われます。ご存知のように、ClangとGCCはどちらも機能セット全体をサポートしています。

そうは言っても、最近、私はあなたのクエリに直接関係があると思うライブラリ開発に関する本当に素晴らしい戦略について読みました。この記事のタイトルは、「C ++例外を処理するACエラー処理スタイル」です。StefanuDu Toitは、この戦略を「砂時計」パターンと呼んでいます。記事の最初の段落:

「砂時計」パターンと呼ばれるものを使用して、多くのライブラリコードを記述しました。ライブラリを実装し(通常、C ++を使用)、ライブラリへの唯一のエントリポイントとなるC APIでラップします。次に、そのC APIをC ++または他の言語でラップして、豊富な抽象化と便利な構文を提供します。ネイティブのクロスプラットフォームコードに関しては、C APIは比類のないABIの安定性と、FFIを介した他の言語への移植性を提供します。APIを、さまざまなFFIに移植可能であることがわかっているCのサブセットに制限し、ライブラリが内部データ構造の変更を漏洩しないようにします。これについては、今後のブログ投稿で詳しく説明します。


次に、パフォーマンスに関する主要な懸念事項に対処します。

ここでの他の多くの回答と同様に、どちらの言語でもコードを書くことはパフォーマンスの観点からも同様に機能することをお勧めします。個人的な観点からは、言語の機能により、C ++で正しいコードを書く方が簡単だと感じていますが、個人的な好みだと思います。いずれにせよ、コンパイラは本当に賢く、とにかくあなたよりも良いコードを書く傾向があります。つまり、コンパイラはおそらくあなたよりもコードを最適化するでしょう。

多くのプログラマーがこれを言っていることを知っていますが、最初にすべきことは、コードを記述し、それをプロファイリングし、プロファイラーがあなたに提案するところで最適化を行うことです。ボトルネックがどこにあるかがわかると、機能の作成と最適化に時間を費やすことができます。


ここで、言語機能と最適化が実際にどのように機能するかについての楽しい読み物があります:

std :: unique_ptrのオーバーヘッドはゼロです

constexpはコンパイル時の計算を可能にします

移動セマンティクスは、不必要な一時オブジェクトを防ぎます


std::unique_ptr has zero overheadスタックが例外のために巻き戻された場合、コンストラクターを呼び出さなければならないため、これはおそらく(技術的に言えば)真実ではありえません。生のポインタにはこのオーバーヘッドがなく、コードがおそらくスローしない場合でも正しいです。コンパイラは、一般的なケースではこれを証明できません。
トーマスエディング

2
@ThomasEding例外のないコードに関するサイズとランタイムのオーバーヘッドについて言及していました。間違っている場合は修正してください。ただし、例外がスローされないときにランタイムオーバーヘッドがゼロで、必要に応じて例外を伝達できる実行モデルがあります。それでも、いつ例外がスローされる可能性がありますunique_ptrか?宣言さnoexceptれているため、少なくともすべての例外を処理しますが、そもそもどのような種類の例外がスローされるか想像できません。
vmrob

vmrob:ご容赦ください。「コンストラクタ」ではなく「デストラクタ」と書くつもりでした。私も「間違いなく投げない」と書くつもりでした。いいね!
トーマスエディング

2
@ThomasEdingご存知のように、デストラクタが例外をスローしたとしても、それは問題ではないと思います。デストラクタが新しい例外を導入しない限り、オーバーヘッド破壊はゼロです。さらに、デストラクタ全体が最適化された単一のdelete / free呼び出しにインライン化されると思います。
vmrob

4

C ++とCのパフォーマンスの違いは、厳密に言えば言語の何かによるものではありませんが、あなたがしたいことです。クレジットカードと現金のようなものです。それはあなたがより多くを費やすことはありませんが、あなたが非常に訓練されていない限り、とにかくやります。

次に、C ++で作成されたプログラムの例を示します。その後、積極的にパフォーマンスを調整しました。言語に関係なく、積極的なパフォーマンスチューニングを行う方法を知る必要があります。このビデオに示すように、私が使用する方法はランダムな一時停止です。

C ++がやろうとする高価なことは、過剰なメモリ管理、通知スタイルのプログラミング、多層化された抽象化ライブラリ(@Ianが言ったように)へのプログラムカウンターの信頼、スローイングの隠蔽などです。


2

両方の言語で同じことを行う場合、CにはC ++と比較してパフォーマンス上の利点はありません。まともなCプログラマーによって書かれた古いCコードを使用して、有効かつ同等のC ++コードに変換することができます。これは、同様に高速に実行されます(ユーザーとコンパイラーの両方が「制限」キーワードほとんどの人はそうではありません)。

C ++は、(1)標準C ++ライブラリを使用して、ライブラリを使用せずにはるかに高速で簡単に実行できることを行う場合、または(2)標準C ++ライブラリを使用する場合、大幅に異なるパフォーマンスを持つことができます。悪いCでライブラリを再実装するよりもはるかに簡単かつ迅速に処理する


1
これは6つの前の回答で説明したものに比べて大幅のオファーは何もしていないようだ
ブヨ

この答えは、他の誰も言及していない重要なポイントに言及していると思う。一見すると、C ++はCのスーパーセットのように見えるため、高速なC実装を作成できる場合は、同等のC ++実装を作成できるはずです。ただし、C99は、意図しないポインターエイリアシングを回避できるrestrictキーワードをサポートします。C ++にはそのようなサポートはありません。ポインターのエイリアシングを回避する機能は、Fortranの重要な機能であり、高性能アプリケーションに役立ちます。同様のドメインでは、C ++よりもC99の方がパフォーマンスが向上する可能性もあります。
-user27539
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.