假设我有一个单线程程序中的函数,它看起来像这样:
void f(some arguments){
char buffer[32];
some operations on buffer;
}
“f”似乎在一些经常调用的循环内部,因此我希望将其尽可能快速。在我看来,每次调用“f”时都需要分配缓冲区,但是如果我将其声明为静态变量,则不会发生这种情况。这个推理正确吗?这是免费的加速吗?仅仅因为这个事实(这是一个简单的加速),优化编译器是否已经为我做了类似的事情?”
假设我有一个单线程程序中的函数,它看起来像这样:
void f(some arguments){
char buffer[32];
some operations on buffer;
}
“f”似乎在一些经常调用的循环内部,因此我希望将其尽可能快速。在我看来,每次调用“f”时都需要分配缓冲区,但是如果我将其声明为静态变量,则不会发生这种情况。这个推理正确吗?这是免费的加速吗?仅仅因为这个事实(这是一个简单的加速),优化编译器是否已经为我做了类似的事情?”
不,这并不是免费的加速。
首先,开始时分配几乎是免费的(因为它仅包括将32添加到堆栈指针中),其次,有至少两个原因可能使静态变量更慢。
因此,这并不是免费的加速。但可能在您的情况下更快(虽然我怀疑)。 因此,请尝试运行、测试并查看在您特定的场景中哪种方法最有效。
在几乎所有系统上,增加栈上的32个字节几乎不会带来任何代价。但你应该测试一下。对静态版本和本地版本进行基准测试,并发布结果。
对于使用堆栈存储局部变量的实现,通常分配涉及推进寄存器(将值添加到其中),例如堆栈指针(SP)寄存器。这个时间非常短暂,通常只有一条指令或更少。
然而,初始化堆栈变量需要更长的时间,但也不多。请查看您的汇编语言清单(由编译器或调试器生成)以获取确切的细节。标准中没有关于初始化变量所需持续时间或指令数的规定。
静态局部变量的分配通常是不同的。一种常见的方法是将这些变量放置在与全局变量相同的区域中。通常,在调用main()
之前,该区域中的所有变量都会被初始化。在这种情况下,分配是将地址赋给寄存器或将区域信息存储在内存中的问题。这里没有浪费太多执行时间。
动态分配是执行周期被消耗的情况。但这不在您的问题范围之内。
我建议更一般的方法是,如果您有一个需要一些本地变量的函数被多次调用,则考虑将其包装在类中,并使这些变量成为成员函数。如果需要使大小动态化,则可以使用std::vector<char> buffer(requiredSize)
代替char buffer[32]
。但这比每次循环都初始化数组要昂贵。
class BufferMunger {
public:
BufferMunger() {};
void DoFunction(args);
private:
char buffer[32];
};
BufferMunger m;
for (int i=0; i<1000; i++) {
m.DoFunction(arg[i]); // only one allocation of buffer
}
将缓冲区设置为静态的另一个含义是,该函数在多线程应用程序中不安全,因为两个线程可能同时调用它并覆盖缓冲区中的数据。另一方面,在需要使用缓冲区的每个线程中使用单独的BufferMunger
是安全的。
现在的写法,分配没有成本:32个字节在栈上。唯一需要做的工作是零初始化。
在这里使用本地静态变量不是一个好主意。它不会更快,而且你的函数不能再从多个线程中使用,因为所有调用都共享同一个缓冲区。更不用说本地静态变量的初始化不能保证是线程安全的了。
static
变量(与C不同)在首次使用时初始化。这意味着您将引入额外的运行时检查成本。分支可能会使性能变得更糟,而不是更好。(但实际上,应该进行性能分析,正如其他人所提到的那样。)使用gcc编译器,我确实看到了一些加速:
void f() {
char buffer[4096];
}
int main() {
int i;
for (i = 0; i < 100000000; ++i) {
f();
}
}
还有时间:
$ time ./a.out
real 0m0.453s
user 0m0.450s
sys 0m0.010s
将缓冲区更改为静态:
$ time ./a.out
real 0m0.352s
user 0m0.360s
sys 0m0.000s
f
的调用很可能会被省略。 - Konrad Rudolph
char x; char foo[8192]; int y;
创建了一个8KB的变量,它可能被分配在栈上的x
和y
之间,将x
和y
强制分别进入不同的缓存行,从而使得读取x
不会把y
"免费" 加载到缓存中。使用static char foo[8192];
将foo
放入 BSS 中,消除了间隙,并且(可能但不总是)将x
和y
放入同一 CPU 缓存行中。 - Jody Bruchon