类是否更有效地访问成员变量或局部变量?例如,假设您有一个(回调)方法,其唯一的职责是接收数据,对其执行计算,然后将其传递给其他类。在性能方面,有一个成员变量列表更有意义,该方法在接收数据时填充?或者只是在每次调用回调方法时声明局部变量?
假设这种方法每秒会被调用数百次......
如果我不清楚,这里有一些简单的例子:
// use local variables
class thisClass {
public:
void callback( msg& msg )
{
int varA;
double varB;
std::string varC;
varA = msg.getInt();
varB = msg.getDouble();
varC = msg.getString();
// do a bunch of calculations
}
};
// use member variables
class thisClass {
public:
void callback( msg& msg )
{
m_varA = msg.getInt();
m_varB = msg.getDouble();
m_varC = msg.getString();
// do a bunch of calculations
}
private:
int m_varA;
double m_varB;
std::string m_varC;
};
答案 0 :(得分:45)
执行摘要:在几乎所有场景中,无关紧要,但局部变量略有优势。
警告:您正在进行微观优化。你最终会花费数小时试图理解本应该赢得纳秒的代码。
警告:在你的场景中,性能不应该是问题,而是变量的作用 - 它们是临时的,还是thisClass的状态?
警告:优化的第一,第二和最后一条规则:措施!
首先,看看为x86生成的典型程序集(您的平台可能会有所不同):
// stack variable: load into eax
mov eax, [esp+10]
// member variable: load into eax
mov ecx, [adress of object]
mov eax, [ecx+4]
加载对象的地址后,在寄存器中,指令是相同的。加载对象地址通常可以与之前的指令配对,并且不会达到执行时间。
但这意味着ecx寄存器不可用于其他优化。 然而,现代CPU做了一些强烈的诡计,以减少问题。
此外,当访问许多对象时,这可能会花费额外的费用。 然而,这不到一个周期平均值,并且配对指令通常有更多的机会。
记忆位置:这是筹码赢得大时间的机会。堆栈顶部几乎总是在L1缓存中,因此负载需要一个周期。该对象更有可能被推回到L2缓存(经验法则,10个周期)或主存储器(100个周期)。
然而,您只需为首次访问付费。如果只有一次访问,则10或100个循环是不可察觉的。如果你有成千上万的访问,对象数据也将在L1缓存中。
总之,增益非常小,以至于将成员变量复制到本地以实现更好的性能几乎没有意义。
答案 1 :(得分:6)
我更喜欢局部变量的一般原则,因为它们可以最大限度地减少程序中的恶变状态。至于性能,您的探查器会告诉您需要知道的所有信息。对于整数和其他内置函数,本地应该更快,因为它们可以放在寄存器中。
答案 2 :(得分:4)
这应该是您的编译器问题。相反,优化可维护性:如果信息仅在本地使用,则将其存储在本地(自动)变量中。我讨厌阅读那些散布着成员变量的类,这些变量实际上并没有告诉我关于类本身的任何信息,而只讨论了一堆方法如何协同工作的一些细节:(
事实上,如果局部变量不是更快,我会感到惊讶 - 它们必然会在缓存中,因为它们接近其余的函数数据(调用帧),并且对象指针可能完全在某处别的 - 但我只是在这里猜测。
答案 3 :(得分:4)
愚蠢的问题。
这一切都取决于编译器及其对优化的作用。
即使它确实有效,你获得了什么?如何模糊你的代码?
变量访问通常通过指针和偏移完成。
另外,不要忘记添加将变量移动到本地存储的成本,然后再将结果复制回来。所有这些都可能意义不大,因为编译器可能足够聪明,无论如何都可以优化其中的大部分。
答案 4 :(得分:2)
其他人未明确提及的几点:
您可能在代码中调用赋值运算符。 例如varC = msg.getString();
每次设置功能框架时都会浪费一些周期。您正在创建变量,调用默认构造函数,然后调用赋值运算符以将RHS值引入本地。
将locals声明为const-refs,当然也要初始化它们。
成员变量可能在堆上(如果你的对象是在那里分配的),因此会受到非本地化的影响。
即使节省了几个周期也是好的 - 如果可以避免的话,为什么要浪费计算时间。
答案 5 :(得分:1)
如有疑问,请自行查看基准测试。并确保它首先发挥作用 - 每秒数百次并不是现代处理器的巨大负担。
那就是说,我认为没有任何区别。两者都是指针的常量偏移量,本地人将来自堆栈指针,成员将来自“this”指针。
答案 6 :(得分:1)
在我的意见中,它不应该影响性能,因为:
因此,没有太大区别。
但是,你应该避免复制字符串;)
答案 7 :(得分:1)
与您在算法实现中表示数据的方式相比,您将与之交互的数据量对执行速度的影响更大。
处理器并不真正关心数据是在堆栈上还是在堆上(除了堆栈顶部可能像处理器中提到的那样处理器缓存)但是为了获得最大速度,数据将具有适合处理器的缓存(L1缓存,如果你有多个级别的缓存,几乎所有现代处理器都有)。来自L2缓存的任何负载 - 或者$ DEITY禁止主内存 - 将减慢执行速度。因此,如果您正在处理一个大小为几百KB且每次调用几率的字符串,那么这种差异甚至无法衡量。
请记住,在大多数情况下,程序中10%的加速几乎无法被最终用户检测到(除非您设法将过夜批次的运行时间从25小时减少到不到24小时)所以这不是值得烦恼,除非你确定并且有探查器输出来备份这个特定的代码片段在10%-20%的“热区”内,这对你的程序的运行时有重大影响。
其他考虑因素应该更为重要,例如可维护性或其他外部因素。例如,如果上面的代码是大量多线程代码,那么使用局部变量可以使实现更容易。
答案 8 :(得分:1)
这取决于,但我希望绝对没有区别。
重要的是:使用成员变量作为临时代码会使代码不可重入 - 例如,如果两个线程试图在同一个对象上调用callback(),它将失败。使用静态局部变量(或静态成员变量)更糟糕,因为如果两个线程试图在任何 thisClass对象 - 或后代上调用callback(),则代码将失败。
答案 9 :(得分:0)
使用成员变量应该稍微快一些,因为它们只需要分配一次(构造对象时),而不是每次调用回调时。但与你可能正在做的其他工作相比,我预计这将是一个非常小的百分比。 Benckmark两者都看得更快。
答案 10 :(得分:0)
此外,还有第三种选择:静态本地人。每次调用函数时都不会重新分配这些函数(实际上,它们会在调用之间保留)但它们不会使用过多的成员变量污染类。