有一个类SomeClass包含一些操作此数据的数据和方法.它必须用一些参数创建,如:
SomeClass(int some_val, float another_val);
Run Code Online (Sandbox Code Playgroud)
比方说Manager,还有另一个类,它包含SomeClass并大量使用其方法.
那么,在性能(数据位置,缓存命中等)方面会更好,声明SomeClass作为成员的对象Manager并在Manager构造函数中使用成员初始化或将SomeClass的对象声明为unique_ptr?
class Manager
{
public:
Manager() : some(5, 3.0f) {}
private:
SomeClass some;
};
Run Code Online (Sandbox Code Playgroud)
要么
class Manager
{
public:
Manager();
private:
std::unique_ptr<SomeClass> some;
}
Run Code Online (Sandbox Code Playgroud)
stg*_*lov 10
最有可能的是,访问子对象的运行时效率没有差异.但由于多种原因,使用指针可能会变慢(请参阅下面的详细信息).
此外,还有其他一些你应该记住的事情:
说到编译时,指针优于普通成员.使用普通成员,您不能删除Manager声明对SomeClass声明的依赖性.使用指针,您可以使用前向声明来完成.较少的依赖性可能导致较少的构建时间.
我想提供有关子对象访问性能的更多详细信息.我认为使用指针可能比使用普通成员慢,原因如下:
Manager和SomeClass一起访问数据,并且保证普通成员接近其他数据,而堆分配可能使对象和子对象彼此远离.这是最后一个问题的示例(完整代码在这里):
struct IntValue {
int x;
IntValue(int x) : x(x) {}
};
class MyClass_Ptr {
unique_ptr<IntValue> a, b, c;
public:
void Compute() {
a->x += b->x + c->x;
b->x += a->x + c->x;
c->x += a->x + b->x;
}
};
Run Code Online (Sandbox Code Playgroud)
显然,这是愚蠢的店子对象a,b,c的指针.我已经测量了在Compute单个对象的十亿次方法调用中花费的时间.以下是具有不同配置的结果:
2.3 sec: plain member (MinGW 5.1.0)
2.0 sec: plain member (MSVC 2013)
4.3 sec: unique_ptr (MinGW 5.1.0)
9.3 sec: unique_ptr (MSVC 2013)
Run Code Online (Sandbox Code Playgroud)
在每种情况下查看生成的最内层循环组件时,很容易理解为什么时间如此不同:
;;; plain member (GCC)
lea edx, [rcx+rax] ; well-optimized code: only additions on registers
add r8d, edx ; all 6 additions present (no CSE optimization)
lea edx, [r8+rax] ; ('lea' instruction is also addition BTW)
add ecx, edx
lea edx, [r8+rcx]
add eax, edx
sub r9d, 1
jne .L3
;;; plain member (MSVC)
add ecx, r8d ; well-optimized code: only additions on registers
add edx, ecx ; 5 additions instead of 6 due to a common subexpression eliminated
add ecx, edx
add r8d, edx
add r8d, ecx
dec r9
jne SHORT $LL6@main
;;; unique_ptr (GCC)
add eax, DWORD PTR [rcx] ; slow code: a lot of memory accesses
add eax, DWORD PTR [rdx] ; each addition loads value from memory
mov DWORD PTR [rdx], eax ; each sum is stored to memory
add eax, DWORD PTR [r8] ; compiler is afraid that some values may be at same address
add eax, DWORD PTR [rcx]
mov DWORD PTR [rcx], eax
add eax, DWORD PTR [rdx]
add eax, DWORD PTR [r8]
sub r9d, 1
mov DWORD PTR [r8], eax
jne .L4
;;; unique_ptr (MSVC)
mov r9, QWORD PTR [rbx] ; awful code: 15 loads, 3 stores
mov rcx, QWORD PTR [rbx+8] ; compiler thinks that values may share
mov rdx, QWORD PTR [rbx+16] ; same address with pointers to values!
mov r8d, DWORD PTR [rcx]
add r8d, DWORD PTR [rdx]
add DWORD PTR [r9], r8d
mov r8, QWORD PTR [rbx+8]
mov rcx, QWORD PTR [rbx] ; load value of 'a' pointer from memory
mov rax, QWORD PTR [rbx+16]
mov edx, DWORD PTR [rcx] ; load value of 'a->x' from memory
add edx, DWORD PTR [rax] ; add the 'c->x' value
add DWORD PTR [r8], edx ; add sum 'a->x + c->x' to 'b->x'
mov r9, QWORD PTR [rbx+16]
mov rax, QWORD PTR [rbx] ; load value of 'a' pointer again =)
mov rdx, QWORD PTR [rbx+8]
mov r8d, DWORD PTR [rax]
add r8d, DWORD PTR [rdx]
add DWORD PTR [r9], r8d
dec rsi
jne SHORT $LL3@main
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2308 次 |
| 最近记录: |