22

我的函数将被调用数千次。如果我想让它更快,将局部函数变量更改为静态有什么用吗?我背后的逻辑是,因为静态变量在函数调用之间是持久的,它们只在第一次分配,因此,每个后续调用都不会为它们分配内存并且会变得更快,因为内存分配步骤没有完成。

另外,如果上述情况属实,那么每次调用函数时使用全局变量而不是参数会更快地将信息传递给函数吗?我认为在每个函数调用上也为参数分配空间,以允许递归(这就是递归占用更多内存的原因),但由于我的函数不是递归的,如果我的推理是正确的,那么理论上取消参数会使它更快。

我知道我想做的这些事情是可怕的编程习惯,但是请告诉我这是否明智。无论如何我都会尝试,但请给我你的意见。

4

10 回答 10

25

局部变量的开销为零。每次调用函数时,您已经在为参数、返回值等设置堆栈。添加局部变量意味着您向堆栈指针添加了一个稍大的数字(一个在编译时计算的数字) .

此外,由于缓存局部性,局部变量可能更快。

如果您只调用函数“数千次”(而不是数百万或数十亿次),那么您应该在运行分析器后查看您的算法以获得优化机会。


回复:缓存局部性(在此处阅读更多内容):经常访问的全局变量可能具有时间局部性。它们也可以在函数执行期间被复制到寄存器,但在函数返回后会被写回内存(缓存)(否则它们将无法被其他任何东西访问;寄存器没有地址)。

局部变量通常具有时间和空间局部性(它们通过在堆栈上创建来获得)。此外,它们可能被直接“分配”到寄存器,而永远不会被写入内存。

于 2010-10-01T01:12:23.573 回答
12

找出答案的最佳方法是实际运行分析器。这可以像使用这两种方法执行几个定时测试然后平均结果并进行比较一样简单,或者您可以考虑一个成熟的分析工具,它将自身附加到进程并绘制内存使用随时间和执行速度的变化。

不要执行随机的微代码调整,因为你直觉它会更快。编译器对事物的实现都略有不同,在一个环境中的一个编译器上正确的东西在另一种配置上可能是错误的。

为了解决关于更少参数的评论:“内联”函数的过程基本上消除了与调用函数相关的开销。编译器可能会自动内联一个小函数,但您也可以建议内联一个函数

在另一种语言 C++ 中,即将推出的新标准支持完美转发和具有右值引用的完美移动语义,这在某些情况下消除了对临时变量的需求,从而可以降低调用函数的成本。

我怀疑您过早地进行了优化,但是,在您发现真正的瓶颈之前,您不应该如此关心性能。

于 2010-10-01T01:06:34.990 回答
4

绝对不是!唯一的“性能”差异是变量初始化的时间

    int anint = 42;
 vs
    static int anint = 42;

在第一种情况下,每次调用函数时整数将设置为 42,在第二种情况下,加载程序时将设置为 42。

然而,差异是如此微不足道,以至于几乎无法注意到。这是一个常见的误解,即必须在每次调用时为“自动”变量分配存储空间。并非如此,C 将堆栈中已分配的空间用于这些变量。

静态变量实际上可能会减慢您的速度,因为它无法对静态变量进行一些积极的优化。此外,由于本地人位于堆栈的连续区域中,因此它们更容易有效地缓存。

于 2010-10-01T01:08:37.623 回答
3

对此没有一个答案。它会随着 CPU、编译器、编译器标志、您拥有的局部变量的数量、调用函数之前 CPU 一直在做什么以及很可能是月相而有所不同。

考虑两个极端;如果您只有一个或几个局部变量,它/它们可能很容易存储在寄存器中,而不是被分配内存位置。如果寄存器“压力”足够低以至于根本不执行任何指令就可能发生这种情况。

在相反的极端,有一些机器(例如,IBM 大型机)根本没有堆栈。在这种情况下,我们通常认为的堆栈帧实际上被分配为堆上的链表。正如您可能猜到的那样,这可能会慢。

在访问变量时,情况有点相似——可以很好地保证访问机器寄存器比分配在内存中的任何东西都快。OTOH,访问堆栈上的变量可能会非常慢——它通常需要索引间接访问之类的东西,这(尤其是对于较旧的 CPU)往往相当慢。OTOH,访问全局(静态是,即使它的名称不是全局可见的)通常需要形成一个绝对地址,一些 CPU 也会在一定程度上对其进行惩罚。

底线:即使是分析代码的建议也可能放错地方——差异可能很容易很小,以至于即使分析器也无法可靠地检测到它,唯一确定的方法是检查生成的汇编语言(和花几年时间学习汇编语言,足以知道当你看到它时会说什么。另一方面是,当您处理甚至无法可靠测量的差异时,它对实际代码速度产生重大影响的可能性非常小,因此可能不值得麻烦。

于 2010-10-01T01:50:05.560 回答
2

使用静态变量可能会使函数更快一点。但是,如果您想让您的程序多线程,这将导致问题。由于静态变量在函数调用之间共享,因此在不同线程中同时调用函数将导致未定义的行为。多线程是您将来可能想要做的事情,以真正加速您的代码。

您提到的大多数事情都被称为微优化。一般来说,担心这些事情是个坏主意。它使您的代码更难阅读,也更难维护。它也极有可能引入错误。在更高级别进行优化可能会物有所值。

正如 M2tM 所建议的,运行分析器也是一个好主意。查看gprof一个非常容易使用的。

于 2010-10-01T01:10:05.687 回答
2

看起来静态与非静态已经完全涵盖,但关于全局变量的主题。通常这些会减慢程序的执行速度而不是加快它的速度。

原因是紧密范围的变量使编译器可以轻松地进行大量优化,如果编译器必须在整个应用程序中查看可能使用全局变量的实例,那么它的优化就不会那么好。

当您引入指针时,这会变得更加复杂,假设您有以下代码:

int myFunction()
{
    SomeStruct *A, *B;
    FillOutSomeStruct(B);
    memcpy(A, B, sizeof(A);
    return A.result;
}

编译器知道指针 A 和 B 永远不会重叠,因此它可以优化副本。如果 A 和 B 是全局的,那么它们可能指向重叠或相同的内存,这意味着编译器必须“安全行事”,这会更慢。该问题通常称为“指针别名”,并且可能发生在很多情况下,而不仅仅是内存副本。

http://en.wikipedia.org/wiki/Pointer_alias

于 2010-10-01T01:52:20.797 回答
1

您始终可以为您的应用程序计时,以真正确定什么是最快的。这是我的理解:(所有这些都取决于您的处理器的体系结构,顺便说一句)

C 函数创建一个堆栈帧,其中放置传递的参数,放置局部变量,以及返回指向调用者调用函数的位置的指针。这里没有内存管理分配。它通常是一个简单的指针移动,仅此而已。从堆栈访问数据也非常快。当你处理指针时,惩罚通常会发挥作用。

至于全局变量或静态变量,它们是相同的……从它们将被分配到同一内存区域的角度来看。访问这些可能使用与局部变量不同的访问方法,具体取决于编译器。

您的方案之间的主要区别是内存占用,而不是速度。

于 2010-10-01T01:10:58.150 回答
1

使用静态变量实际上会使您的代码明显变慢。静态变量必须存在于内存的“数据”区域中。为了使用该变量,函数必须执行加载指令以从主存储器读取,或执行存储指令以写入它。如果该区域不在缓存中,则会丢失许多周期。位于堆栈上的局部变量肯定会在缓存中具有地址,甚至可能在 cpu 寄存器中,根本不会出现在内存中。

于 2010-10-01T01:46:56.050 回答
0

我同意其他关于分析以找出类似内容的评论,但一般来说,函数静态变量应该更慢。如果你想要它们,你真正追求的是一个全球性的。函数静态插入代码/数据以检查是否已经初始化了每次调用函数时都会运行的事物。

于 2010-10-01T01:37:31.233 回答
0

剖析可能看不到差异,拆卸并知道要寻找什么可能。

我怀疑您只会在每个循环中获得多达几个时钟周期的变化(平均取决于编译器等)。有时变化会显着改善或显着变慢,这不一定是因为变量 home 已移入/移出堆栈。假设您在 2ghz 处理器上为 10000 次调用节省了每个函数调用的四个时钟周期。非常粗略的计算:节省了 20 微秒。与您当前的执行时间相比,20 微秒是多还是少?

通过将所有 char 和 short 变量转换为 int 等,您可能会获得更多的性能改进。了解微优化是一件好事,但需要花费大量时间进行实验、反汇编、计时代码的执行,例如,了解更少的指令并不一定意味着更快。

拿你的特定程序,反汇编有问题的函数和调用它的代码。有无静电。如果你只获得一两条指令,而这是你要做的唯一优化,那可能不值得。您可能无法在分析时看到差异。例如,在代码更改之前,缓存行命中位置的更改可能会显示在分析中。

于 2010-10-01T05:03:20.737 回答