假设代码打击是我的班级。这是简化而不完整。让我们关注operator()
的实现。
class Delta{
public:
long long operator()() {
auto now = steady_clock::now();
auto delta = (now - last).count();
last = now;
return delta;
}
private:
steady_clock::time_point last;
};
operator()
每秒可能被调用数千次。我只是想知道经常分配和释放变量now
和delta
可能会损害operator()
的性能。如果我想最大限度地提高速度,最好是now
和delta
class Delta
数据成员?但我也听说编译后局部变量甚至可能不存在。所以不知何故,开销也不存在。
答案 0 :(得分:2)
在x86-64上,我希望这段代码最终会在RAX中分配now
和delta
。在汇编语言中,代码看起来像这个顺序:
assume RSI:ptr _Delta
call steady_clock::now()
sub rax, [rsi].last
mov [rsi].last, rax
ret
当然,在实际的汇编语言中,你会看到steady_clock::now()
的错位名称(例如),但你会得到一般的想法。在进入任何非静态成员函数时,它将在某个寄存器中具有this
。返回值始终为rax
。我没有看到编译器需要(甚至想要)为任何其他变量分配空间的任何特别好的理由。
在32位x86上,最终使用一些堆栈空间的可能性要高得多,尽管它可能会在EDX中返回64位值:EAX,在这种情况下,事情最终会非常相似如上所述,只需再使用一个寄存器。
大多数其他处理器的开始时寄存器数多于x86,因此寄存器压力较低。例如,在SPARC上,例程通常以8个本地寄存器开始并且可以使用,因此在寄存器中分配now
几乎是确定的。
底线:你不太可能看到显着的速度差异,但如果你确实看到了差异,我猜它更倾向于使用局部变量而不是成员变量。
答案 1 :(得分:1)
它不会产生太多(如果有的话)差异。 OS根据页面分配内存(包括堆栈)。因此,堆栈可能无法完成页面,因此该过程不需要上下文切换来获取另一页面。
至于编译器中立的答案,速度将归结为上下文切换,处理器上运行的其他事情,....
除了像你这样的人似乎专注于微观性能改进,但避免更大的图景。最好首先找出瓶颈的第一个位置并专注于那些。请记住80/20规则。
答案 2 :(得分:1)
优化通常取决于编译器。但假设你使用的是一些不错的编译器,就不会有性能损失,所以不用担心。为了证明这一点,我用gcc 4.7编译了你的代码,优化级别3:
call 400770 <std::chrono::system_clock::now()@plt> ;; Call.
mov rdx,rax ;; Remembe temporary value in %rdx.
sub rax,QWORD PTR [rbx] ;; Divide
mov QWORD PTR [rbx],rdx ;; Wrie Back.
根据具体情况,可能会进一步优化。或者它可能会变得更糟。只是为了举例说明何时可以在堆栈上创建临时变量 - 在now
和last
之间放置了大量代码,并且寄存器分配算法不能将所有变量放在寄存器中,它将诉诸使用堆栈。因此,对于实际结果,您必须检查生成的机器代码。但坦率地说,除了一个显而易见的事情之外,这里没有太多要优化的地方。如果您关心性能 ,那么您需要担心的是通过PLT进行的大量通话。换句话说 - 请勿使用std::chrono::system_clock::now()
。
答案 3 :(得分:0)
粗略的是您的代码存在性能问题。
每当在堆栈上调用 operator()时,将会创建并销毁两个变量(实际上它会发生)。
短期内您不会注意到性能,因为系统将始终为Stack保留一些内存,并且每次都要访问相同的内存。
但从长远来看(性能运行),您将能够看到差异。
答案 4 :(得分:0)
我不同意任何其他答案,但让我试着用简单的术语解释这个没有机器代码。我将忽略一些重要的现实生活细节,但不要教导你所提出的概念。
假设您有这些变量的函数
int a;
int b;
int c;
int d;
在编译期间,编译器会将所有局部变量的大小相加,并且在调用函数时,运行时代码会为所有变量分配足够的堆栈空间。因此,如果sizeof(int)为4,则上述变量需要16个字节的堆栈空间。大多数编译器使用机器寄存器来保存堆栈指针(sp),因此当调用我们的函数时,运行时代码将执行类似
的操作。sp = sp + 16
为我们的4个变量预留空间。请注意,如果函数具有1或1000个局部变量,则分配局部变量的运行时代码需要相同的时间。每个变量没有成本(除非他们有ctors才能调用)。如果我们有一个C语句,如
d = b;
伪机器代码看起来像
*(sp + 12)= *(sp + 4)
其中12是堆栈上变量d的偏移量,4是b的偏移量。 (偏移量不会这么简单,堆栈上还有其他东西。)
使用
等成员变量定义结构/类时class X {
int a;
int b;
int c;
int d;
void foo() { d = b; }
};
编译器还会将所有变量的大小和每个变量的偏移量相加。 但现在foo()中的代码变为
*(this+12) = *(this + 4)
虽然sp几乎总是保存在机器寄存器中,但“this”指针很可能只在机器寄存器中。现代编译器会查看最常用的变量,并将这些变量存储在寄存器中。由于'this'通常被引用很多(通常是隐式的),因此它通常被分配给寄存器。当'this'在寄存器中时,性能应该相同。