回答編集履歴

1

アセンブリコードの比較を追加

2016/04/17 15:40

投稿

catsforepaw
catsforepaw

スコア5938

test CHANGED
@@ -5,3 +5,121 @@
5
5
  また、値渡しでは、コンパイルオプションや明示的な呼び出し規約の指定により、値をレジスターで渡すことができるようになるので、メモリアクセスすら発生しない高速な関数呼び出しが可能になります。Visual C++では、x64用ビルド時は、デフォルトでレジスター渡しになっています。
6
6
 
7
7
  ただし、レジスターの数には限りがあるので、引数の数が規定数を上回る場合はスタックが使われます。
8
+
9
+
10
+
11
+ ---
12
+
13
+ 私も実際にどのようなコードになるのか試してみました。
14
+
15
+ x64/Releaseビルドでコンパイルしましたが、デフォルトではグローバルな最適化が有効になっていて別ファイルにしてもインライン展開してしまうため、そのオプションだけ無効化しています。
16
+
17
+ ```C++
18
+
19
+ int testfunc_reference(const int &a, const int &b)
20
+
21
+ {
22
+
23
+ return a + b;
24
+
25
+ }
26
+
27
+
28
+
29
+ int testfunc_value(int a, int b)
30
+
31
+ {
32
+
33
+ return a + b;
34
+
35
+ }
36
+
37
+ ```
38
+
39
+ ```C++
40
+
41
+ // 呼び出し側
42
+
43
+ volatile int X;
44
+
45
+ int main()
46
+
47
+ {
48
+
49
+ X = testfunc_reference(123, 456);
50
+
51
+ //X = testfunc_value(123, 456);
52
+
53
+ return 0;
54
+
55
+ }
56
+
57
+ ```
58
+
59
+ 出力されたアセンブリコード
60
+
61
+ ```asm
62
+
63
+ ; testfunc_reference
64
+
65
+
66
+
67
+ mov eax, DWORD PTR [rcx]
68
+
69
+ add eax, DWORD PTR [rdx]
70
+
71
+ ret 0
72
+
73
+
74
+
75
+ ; testfunc_value
76
+
77
+
78
+
79
+ lea eax, DWORD PTR [rcx+rdx]
80
+
81
+ ret 0
82
+
83
+ ```
84
+
85
+ ```asm
86
+
87
+ ; 呼び出し側
88
+
89
+
90
+
91
+ ; X = testfunc_reference(123, 456);
92
+
93
+
94
+
95
+ lea rdx, QWORD PTR $T1[rsp]
96
+
97
+ mov DWORD PTR $T1[rsp], 456 ; 000001c8H
98
+
99
+ lea rcx, QWORD PTR $T2[rsp]
100
+
101
+ mov DWORD PTR $T2[rsp], 123 ; 0000007bH
102
+
103
+ call ?testfunc_reference@@YAHAEBH0@Z ; testfunc_reference
104
+
105
+ mov DWORD PTR ?X@@3HC, eax ; X
106
+
107
+
108
+
109
+ ; X = testfunc_value(123, 456);
110
+
111
+
112
+
113
+ mov edx, 456 ; 000001c8H
114
+
115
+ mov ecx, 123 ; 0000007bH
116
+
117
+ call ?testfunc_value@@YAHHH@Z ; testfunc_value
118
+
119
+ mov DWORD PTR ?X@@3HC, eax ; X
120
+
121
+ ```
122
+
123
+ 明らかにコード量が違いますね。参照渡しの方は引数にリテラル値を渡した場合、いったんメモリに格納してからそのアドレスを渡しているので、その分余計に値渡しよりも効率が悪くなっています。
124
+
125
+