难道主流的编译器转换通过按参考基本类型为通通过拷贝? [英] Do mainstream compilers convert passed-by-reference basic types into pass-by-copy?
问题描述
按引用传递的对象是一个地址传递给它一个更简单,更快捷,更安全的方式。
但对于大多数编译器,它都是一样的:引用是真的指针
现在怎么样基本类型,如 INT
?传递一个地址到 INT
,并用它在函数内部会比通过复制传递给它慢,因为指针使用前需要被取消引用。
如何现代编译器手柄,这一点?
INT美孚(const int的&安培; I)
{
COUT<<一世; //做任何只读与我。
}
我可以信任他们编译成这样呢?
INT美孚(const int的我)
{
COUT<<一世;
}
顺便说一句,在某些情况下,甚至可能会更快地同时通过 I
和&放大器;我
,然后用 I
阅读和 * I
写作。
INT美孚(const int的我,为int * ptr_i)
{
COUT<<一世; //没有dereferencement,因此更快的(?)
//更多只读以i操作。
* ptr_i = 123;
}
Visual Studio 2010中(前preSS)不,在简单情况下,我已经至少进行测试。任何人测试GCC?
我测试过以下内容:
1。只有通过 I
:
INT瓦尔[] = {} 1,2,3,12,3,23,1,213,231,1,21,12,213,21321,213,123213,213123;INT OK1(const int的我){
返回sqrtl商(VAR [I]);
}INT OK2(const int的&安培;我){
返回sqrtl商(VAR [I]);
}无效的主要(){
INT I;
给std :: cin>>一世;
// I = OK1(ⅰ);
I = OK2(ⅰ);
性病::法院LT&;<一世;
}
该ASM:
I = OK1(I)
000D1014 MOV ECX,DWORD PTR [I]
000D1017 FILD DWORD PTR瓦尔(0D3018h)[ECX * 4]
000D101E调用_CIsqrt(0D1830h)
000D1023调用_ftol2_sse(0D1840h)I = OK2(ⅰ);
013A1014 MOV ECX,DWORD PTR [I]
013A1017 FILD DWORD PTR瓦尔(13A3018h)[ECX * 4]
013A101E调用_CIsqrt(13A1830h)
013A1023调用_ftol2_sse(13A1840h)
那么,ASM的是相同的,无疑是优化进行。
2。通过 I
和&放大器;我
:
让我们在这里考虑@newacct的雁。
INT瓦尔[] = {} 1,2,3,12,3,23,1,213,231,1,21,12,213,21321,213,123213,213123;INT OK1(const int的我,为int * PI){
* PI = 2;
返回sqrtl商(VAR [I]);
}INT OK2(const int的&安培;我,INT * PI){
* PI = 2;
返回sqrtl商(VAR [I]);
}无效的主要(){
INT I;
INT * PI =安培;我;
给std :: cin>>一世;
I = OK1(I,PI);
// I = OK2(I,PI);
性病::法院LT&;<一世;
}
该ASM:
I = OK1(I,PI);
00891014 MOV ECX,DWORD PTR [I]
00891017 FILD DWORD PTR瓦尔(893018h)[ECX * 4] //访问瓦尔[I]
0089101E调用_CIsqrt(891830h)
00891023电话_ftol2_sse(891840h)I = OK2(I,PI);
011B1014 FILD DWORD PTR [瓦尔+ 8(11B3020h)] //访问瓦尔[2]
011B101A调用_CIsqrt(11B1830h)
011B101F调用_ftol2_sse(11B1840h)
在 OK1
我无法看到它写2到 PI
。也许这了解到,内存位置将被函数的结果,反正被覆盖,所以写的是无用的。
使用 OK2
,编译器是智能屁股如我所料。据了解, I
和 PI
指向同一个地方,所以它采用了硬codeD 2
直接
注:
- 我为两个测试编了两次,一次取消注释
OK1
,一旦取消注释仅OK2
。同时编制都导致了两个函数,这结束了所有内联和混合起来之间更复杂的优化 - 我在阵列中增加了一个查找
瓦尔
因为sqrtl
简单的调用被简化成基本ADD-和没有实际调用MUL样操作 - 在发行版编译
- 产生预期的结果,当然
Passing an object by reference is an easier, faster and safer way to pass an address to it. But for most compilers, it's all the same: references are really pointers.
Now what about basic types like int
? Passing an address to an int
and using it inside a function would be slower than passing it by copy, because the pointer needs to be dereferenced before use.
How do modern compiler handle, this?
int foo(const int & i)
{
cout << i; // Do whatever read-only with i.
}
May I trust them to compile this into this?
int foo(const int i)
{
cout << i;
}
By the way, in some cases it could even be faster to pass both i
and &i
, then use i
for reading, and *i
for writing.
int foo(const int i, int * ptr_i)
{
cout << i; // no dereferencement, therefore faster (?)
// many more read-only operations with i.
*ptr_i = 123;
}
Visual Studio 2010 (Express) does, in the simple cases I've tested at least. Anyone to test gcc?
I've tested the following:
1. Passing only i
:
int vars[] = {1,2,3,12,3,23,1,213,231,1,21,12,213,21321,213,123213,213123};
int ok1(const int i){
return sqrtl(vars[i]);
}
int ok2(const int & i){
return sqrtl(vars[i]);
}
void main() {
int i;
std::cin >> i;
//i = ok1(i);
i = ok2(i);
std::cout << i;
}
The ASM:
i = ok1(i);
000D1014 mov ecx,dword ptr [i]
000D1017 fild dword ptr vars (0D3018h)[ecx*4]
000D101E call _CIsqrt (0D1830h)
000D1023 call _ftol2_sse (0D1840h)
i = ok2(i);
013A1014 mov ecx,dword ptr [i]
013A1017 fild dword ptr vars (13A3018h)[ecx*4]
013A101E call _CIsqrt (13A1830h)
013A1023 call _ftol2_sse (13A1840h)
Well, the ASMs are identical, no doubt the optimization was performed.
2. Passing i
and &i
:
Let's consider @newacct 's anser here.
int vars[] = {1,2,3,12,3,23,1,213,231,1,21,12,213,21321,213,123213,213123};
int ok1(const int i, int * pi) {
*pi = 2;
return sqrtl(vars[i]);
}
int ok2(const int & i, int * pi) {
*pi = 2;
return sqrtl(vars[i]);
}
void main() {
int i;
int * pi = &i;
std::cin >> i;
i = ok1(i, pi);
//i = ok2(i, pi);
std::cout << i;
}
The ASM:
i = ok1(i, pi);
00891014 mov ecx,dword ptr [i]
00891017 fild dword ptr vars (893018h)[ecx*4] // access vars[i]
0089101E call _CIsqrt (891830h)
00891023 call _ftol2_sse (891840h)
i = ok2(i, pi);
011B1014 fild dword ptr [vars+8 (11B3020h)] // access vars[2]
011B101A call _CIsqrt (11B1830h)
011B101F call _ftol2_sse (11B1840h)
In ok1
I can't see it writing 2 into pi
. Probably it understands that the memory location will be overwritten by the result of the function anyway, so the writing is useless.
With ok2
, the compiler is as smart-ass as I expected. It understands that i
and pi
point to the same place, so it uses a hardcoded 2
directly.
Notes:
- I've compiled twice for both test, once uncommenting only
ok1
, once uncommenting onlyok2
. Compiling both at the same time leads to more complex optimizations between the two functions, which end up all inlined and mixed up - I've added a lookup in the array
vars
because simple calls tosqrtl
were simplified into basic ADD- and MUL-like operations without the actual call - Compiled in Release
- Yielded the expected results, of course
这篇关于难道主流的编译器转换通过按参考基本类型为通通过拷贝?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!