我认为下面的图片解释得非常多,但差异基本上是每次调用分配的静态变量,或每次调用时创建的正常变量(?)。
感谢您提供的任何见解。
编辑:我已经添加了一个快速程序。我不知道它是多么无意义,但它们在同一时间执行。我觉得这不是一个好的考试。我想这不值得担心。#include <iostream>
#include <time.h>
using namespace std;
#define ONE_BILLION 1000000000
#define HUNDRED_MILLION 100000000
long long functionStatic(int arg)
{
static long long randNum;
srand(time(NULL));
randNum = rand() % 20;
return arg + randNum;
}
long long functionLocal(int arg)
{
long long randNum;
srand(time(NULL));
randNum = rand() % 20;
return arg + randNum;
}
int main()
{
long long timeStart;
long long duration;
long long randNum;
long long accum = 0;
timeStart = clock();
for (int i = 0; i < HUNDRED_MILLION; i++)
{
srand(time(NULL));
randNum = rand() % 50;
accum += functionStatic(randNum);
}
cout << "Time for static variable function: " << clock() - timeStart << " milliseconds" << endl << endl;
timeStart = clock();
for (int i = 0; i < HUNDRED_MILLION; i++)
{
srand(time(NULL));
randNum = rand() % 50;
accum += functionLocal(randNum);
}
cout << "Time for local variable function: " << clock() - timeStart << " milliseconds" << endl;
return 0;
}
答案 0 :(得分:3)
让我们看看两种情况都发生了什么。
案例1: - x
作为本地变量。
在这种情况下,编译器可以进行优化,即可以将其放在寄存器或缓存中。因此,如果函数被多次调用,那么这个函数将比int a
变量略微优势。
案例2:使其成为static
在这种情况下,一旦变量初始化,它将在程序的静态数据区中分配一个存储,并在那里直到程序退出。 编译器也拒绝对此进行任何优化,因此每次调用该函数时{J}都将从主内存中获取然后修改然后再次写入主内存,这将转换为更多装配说明的数量比第一种情况。
最终裁决: - 第一种情况可能比第二种情况更好。但可能并非总是如此,因为有更多因素会影响性能,例如编译器,CPU,其他人提到的底层硬件平台或操作系统。
答案 1 :(得分:3)
还有一点尚未真正涵盖,我认为这与你的问题有关。在您的示例中,您碰巧使用了int。因为你做过(可能适用于任何普通类型),静态初始化可以以特别有效的方式完成。但是,在函数中创建静态变量可能非常昂贵:
struct A {
A(double y) : z(y) { }
double z;
};
A func(double x) {
static A a(0);
a.z += x;
return a;
}
编译为(使用-O2):
func(double): # @func(double)
push rax
mov al, byte ptr [rip + guard variable for func(double)::a]
test al, al
jne .LBB0_3
mov edi, guard variable for func(double)::a
movsd qword ptr [rsp], xmm0 # 8-byte Spill
call __cxa_guard_acquire
movsd xmm0, qword ptr [rsp] # 8-byte Reload
test eax, eax
je .LBB0_3
mov qword ptr [rip + func(double)::a], 0
mov edi, guard variable for func(double)::a
call __cxa_guard_release
movsd xmm0, qword ptr [rsp] # 8-byte Reload
.LBB0_3:
addsd xmm0, qword ptr [rip + func(double)::a]
movsd qword ptr [rip + func(double)::a], xmm0
pop rax
ret
发生了什么?好吧,像a一样的静态局部必须初始化一次,第一次调用func。但是你怎么能确保这发生呢?您必须创建一个不可见的布尔变量,将其初始化为false,在初始化之前检查它,仅初始化为false,然后将其设置为true。并以线程安全的方式完成所有这些工作。在前面的例子中,我们有一个带有常量初始化值的普通类型(int)。所以编译器只是在编译时执行初始化,然后生成省略初始化的代码。
虽然你的例子是一个整数,但你的问题的文字是通用的。因此,我觉得正确答案应该是这样的:一般来说,如果你关心表现,尽量避开静态本地人。
答案 2 :(得分:2)
对于使用-O2构建的g ++ 4.9.2,使用静态会有轻微的损失,因为它不会优化存储。
// static int a;
// a = 2;
// a += Someclass::number + Someclass::number2;
mov 0x0(%rip),%eax # Get Someclass::number
add 0x0(%rip),%eax # Add Someclass::number2
add $0x2,%eax # Add 2
mov %eax,0x0(%rip) # Store result in a
retq # return
a
不是静态的版本少了一条指令:
// int a = 2;
// a += Someclass::number + Someclass::number2;
mov 0x0(%rip),%eax # Get Someclass::number
add 0x0(%rip),%eax # Add Someclass::number2
add $0x2,%eax # Add 2
retq # Return
然而,对于clang 3.6.0,结果是相同的:
// static int a;
// a = 2;
// a += Someclass::number + Someclass::number2;
mov 0x0(%rip),%eax
mov 0x0(%rip),%ecx
lea 0x2(%rax,%rcx,1),%eax
retq
// int a = 2;
// a += Someclass::number + Someclass::number2;
mov 0x0(%rip),%eax
mov 0x0(%rip),%ecx
lea 0x2(%rax,%rcx,1),%eax
retq
答案 3 :(得分:1)
除了&#34之外,不可能有真正的答案;它取决于&#34;。
您的问题确实属于&#34;过早优化&#34;篮。事实上你还没有提到像&#34;测试&#34;等概念。或&#34;剖析&#34; - 而是担心独立的特定代码结构的性能 - 支持表征。
使用静态意味着在第一次调用函数之前的某个时间(一次性)创建该静态,并且每次都重新分配。在运行时,这意味着访问和/或修改内存中该位置的最新值。这可能意味着,取决于调用函数的频率和CPU的工作方式,访问处理器缓存,返回RAM(比缓存慢),或从交换空间检索该位置的数据(比内存慢得多)交换硬盘上的空间)。因此,性能取决于主机系统的内存架构,并且可能有些变化。
使用自动变量通常意味着分配和初始化堆栈空间的某些区域。它的性能再次取决于主机系统的内存架构,但不太可能像静态一样可变。
编译器还可以完全优化自动变量的使用,将其置于机器寄存器或其他技巧中以增强代码的性能。当涉及静态变量时,编译器使用这种优化移动的空间较小。
然而,最后,差异可能微不足道 - 即使在频繁调用的代码中也是如此。您经常调用的函数中的其他内容 - 算法等 - 可能会对性能产生更大的影响,而不是担心变量是静态的还是自动的。我建议首先编写函数的可读性和可维护性。这意味着,在实践中,可能不会使变量成为静态 - 因为这意味着有更多定义的数据路径可以进入和退出函数,因此更难理解。然后在实际用例中测试代码的性能。如果发现某些性能问题,请通过分析器运行它,并找出真正的瓶颈 - 如果有的话 - 。更有可能的是,代码中的变量a
甚至不会涉及任何瓶颈。或者,如果是,则是低阶效应 - 其他因素将占主导地位。因为,在现实世界中,程序性能的驱动因素通常是代码的不同部分之间的交互,而不是可能受到一个变量的属性的微小调整的影响。