类访问成员变量或局部变量更有效吗?例如,假设您有一个(回调)方法,其唯一的职责是接收数据,对其执行计算,然后将其传递给其他类.在性能方面,有一个成员变量列表更有意义,该方法在接收数据时填充?或者只是在每次调用回调方法时声明局部变量?
假设这种方法每秒会被调用数百次......
如果我不清楚,这里有一些简单的例子:
// 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; };
peterchen.. 47
执行摘要:在几乎所有场景中,无关紧要,但局部变量略有优势.
警告:您正在进行微观优化.你最终会花费数小时试图理解本应该赢得纳秒的代码.
警告:在您的场景中,性能不应该是问题,而是变量的作用 - 它们是临时的,还是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缓存中.
总之,增益是如此之小,以至于将成员变量复制到本地以实现更好的性能几乎没有意义.
执行摘要:在几乎所有场景中,无关紧要,但局部变量略有优势.
警告:您正在进行微观优化.你最终会花费数小时试图理解本应该赢得纳秒的代码.
警告:在您的场景中,性能不应该是问题,而是变量的作用 - 它们是临时的,还是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缓存中.
总之,增益是如此之小,以至于将成员变量复制到本地以实现更好的性能几乎没有意义.
我更喜欢局部变量的一般原则,因为它们可以最大限度地减少程序中的恶变状态.至于性能,您的探查器会告诉您需要知道的所有信息.对于整数和其他内置函数,本地应该更快,因为它们可以放在寄存器中.