引用通常会被优化掉吗?
Are references usually optimized away?
考虑这段代码示例:
// Static variables
void Object::f_v1()
{
static const int& foo = dataObjectConstRef.dataField.foo;
static const int& bar = dataObjectConstRef.dataField.bar;
static const int& baz = dataObjectConstRef.dataField.baz;
return foo * foo + baz * bar + baz / bar + baz;
}
// References
void Object::f_v2()
{
const int& foo = dataObjectConstRef.dataField.foo;
const int& bar = dataObjectConstRef.dataField.bar;
const int& baz = dataObjectConstRef.dataField.baz;
return foo * foo + baz * bar + baz / bar + baz;
}
// Everything written out
void Object::f_v3()
{
return dataObjectConstRef.dataField.foo * dataObjectConstRef.dataField.foo +
dataObjectConstRef.dataField.baz * dataObjectConstRef.dataField.bar +
dataObjectConstRef.dataField.baz / dataObjectConstRef.dataField.bar +
dataObjectConstRef.dataField.baz;
}
如果启用了编译器优化,其中哪一项性能最佳?
- 静态版本只构建一次引用,但总是
之后检查互斥锁。
- 普通参考版
应该只计算一次地址,但是快捷方式是
在此版本或上一个版本中完全优化
版本?
- 如果不是,什么更快,每次检查互斥锁
或生成引用?
我正在使用 gcc 7.1 和 -O3。
Are references usually optimized away?
如果可以的话,是的,它们通常会被优化掉。
f_v1
具有与其他行为不同的行为,除非 dataObjectConstRef
本身引用静态对象。由于在从多个线程调用该函数时需要同步,它也有一些性能损失。根据经验:线程同步往往比指针/引用的分配慢。但是衡量它是否重要。
假设这些字段实际上具有类型 int
(因此不涉及转换)f_v2
和 f_v3
具有相同的行为。编译器应该很容易证明这一点,我希望优化编译器为两者生成完全相同的代码。
编译一个最小的例子:
struct DataObject
{
struct DataField
{
double foo, bar, baz;
} dataField;
};
struct Object
{
// Everything written out
double f_v3()
{
return dataObjectConstRef.dataField.foo * dataObjectConstRef.dataField.foo +
dataObjectConstRef.dataField.baz * dataObjectConstRef.dataField.bar +
dataObjectConstRef.dataField.baz / dataObjectConstRef.dataField.bar +
dataObjectConstRef.dataField.baz;
}
DataObject const& dataObjectConstRef;
};
extern const DataObject const& getData();
int main()
{
Object o { getData() };
return o.f_v3();
}
使用 clang 3.9,-O3:
main: # @main
push rax
call getData()
vmovsd xmm0, qword ptr [rax] # xmm0 = mem[0],zero
vmovsd xmm1, qword ptr [rax + 8] # xmm1 = mem[0],zero
vmulsd xmm0, xmm0, xmm0
vmovsd xmm2, qword ptr [rax + 16] # xmm2 = mem[0],zero
vmulsd xmm3, xmm2, xmm1
vaddsd xmm0, xmm0, xmm3
vdivsd xmm1, xmm2, xmm1
vaddsd xmm0, xmm1, xmm0
vaddsd xmm0, xmm2, xmm0
vcvttsd2si eax, xmm0
pop rcx
ret
仅加载 3 次 (vmovsd
)。
所以是的,在这种情况下,引用的解除引用已经被优化掉了。
C++ 标准允许编译器执行此操作,因为优化后的代码会产生相同的结果就好像原始源代码已逐字执行一样。
考虑这段代码示例:
// Static variables
void Object::f_v1()
{
static const int& foo = dataObjectConstRef.dataField.foo;
static const int& bar = dataObjectConstRef.dataField.bar;
static const int& baz = dataObjectConstRef.dataField.baz;
return foo * foo + baz * bar + baz / bar + baz;
}
// References
void Object::f_v2()
{
const int& foo = dataObjectConstRef.dataField.foo;
const int& bar = dataObjectConstRef.dataField.bar;
const int& baz = dataObjectConstRef.dataField.baz;
return foo * foo + baz * bar + baz / bar + baz;
}
// Everything written out
void Object::f_v3()
{
return dataObjectConstRef.dataField.foo * dataObjectConstRef.dataField.foo +
dataObjectConstRef.dataField.baz * dataObjectConstRef.dataField.bar +
dataObjectConstRef.dataField.baz / dataObjectConstRef.dataField.bar +
dataObjectConstRef.dataField.baz;
}
如果启用了编译器优化,其中哪一项性能最佳?
- 静态版本只构建一次引用,但总是 之后检查互斥锁。
- 普通参考版
应该只计算一次地址,但是快捷方式是
在此版本或上一个版本中完全优化
版本?
- 如果不是,什么更快,每次检查互斥锁 或生成引用?
我正在使用 gcc 7.1 和 -O3。
Are references usually optimized away?
如果可以的话,是的,它们通常会被优化掉。
f_v1
具有与其他行为不同的行为,除非 dataObjectConstRef
本身引用静态对象。由于在从多个线程调用该函数时需要同步,它也有一些性能损失。根据经验:线程同步往往比指针/引用的分配慢。但是衡量它是否重要。
假设这些字段实际上具有类型 int
(因此不涉及转换)f_v2
和 f_v3
具有相同的行为。编译器应该很容易证明这一点,我希望优化编译器为两者生成完全相同的代码。
编译一个最小的例子:
struct DataObject
{
struct DataField
{
double foo, bar, baz;
} dataField;
};
struct Object
{
// Everything written out
double f_v3()
{
return dataObjectConstRef.dataField.foo * dataObjectConstRef.dataField.foo +
dataObjectConstRef.dataField.baz * dataObjectConstRef.dataField.bar +
dataObjectConstRef.dataField.baz / dataObjectConstRef.dataField.bar +
dataObjectConstRef.dataField.baz;
}
DataObject const& dataObjectConstRef;
};
extern const DataObject const& getData();
int main()
{
Object o { getData() };
return o.f_v3();
}
使用 clang 3.9,-O3:
main: # @main
push rax
call getData()
vmovsd xmm0, qword ptr [rax] # xmm0 = mem[0],zero
vmovsd xmm1, qword ptr [rax + 8] # xmm1 = mem[0],zero
vmulsd xmm0, xmm0, xmm0
vmovsd xmm2, qword ptr [rax + 16] # xmm2 = mem[0],zero
vmulsd xmm3, xmm2, xmm1
vaddsd xmm0, xmm0, xmm3
vdivsd xmm1, xmm2, xmm1
vaddsd xmm0, xmm1, xmm0
vaddsd xmm0, xmm2, xmm0
vcvttsd2si eax, xmm0
pop rcx
ret
仅加载 3 次 (vmovsd
)。
所以是的,在这种情况下,引用的解除引用已经被优化掉了。
C++ 标准允许编译器执行此操作,因为优化后的代码会产生相同的结果就好像原始源代码已逐字执行一样。