[解決済み】モダンC++はタダで性能を手に入れられる?
質問内容
C++11/14は、単にC++98のコードをコンパイルするだけでも、パフォーマンスが上がると主張されることがあります。その正当性は通常、移動セマンティクスに沿ったもので、いくつかのケースでは rvalue コンストラクタが自動的に生成されたり、今では STL の一部になっているためです。このようなケースは、実は以前はRVOや同様のコンパイラ最適化によってすでに処理されていたのではないか、と私は考えています。
そこで質問なのですが、新しい言語機能をサポートするコンパイラーを使用して、修正なしでより速く実行できる C++98 コードの実際の例を教えてください。標準に準拠したコンパイラでは、コピー消去を行う必要はなく、移動セマンティクスが高速化をもたらすかもしれないことは理解していますが、病的でないケースを見てみたいのです。
EDIT: 念のため、私は新しいコンパイラが古いコンパイラより速いかどうかを尋ねているのではなく、私のコンパイラのフラグに -std=c++14 を追加すると、より速く実行できるコードがあるかどうか(コピーを避ける、しかし移動セマンティクス以外に何か思いつくなら、私も興味がある)です。
解決方法は?
C++03コンパイラをC++11として再コンパイルすると、実装の品質とは実質的に無関係に、無制限にパフォーマンスが向上する5つの一般的なカテゴリを私は知っています。 これらはすべて、移動セマンティクスのバリエーションです。
std::vector
リロケート
struct bar{
std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03
を実行するたびに
foo
のバッファが再割り当てされると、C++03 では、すべての
vector
で
bar
.
C++11では、その代わりに
bar::data
のように、基本的に自由です。
今回の場合、これは内部での最適化に依存しています。
std
コンテナ
vector
. 以下のすべてのケースで
std
コンテナは、C++オブジェクトであるため、効率的な
move
のセマンティクスは、コンパイラをアップグレードすると、C++11で "自動的に"されます。 を含む、それをブロックしないオブジェクト。
std
コンテナもまた、自動的に改善された
move
のコンストラクタを使用します。
NRVOの失敗
NRVO(名前付き戻り値最適化)が失敗すると、C++03ではコピーに、C++11では移動に戻る。 NRVOの失敗は簡単です。
std::vector<int> foo(int count){
std::vector<int> v; // oops
if (count<=0) return std::vector<int>();
v.reserve(count);
for(int i=0;i<count;++i)
v.push_back(i);
return v;
}
とかでもいい。
std::vector<int> foo(bool which) {
std::vector<int> a, b;
// do work, filling a and b, using the other for calculations
if (which)
return a;
else
return b;
}
3つの値 -- 返り値、そして関数内の2つの異なる値 -- があります。 エリジョンによって、関数内の値は戻り値に「マージ」されますが、互いにマージされることはありません。 エリジオンは、関数内の値を戻り値に「マージ」することができますが、互いにマージすることはできません。
基本的な問題は、NRVOエリシオンは壊れやすいということです。
return
のサイトで突然、診断が出ずにその場所でパフォーマンスが大幅に低下することがあります。 NRVOが失敗するほとんどのケースで、C++11の場合は
move
C++03はコピーで終わりますが。
関数の引数を返す
ここでもエリシオンは不可能です。
std::set<int> func(std::set<int> in){
return in;
}
C++11では、これは安価です:C++03では、コピーを回避する方法はありません。 関数への引数は、パラメータと戻り値の寿命と位置が呼び出し側のコードで管理されるため、戻り値でエライ目に遭うことはないのです。
しかし、C++11では、一方から他方へ移行することができます。 (あまりおもちゃではない例では、何かが
set
).
push_back
または
insert
しかし、C++11 では、rvalue move insert 演算子をオーバーロードすることで、コピーを節約することができます。
struct whatever {
std::string data;
int count;
whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );
C++03では、一時的な
whatever
が作成され、それがベクターにコピーされます。
v
. 2
std::string
バッファが割り当てられ、それぞれ同じデータが割り当てられ、1つは捨てられる。
C++11では、一時的な
whatever
が作成されます。 その
whatever&&
push_back
をオーバーロードすると
move
は、その一時的なものをベクターに
v
. 一つ
std::string
バッファが確保され、ベクターに移動されます。 空の
std::string
は破棄されます。
課題
以下の@Jarod42さんの回答から盗用。
エリシオンはassignで発生しないが、move-fromは発生する。
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
こちら
some_function
はエライドの候補を返しますが、オブジェクトを直接構築するために使用されていないため、エライドすることはできません。 C++03では、上記の結果、テンポラリーの内容が
some_value
. C++11では、それは
some_value
基本的に自由です。
上記の効果を十分に発揮させるためには、ムーブコンストラクタと代入を合成してくれるコンパイラが必要です。
MSVC 2013 は、移動コンストラクタを
std
コンテナで使用されますが、型に対する移動コンストラクタは合成されません。
そのため
std::vector
などはMSVC2013では改善されませんが、MSVC2015から改善される予定です。
clang と gcc はずっと以前から暗黙の移動コンストラクタを実装しています。 Intel の 2013 年版コンパイラーは、暗黙的な移動コンストラクターの生成をサポートします。
-Qoption,cpp,--gen_move_operations
(MSVC2013とのクロスコンパチビリティのために、デフォルトではそうなっていません)。
関連
-
[解決済み] テスト
-
[解決済み】文字列関数で'char const*'のインスタンスを投げた後に呼び出されるterminate [閉店].
-
[解決済み] 非常に基本的なC++プログラムの問題 - バイナリ式への無効なオペランド
-
[解決済み】Visual C++で "Debug Assertion failed "の原因となる行を見つける。
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み] スタックアロケーションにより初期化されていない値が作成された
-
[解決済み] Pythonスクリプトのプロファイリングはどのように行うのですか?
-
[解決済み] Eclipseを高速化する方法とは?
-
[解決済み] noexceptを本当に使うべきはいつですか?
-
[解決済み】画像処理。コカ・コーラ缶」認識のためのアルゴリズム改良
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン