在C中,在函数中使用静态变量会使它更快吗?

时间:2010-10-01 01:01:45

标签: c memory-management global-variables static-variables local-variables

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

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

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

10 个答案:

答案 0 :(得分:24)

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

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

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


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

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

答案 1 :(得分:10)

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

不要进行随机微代码调整,因为你有直觉感觉它会更快。编译器的实现略有不同,在一个环境中的一个编译器上的真实情况在另一个配置上可能是错误的。

要解决有关较少参数的注释:“内联”函数的过程实质上消除了与调用函数相关的开销。机会是一个很小的功能,会被编译器自动排列,但你也可以suggest a function be inlined

在另一种语言C ++中,出现的新标准支持完美转发,并且使用右值引用完美移动语义,这在某些情况下不需要临时值,这可以降低调用函数的成本。

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

答案 2 :(得分:4)

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

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

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

然而,差异是微不足道的,几乎不可能。它是一种常见的误解,即每次调用都必须为“自动”变量分配存储空间。这不是那么C使用堆栈中已经分配的空间来存储这些变量。

静态变量实际上可能会降低你的速度,因为静态变量无法进行一些积极的优化。此外,由于本地人位于堆栈的连续区域,因此更容易高效缓存。

答案 3 :(得分:3)

对此没有一个答案。它会随着CPU,编译器,编译器标志,你拥有的局部变量的数量,CPU在调用函数之前所做的事情以及很可能是月亮的相位而变化。

考虑两个极端;如果你只有一个或几个局部变量,它/它们可能很容易存储在寄存器中而不是分配给存储器位置。如果注册“压力”足够低,则可能在没有执行任何指令的情况下发生这种情况。

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

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

结论:即使是对您的代码进行分析的建议也可能是错误的 - 差异可能很小,即使分析器也无法可靠地检测到它,并且只有方式可以确定是检查所产生的汇编语言(并花费几年时间学习汇编语言,以便在查看它时知道任何事情)。另一方面是,当你处理差异时,你甚至无法衡量它的可靠性,它对实际代码的速度产生重大影响的可能性是如此之大,以至于它可能不值得麻烦。

答案 4 :(得分:2)

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

原因在于,如果编译器必须在整个应用程序中查找可能使用全局的实例,那么严格范围的变量可以使编译器轻松优化,那么它的优化就不会那么好。 / p>

当你引入指针时会更复杂,比如你有以下代码:

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

答案 5 :(得分:1)

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

您提到的大部分内容都称为微优化。一般来说,担心这类事情是bad idea。它使您的代码更难以阅读,并且更难维护。它也极有可能引入错误。在较高级别进行优化时,你可能会获得更大的收益。

作为M2tM的建议,运行分析器也是一个好主意。查看gprof以获取一个非常易于使用的内容。

答案 6 :(得分:1)

您可以随时为应用程序计时,以确定最快的速度。以下是我的理解:(所有这些都取决于处理器的架构,顺便说一句)

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

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

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

答案 7 :(得分:1)

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

分析可能看不出差异,反汇编并知道要查找的内容。

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

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

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