问题的意图是efficeincey使用局部变量的一个很好的理由而不是成员,我不是试图在这里测试编译器优化...请
如果您认为我的比较并不完美,请提供替代代码。
任何人都可以详细解释原因吗? 为什么本地访问速度更快,即使每次调用函数时都必须对堆栈进行crteate并拆除堆栈 成员只需要解除这个指针 我可以看到本地堆栈更快?
RESULT 当地时间:271 成员时间:418
代码:
class local {
public:
void incr() {
int i;
++i;
}
};
class Member {
int i;
public:
void incr() {
++i;
}
};
#include <ctime>
#include <iostream>
#include <time.h>
int main(int argc, char**argv) {
time_t star;
time_t end;
Member m;
local l;
time(&star);
for(unsigned int j=0;j<200000;++j)
for(unsigned int i=0;i<400000;++i) {
l.incr();
}
time(&end);
std::cout << "\nlocal time:" << end-star << "\n";
time(&star);
for(unsigned int j=0;j<200000;++j)
for(unsigned int i=0;i<400000;++i) {
m.incr();
}
time(&end);
std::cout << "\nmember time:" << end-star<< "\n";
return 0;
}
新代码: g ++ -O1 localmember.cpp
当地时间:128 成员时间:117
代码:
class local {
public:
int diff(int a, int b) {
int d=0;
d=a-b;
return d;
}
};
class Member {
int d;
public:
int diff(int a, int b) {
d=0;
d=a-b;
return d;
}
};
static int gr;
#include <ctime>
#include <iostream>
#include <time.h>
void dumpdiff(int r) {
gr=r ;
}
int main(int argc, char**argv) {
time_t star;
time_t end;
Member m;
local l;
int r=0;
int r2=0;
time(&star);
int in1=2,in2=0;
for(unsigned int j=0;j<200000;++j)
for(unsigned int i=0;i<200000;++i) {
r = l.diff(in1*i,in2*i);
in1+=1;
in2+=1;
if(r){r2=r;}
dumpdiff(r);
}
time(&end);
std::cout << "\nlocal time:" << end-star << "\n";
time(&star);
for(unsigned int j=0;j<200000;++j)
for(unsigned int i=0;i<200000;++i) {
r = m.diff(in1,in2);
in1+=1;
in2+=1;
if(r){r2=r;}
dumpdiff(r);
}
time(&end);
std::cout << "\nmember time:" << end-star<< "\n";
return 0;
答案 0 :(得分:3)
这是因为您的编译器可以看到您从未在i
中阅读local::incr
。如果你不读它,就没有必要增加它,所以编译器可以优化与local
有关的任何东西。而 nothing 当然比做任何事都快。
但是,我怀疑你是否使用完全优化编译,否则编译器会看到与Member
相关的东西也没有做任何事情,然后你会得到0和0两次,因为好的优化器可以看到足够优化甚至环路,因为它们没有副作用。
答案 1 :(得分:3)
变量是否是本地vs成员对于性能不如缓存位置和寄存器位移那么重要。
鉴于您对“不测试优化”的评论,我怀疑您的“问题”是“如何测试证明一个人是否比另一个更快?”
答案是:你必须查看程序集(例如gcc -o test.S -S test.cpp)。使用-O1或更高版本时,GCC完全取消了对Local.incr()函数的调用,这显然使测试无效。
但是:如果您(大概是使用-O0编译的话)那么您正在加载测试以支持局部变量,因为使用-O0会引起成员操作的成本 - 调用访问成员变量的成员函数会更加昂贵。 / p>
我拿了你的例子并将其更改为:
void incr() {
int i;
++i;
}
class local {
public:
void incr() {
int i;
++i;
}
};
class member {
int m_i;
public:
void incr() {
++m_i;
}
};
int main(int argc, const char** argv)
{
local l;
member m;
for(unsigned int j = 0; j < 200000; ++j) {
for(unsigned int i = 0; i < 400000; ++i) {
incr();
}
}
for(unsigned int j = 0; j < 200000; ++j) {
for(unsigned int i = 0; i < 400000; ++i) {
l.incr();
}
}
for(unsigned int j = 0; j < 200000; ++j) {
for(unsigned int i = 0; i < 400000; ++i) {
m.incr();
}
}
return 0;
}
使用“g ++ -std = c ++ 11 -Wall -O0 -g -o test.S -S test.cpp”,“incr”的实现是
_Z4incrv: .LFB0: .file 1 "test.cpp" .loc 1 1 0 .cfi_startproc pushq %rbp .LCFI1: .cfi_def_cfa_register 6 .LBB2: .loc 1 3 0 addl $1, -4(%rbp) .LBE2: .loc 1 4 0 popq %rbp .LCFI2: .cfi_def_cfa 7, 8 ret .cfi_endproc
虽然local :: incr是
_ZN5local4incrEv: .LFB1: .loc 1 8 0 .cfi_startproc pushq %rbp .LCFI3: .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .LCFI4: .cfi_def_cfa_register 6 movq %rdi, -24(%rbp) .LBB3: .loc 1 10 0 addl $1, -4(%rbp) .LBE3: .loc 1 11 0 popq %rbp .LCFI5: .cfi_def_cfa 7, 8 ret .cfi_endproc
因为它必须接收“this”指针。但是它没有访问任何成员变量,因此它实际上不必以任何方式使用this指针。
_ZN6member4incrEv: .LFB2: .loc 1 17 0 .cfi_startproc pushq %rbp .LCFI6: .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .LCFI7: .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) .loc 1 18 0 movq -8(%rbp), %rax movl (%rax), %eax leal 1(%rax), %edx movq -8(%rbp), %rax movl %edx, (%rax) .loc 1 19 0 popq %rbp .LCFI8: .cfi_def_cfa 7, 8 ret .cfi_endproc
在这种使用-O0的调试版本中,成员访问总是会更昂贵。如果我向Member添加“m_j”并在Member :: incr()中增加它,编译器继续生成:
.loc 1 20 0 movq -8(%rbp), %rax movl 4(%rax), %eax leal 1(%rax), %edx movq -8(%rbp), %rax movl %edx, 4(%rax)
所以是的 - 在一个未经优化的构建中,在大多数情况下,对于琐碎的情况,成员变量比局部变量更昂贵。
“大多数情景”?如果类型不是简单类型,使用昂贵的构造函数等,那么每次进入函数时都必须运行构造函数,而不必运行一次。考虑:
void simulated_work() { std::this_thread::sleep_for(std::chrono::milliseconds<5000>; }
struct DatabaseInteger {
int m_i;
public:
DatabaseInteger() {
simulated_work();
}
inline DatabaseInteger& operator++() { ++m_i; }
operator int() { return m_i; }
};
class local {
public:
void incr() {
DatabaseInteger i; // does simulated_work every time.
++i;
}
};
“本地”变量随时都会比成员变量更有效,即:if:
答案 2 :(得分:1)
我的水晶球告诉我,编译器正在优化局部变量代码,而它无法证明以后没有访问成员变量,并且实际上已完成增量。
答案 3 :(得分:0)
我喜欢收件箱里的@Tony D答案 “必须在每次调用函数时创建堆栈和拆除堆栈”适用于两者(实际上,任何非内联函数),并且在调用函数时单个堆栈指针调整可能包含局部变量的空间 - 之后访问堆栈-pointer-relative数据不应该比object-this this-pointer相对慢。如果你想获得一种感觉,请查看优化的asm。 - Tony D