C ++哪个函数更快?重复分配或重复创建

时间:2015-10-04 05:08:11

标签: c++ performance function variables static

我认为下面的图片解释得非常多,但差异基本上是每次调用分配的静态变量,或每次调用时创建的正常变量(?)。

Which function is faster?

感谢您提供的任何见解。

编辑:我已经添加了一个快速程序。我不知道它是多么无意义,但它们在同一时间执行。我觉得这不是一个好的考试。我想这不值得担心。

#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;
}

4 个答案:

答案 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甚至不会涉及任何瓶颈。或者,如果是,则是低阶效应 - 其他因素将占主导地位。因为,在现实世界中,程序性能的驱动因素通常是代码的不同部分之间的交互,而不是可能受到一个变量的属性的微小调整的影响。