ブログトップ 記事一覧 ログイン 無料ブログ開設

神様なんて信じない僕らのために このページをアンテナに追加 RSSフィード

2006 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
基本的に適当な日記です。あまり鵜呑みにしないでください。土日はWeb離れしているので反応遅れます。

2009-12-19

[]NRVO(RVO)とMove Semantics

ムーブセマンティクスについて調べたところで自分が気になったこと。

BigFatMovableObject GetBigFatMovableObject()
{
	BigFatMovableObject mo;
	return mo;
}

int main()
{
    BigFatMovableObject mo(GetBigFatMovableObject());
    return 0;
}

最初、自分はこれが適切なムーブコンストラクタを呼び出す、

と考えたんだけど、(どうみてもコピーよりムーブの方がコストが安い状況なので)

実際にはNRVOといった最適化によって、

「移動もコピーも発生しない状況」にされてしまった訳です。

これは駄目だ!!

と思ってvectorでやったわけですが、

意外とNRVO(RVO)って知られてないこともあるのかと思い書いてみる次第。


NRVOとは、

名前付き戻り値の最適化 (Named Return Value Optimization)

というやつです。

RVOは名前付きでないものです。

BigFatMovableObject GetBigFatMovableObject()
{
	BigFatMovableObject mo;    // 名前付き!
	return mo;
}
BigFatMovableObject GetBigFatMovableObject()
{
	return BigFatMovableObject();    // 名前なし!
}

というような感じです。

こいつをどう最適化するかと言うと、

この最適化が働くと、「無用だと思われるコピーコンストラクタ」をコンパイラが削除します。

(削除しても「いいよ」ということになっています)

要するに「値返し」をするさいに本来であれば

1.関数内のオブジェクトの生成/破棄

2.戻り値のオブジェクトの生成(関数内のオブジェクトからのコピー)/破棄

3.関数を呼び出した側のオブジェクトの生成(戻り値のオブジェクトからのコピー)

といった無駄な手順を幾度も踏まなければなりませんが、

「このコピーって無駄だよね?」って感じでコンパイラが省いてくれるものです。

値返しのコピーコストが高いと思って使ってない人もコンパイラによってはちゃんとこれが働いてくれるので値返しでも構わないケースも多々あります。

(ただし規約で必ずそうしなければならない、というわけではなく、

「してもいいよ」なのでコンパイラ実装依存です)


NRVOが働かない状況だとこうなります。

C++ code- 49 lines - codepad

----- GetBigFatMovableObject -----
BigFatMovableObject::BigFatMovableObject()
Address:0x8051438

関数の中でオブジェクトが生成されました。

BigFatMovableObject(const BigFatMovableObject& rhs)
Address:0x805b1a0
BigFatMovableObject::~BigFatMovableObject()
Address:0x8051438

戻り値に対するコピー開始。

戻り値に関するコピーが終了したので関数内オブジェクト破棄。

BigFatMovableObject(const BigFatMovableObject& rhs)
Address:0x8051438
BigFatMovableObject::~BigFatMovableObject()
Address:0x805b1a0

main関数内のオブジェクトにコピー。

戻り値が破棄される。

----- main end. -----
BigFatMovableObject::~BigFatMovableObject()
Address:0x8051438

main関数内オブジェクト破棄。

で、これはかなり悲惨な状況ですが、

gcc4.4だと

----- GetBigFatMovableObject -----
BigFatMovableObject::BigFatMovableObject()
Address:0x100801000
----- main end. -----
BigFatMovableObject::~BigFatMovableObject()
Address:0x100801000

こうなっちゃうんですよね。

見事にコピーコンストラクタもムーブコンストラクタも省略されちゃいます。


なので、0xにちゃんと対応したようなコンパイラを使う場合、

こういうケースではムーブコンストラクタの出番はないかもしれません。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証