2016-12-09 11 views
1

最近、関数から返される値がconst変数に割り当てられ、場合によってはconst&に割り当てられる場合がある、古いコードの一部を分析しています。不思議なことに、私はその違いを見るために解体に切り替えました。上記のコードの以下の解体が無効な最適化とVS2015で取得した戻り値const&const代入 - 逆アセンブリ

struct Data 
{ 
    int chunk[1024]; 
}; 

Data getData() 
{ 
    return Data(); 
} 

int main() 
{ 
    const std::string varInit{ "abc" }; // 1 
    const std::string varVal = "abc"; // 2 
    const std::string& varRef = "abc"; // 3 

    const Data dataVal = getData(); // 4 
    const Data& dataRef = getData(); // 5 

    return 0; 
} 

:しかし、ポイントを取得する前に私が参照するためにいくつかのコードを持っている簡単な例を描いてみましょう。

Disassembly for <code>(1)``(2)``(3)</code> 私は、ASMの専門家だんが、一見私は(1)(2)ために行わ同様の操作があることを言うと思います。それにもかかわらず、(3)には、可変値割り当て中にconst&が使用されなかった以前のバージョンと比較して、2つの追加操作(leaおよびmov)が含まれています。

データが値によって関数から返された場合も同様です。 (5)は、(4)に関してさらに2つの演算を実行します。 Disassembly for <code>(4)``(5)</code>

質問は非常に狭いです:

  • これらの追加の操作はから来て、ここに彼らの目的は何であるかはどこ?一般的にここにはありません:What's the purpose of the LEA instructionしかし、提示された文脈で。
  • これは、少なくとも下位のデータサイズが無視できるオブジェクトの場合、パフォーマンスに影響を及ぼしますか? (例ではData構造体とは対照的に)
  • これは最適化がオンになっているときに影響を与えますか?ちなみに

を(リリースのためのビルド)、私はすでに多少関連しているが、質問の一部ではないことができる値を割り当てる際のconstとconstの&を使用しての長所と短所についてWhy not always assign return values to const reference?を読みました。感覚と同じバイナリコードで - 場合

+1

のようなコードを書く必要があります。 'lea'命令はそのポインタ(' ecx = ebp - 84h')の初期値を計算し、 'mov'命令はその値を' varRef'ポインタに保存します。 – AnT

答えて

4

(3)コンパイラは[ebp-84]で「隠れた」ローカルVAR「のstd :: string」を作成するには、X& v_84それに名前を付け、この

const std::string _84 = "abc"; 
const std::string& varRef = _84;// lea + move (by sense varRef = &_84) 

のようなコードを実行してみましょうX* vからvが実際に両方の場合にXへのポインタで、単に異なる構文が

同じケース(5)

const Data _20a0 = getData(); 
const Data& dataRef = _20a0; // by sense dataRef = &_20a0, but by syntax dataRef = _20a0 
を使用しました

か、この行が正確に2のasm命令を取ることを表示するには、代わりにライン

const Data& dataRef = getData(); 

ライト線

const Data& dataRef = dataVal; 

場合は言う:

lea eax,[dataVal] 
mov [dataRef],eax 

コード(4,5)とData getData()署名は絶対悪夢で、言葉はありません

'値によって' 戻り構造体の詳細については明瞭

- 機能のみを返す結果(alaxeaxおよびx64でrax)又は2レジスタとして登録することができる - edx:eax(8バイト、ハイでEDX)またはrdx:rax (16バイト×64)

の場合 Data getData() - 不可能な返信Dataです。どうやって ?!?

ので、本当にあなたの関数は、コンパイラを行うどのように多くの無意味なのmemcpy

//const Data dataVal = getData(); 
Data _41A0, _3198, dataVal; 
memcpy(&_3198, getData(&_41A0), sizeof(Data)); 
memcpy(&dataVal, &_3198, sizeof(Data)); 

//const Data& dataRef = getData(); 
Data _41A0, _3198, _20a0, &dataRef; 
memcpy(&_51a8, getData(&_61b0), sizeof(Data)); 
memcpy(&_20a0, &_51a8, sizeof(Data)); 
dataRef = &_20a0;// very small influence compare all other 

トライCALCに

Data* getData(Data* p) 
{ 
    Data x; 
    memcpy(p, &x, sizeof(Data)); 
    return p; 
} 

とコードに変換されますか?

はあなたのコンパイラは通常の「変装ポインタ」として `varRef`の参照実装この

void InitData(Data* p); 

Data dataVal; 
InitData(&dataVal); 
+0

最適化をオンにして、それが有利なときにコンパイラにその変換をさせることができるときは、 'void InitData(Data * p);'のようなことを行う理由はまったくありません。ヘック、私はg ++に-O1以上のもので 'getData'への呼び出しをインラインにしないように説得することができませんでした。そして、RVOが蹴ってきて、その場所にオブジェクトを構築しました。 –

+0

@MilesBudnek - はい、現代のコンパイラは、最適化されたソースコードを真剣に最適化することはできません。最良の選択肢 - 私たちが何をしているのか理解し、何を理解しているのかを理解し、より良いコードを書くことを始めます。関数から2つ以上のレジスタサイズを返すことができません。実際にデータへのポインタを渡す必要があります。機能はこのデータを埋めてください – RbMm

+1

最高のコードは読みやすいコードです。 'Data dataVal = getData();'はプログラマーの意図をはっきりと表し、コンパイラーはそれに対して同じオブジェクトコードを生成するか、 'InitData(&dataVal);'を生成します。-O0 g ++でさえ、両方の式にほぼ同じコードを生成します。私はフードの下で何が起こっているのか知ることは良いことであることに同意しますが、コンパイラが本当にうまく機能していることを知ることも重要です。 –

関連する問題