c++性能:局部变量vs数据成员
C++ performance: Local variable vs data member
假设代码blow是我的类。它是简化的,不完整的。让我们关注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
的数据成员是否更好?但我也听说局部变量在编译时甚至可能不存在。所以开销也不存在
实际上,这个运算符的速度对我的应用程序的速度没有任何影响。我只是不想知道一个与编译器无关的答案。当这种情况发生时。我应该让它成为数据成员还是局部变量?
在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:EAX中返回64位值,在这种情况下,事情最终将与上面的情况非常相似,只是使用了一个寄存器。
大多数其他处理器开始使用比x86更多的寄存器,因此寄存器压力更低。例如,在SPARC上,一个例程通常在启动时有8个空闲的本地寄存器可供使用,因此在寄存器中分配now
几乎是肯定的。
底线:你不太可能看到显著的速度差异,但如果你确实看到了差异,我猜更可能倾向于使用局部变量而不是成员变量。
这不会有太大(如果有的话)区别。操作系统按页面分配内存(包括堆栈)。因此,堆栈可能不会完成一个页面,因此进程不需要上下文切换来获得另一个页面。
对于编译器中立的回答,速度将归结为上下文切换,处理器上运行的其他事情,....
此外,像你这样的人似乎只关注微观的性能改进,而回避更大的图景。最好先找出瓶颈在哪里,然后集中精力解决这些问题。记住80/20法则。
优化通常取决于编译器。但是假设您使用的是某种不错的编译器,那么就不会有性能损失,所以不用担心。为了证明这一点,我用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()
.
你的代码中有一个性能问题。
每当操作符()每次在堆栈上被调用时,将有两个变量将被创建和销毁(实际上它会发生)。
短期内的性能你不会注意到,因为系统总是为堆栈保留一些内存,并且每次访问相同的内存。
但是在长期运行(性能运行)中,您将能够看到差异。
我不反对任何其他答案,但是让我试着用简单的术语来解释这个问题。我将忽略一些现实生活中很重要的细节,但不要教你所问的概念。
假设你有一个函数有这些变量
int a;
int b;
int c;
int d;
在编译期间,编译器将所有局部变量的大小加起来,当调用函数时,运行时代码为所有变量分配足够的堆栈空间。因此,如果sizeof(int)为4,则上述变量需要16字节的堆栈空间。大多数编译器使用机器寄存器来保存堆栈指针(sp),所以当函数被调用时,运行时代码会做类似
的事情。sp = sp + 16
为我们的4个变量保留空间。注意,如果函数有1个或1000个局部变量,分配局部变量的运行时代码花费的时间是相同的。每个变量没有成本(除非它们有要调用的变量)。如果我们有一个像
这样的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'在寄存器中时,性能应该是相同的
- 用于访问容器<T>数据成员的正确 API
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 数据成员SFINAE的C++17测试:gcc vs clang
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 如何在c++中定义以struct为数据成员的类中的构造函数
- 静态数据成员模板专用化的实例化点在哪里
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 使用指针访问数组中的对象数据成员
- 友元函数无法访问私有数据成员 (c++)
- 我可以在 C++ 中将数据成员/变量从其定义之外添加到结构中吗?
- 为什么将一个结构的引用设置为等于另一个结构只会更改一个数据成员?
- 将私有数据成员添加到野牛生成的类中
- 输入数据成员未按要求工作
- 二维矢量数据成员
- 在类 A 中创建类型为 B 类的向量 - 访问数据 [C++] [成员在两个类中都是私有的]
- 调用在 HXX 文件中声明的静态数据成员
- 是否可以根据其数据成员的类型确定类型的大小
- 访问数据成员(本身是对象)的数据成员,就好像它们是类成员一样
- 使公共数据成员在C++中无法访问
- c++性能:局部变量vs数据成员