Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/59.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 每微秒100000000次计算?_C++_C_Optimization_Compiler Optimization - Fatal编程技术网

C++ 每微秒100000000次计算?

C++ 每微秒100000000次计算?,c++,c,optimization,compiler-optimization,C++,C,Optimization,Compiler Optimization,好的,我一直在和一位朋友谈论编译器和程序优化,他建议n*0.5比n/2快。我说过编译器会自动进行这种优化,所以我写了一个小程序,看看n/2和n*0.5之间是否有区别: 分部: #include <stdio.h> #include <time.h> int main(int argc, const char * argv[]) { int i, m; float n, s; clock_t t; m = 1000000000;

好的,我一直在和一位朋友谈论编译器和程序优化,他建议
n*0.5
n/2
快。我说过编译器会自动进行这种优化,所以我写了一个小程序,看看
n/2
n*0.5
之间是否有区别:

分部:

#include <stdio.h>
#include <time.h>

int main(int argc, const char * argv[]) {
    int i, m;
    float n, s;
    clock_t t;

    m = 1000000000;
    t = clock();
    for(i = 0; i < m; i++) {
        n = i / 2;
    }
    s = (float)(clock() - t) / CLOCKS_PER_SEC;

    printf("n = i / 2: %d calculations took %f seconds (last calculation = %f)\n", m, s, n);

    return 0;
}
为此他得到了

1.869570s
1.868254s
25.674016s
3.497555s
…按顺序

因此,我在我的机器上运行了用
clang++main.cpp-O1编译的程序,得到了与以前类似的结果:
0.000002到0.000011


然而,当我在没有优化的情况下编译程序时,我在他的第一次测试中得到了与他相似的结果。所以我的问题是,任何数量的优化怎么能使程序更快呢?

因为代码在循环的每次迭代中都没有做任何不同的事情,编译器可以自由地将循环中的代码移到外部(结果将完全相同),并完全删除循环,留下几乎0个运行时,正如您所看到的。

for(i=0;ifor (i = 0; i < m; i++)
{
    //  make it a trivial pure float calculation with no int casting to float
    n = 11.1 * 0.53;
    o = n * 0.53;
    p = o * 0.53;
    q = p * 0.53;
    r = q * 0.53;
    s = r * 0.53;
}
{ //使其成为一个简单的纯浮点计算,不需要对浮点进行int转换 n=11.1*0.53; o=n*0.53; p=o*0.53; q=p*0.53; r=q*0.53; s=r*0.53; }

是一个循环,它不引用
i
m
,也不包含循环引用,因此编译器删除循环语句是很简单的

这是一个很好的例子,说明了对高级语言进行基准测试比对汇编语言进行基准测试更困难(这已经很难做到了)。编译器可以,而且经常会干扰您的基准测试

你的朋友有一个观点,除法(实际的除法,不仅仅是用C写
/
)比乘法慢。对于double,延迟的比率大约为4,除法不是流水线的,而乘法是流水线的,因此吞吐量比率更差:大约为20。(这些数字适用于Haswell,但具有代表性)

整数除法比浮点除法慢,但对整数使用浮点乘法会导致两次转换。转换并不太糟糕,所以总的来说,浮点乘法速度更快

但是任何合适的编译器都会将整除常数转换为整数乘法和移位,还可能会进行一些额外的修正(取决于除数和类型)。除以二的幂就更简单了。有关详细信息,请参阅。例如,考虑

int div2(int i)
{
    return i / 2;
}
GCC将此转换为

mov eax, edi
shr eax, 31
add eax, edi
sar eax
ret
这取决于µarch,需要3或4个循环(不包括控制流)

另一方面,

int div2(int i)
{
    return i * 0.5;
}
变成

    cvtsi2sd    xmm0, edi
    mulsd   xmm0, QWORD PTR .LC0[rip]
    cvttsd2si   eax, xmm0
    ret
.LC0:
    .long   0
    .long   1071644672
大约需要4+5+4=13个循环

编译器也可以将
f/2.0
转换为
f*0.5
,即使没有任何“不安全的优化”,除以二的幂等于乘以其逆。这对于不是二的幂的数字来说并不成立

因此,即使有一个至少衡量了一些东西的基准,它也可能无法衡量正确的东西。为了测量浮点除法与乘法的延迟,您可以编写如下代码:

.section data
align 16
one: dq 1.0, 1.0
.section text
_bench1:
  mov ecx, -10000000
  movapd xmm0, [one]
loopone:
  mulpd xmm0, xmm0
  mulpd xmm0, xmm0
  add ecx, 1
  jnz loopone
  ret
_bench2:
  mov ecx, -10000000
  movapd xmm0, [one]
looptwo:
  divpd xmm0, xmm0
  divpd xmm0, xmm0
  add ecx, 1
  jnz looptwo
  ret
rdtsc
包起来,同时调用1000以获取时间。两种方法都用最少的时间。将时间乘以基本时钟和turbo时钟之间的比率。然后你应该(大约)得到两个循环的循环数,除以20000000得到每个
mulpd
divpd
的循环数。时间分割所需的时间取决于被分割的值,因此它不会给出最一般的结果


您可能还对.

感兴趣,因此您的基准测试程序存在一些严重问题。这会破坏你要测试的任何东西。最值得注意的是,您正在混合类型,并且正在进行类型合并,这本身就非常昂贵(可能是您的朋友所指的)。这里也没有编译器在编译时不能计算的东西
i
升级为
double
,multplies by.5并转换回
float
<代码>n=i/2将i除以(可能移位)
2,然后转换为double。您不是在测试乘法与除法,而是不相关的操作。如果您不希望“基准测试”得到优化,请尝试在
s
变量中的每次迭代中累积结果。完成计时计算后,请打印
s
的最终值。我严重怀疑您是否获得了“每微秒100000000次计算!”@Theppdev:在得到答案后不要完全更改问题。提出一个新问题;否则,您将使已经给出的答案无效。为了清楚起见,我认为@wolfPack88基本上是说您的代码计算结果都是“(float)((m-1)>>1)”。您可能应该看看asm。@dwn:不,看看第三个代码块(他计时时有优化和没有优化)。他有一个循环,可以计算1000000次,但计算结果完全相同。编译器足够聪明,可以注意到这一点,并且只计算一次(甚至在编译时),只剩下很少的可执行文件;变量“n”在循环中从未被引用过,所以它可能只是被忽略了。@dwn:我差不多就是这么说的。。。编译器足够聪明,可以识别出没有使用的东西,并且可以进行优化。这就是为什么优化能产生如此大的影响。@woldPack88好吧,听起来我们或多或少都同意
    cvtsi2sd    xmm0, edi
    mulsd   xmm0, QWORD PTR .LC0[rip]
    cvttsd2si   eax, xmm0
    ret
.LC0:
    .long   0
    .long   1071644672
.section data
align 16
one: dq 1.0, 1.0
.section text
_bench1:
  mov ecx, -10000000
  movapd xmm0, [one]
loopone:
  mulpd xmm0, xmm0
  mulpd xmm0, xmm0
  add ecx, 1
  jnz loopone
  ret
_bench2:
  mov ecx, -10000000
  movapd xmm0, [one]
looptwo:
  divpd xmm0, xmm0
  divpd xmm0, xmm0
  add ecx, 1
  jnz looptwo
  ret