【问题标题】:How slow (how many cycles) is calculating a square root?计算平方根有多慢(多少个周期)?
【发布时间】:2011-12-05 03:51:59
【问题描述】:

计算平方根有多慢(多少个周期)?这出现在分子动力学课程中,效率很重要,并且采用不必要的平方根对算法的运行时间有显着影响。

【问题讨论】:

  • 与同一程序中的其他操作相比?
  • @Piskvor,这正是我想到的答案,见下文。
  • @Tomalak,你能引用参考吗?
  • @Doug:我可以引用《滑稽讽刺的圣经》;这算不算?
  • 如果只需要知道点 (x1, y1) 是否在半径为 r 的圆 (xc,yc) 内,请不要这样做: if r

标签: performance


【解决方案1】:

基于Agner Fog's instruction tables for Core 2 65nm 在将 SSE 性能与 FSQRT、FDIV、FMUL 和 FADD 进行比较时,它大致相等,但看起来更快,因为它不能进行 80 位数学运算。 SSE 有一个超快的近似倒数和近似倒数 sqrt。

在 Core2 45nm 上,FSQRT 和 FDIV root 变得更快,而 FADD 和 FMUL 没有改变。 SSE 的表现再次大致相同。

英特尔酷睿 2(Merom,65nm)

Instruction Operands Latency Reciprocal
throughput
FSQRT 6 - 69
FADD(P) r 3 1
FMUL(P) r 5 2
FDIV(R)(P) r 6 - 38 d 5 - 37 d
ADDSS/D xmm, xmm 3 1
ADDPS/D xmm, xmm 3 1
MULSS xmm, xmm 4 1
MULSD xmm, xmm 5 1
MULPS xmm, xmm 4 1
MULPD xmm, xmm 5 1
DIVSS xmm, xmm 6 - 18 d 5 - 17 d
DIVSD xmm, xmm 6 - 32 d 5 - 31 d
DIVPS xmm, xmm 6 - 18 d 5 - 17 d
DIVPD xmm, xmm 6 - 32 d 5 - 31 d
SQRTSS/PS xmm, xmm 6 - 29 6 - 29
SQRTSD/PD xmm, xmm 6 - 58 6 - 58
RSQRTSS/PS xmm, xmm 3 2

英特尔酷睿 2(Wolfdale,45nm)

Instruction Operands Latency Reciprocal
throughput
FSQRT 6 - 20
FADD(P) r 3 1
FMUL(P) r 5 2
FDIV(R)(P) r 6 - 21 d 5 - 20 d
ADDSS/D xmm, xmm 3 1
ADDPS/D xmm, xmm 3 1
MULSS xmm, xmm 4 1
MULSD xmm, xmm 5 1
MULPS xmm, xmm 4 1
MULPD xmm, xmm 5 1
DIVSS xmm, xmm 6 - 13 d 5 - 12 d
DIVSD xmm, xmm 6 - 21 d 5 - 20 d
DIVPS xmm, xmm 6 - 13 d 5 - 12 d
DIVPD xmm, xmm 6 - 21 d 5 - 20 d
SQRTSS/PS xmm, xmm 6 - 13 5 - 12
SQRTSD/PD xmm, xmm 6 - 20 5 - 19
RSQRTSS/PS xmm, xmm 3 2

说明表中的数字代表我的测量结果,而不是微处理器供应商发布的官方值。我表中的某些值更高或更低 比其他地方公布的值。

延迟:这是指令在依赖链中生成的延迟。数字是最小值。高速缓存未命中、未对齐和异常可能会显着增加时钟计数。浮点操作数被假定为普通数。非正规数、NAN 和无穷大极大地增加了延迟,除了 XMM 移动、洗牌和布尔指令。浮点上溢、下溢、非正规或 NAN 结果会产生类似的延迟。使用的时间单位是核心时钟周期,而不是时间戳计数器给出的参考时钟周期。

倒数吞吐量:同一线程中一系列同类独立指令的每条指令的平均核心时钟周期数。

d 舍入除数或低精度给出低值。

【讨论】:

  • 是的,但重点是不仅要比较原始时钟周期,还要查看它对性能的实际影响。不过,这是一个很好的答案。
  • @DougTreadwell 好吧,这很糟糕,尤其是由于吞吐量超低,它可以完全杀死循环的性能
【解决方案2】:

平方根比使用-O2 的加法慢大约 4 倍,或者在不使用-O2 的情况下大约慢 13 倍。在网上的其他地方,我发现估计 50-100 个周期可能是正确的,但这并不是一个非常有用的相对成本衡量标准,因此我将下面的代码放在一起进行相对衡量。如果您发现测试代码有任何问题,请告诉我。

以下代码在 Windows 7 操作系统下的 Intel Core i3 上运行,并在 DevC++(使用 GCC)中编译。您的里程可能会有所不同。

#include <cstdlib>
#include <iostream>
#include <cmath>

/*
Output using -O2:

1 billion square roots running time: 14738ms

1 billion additions running time   : 3719ms

Press any key to continue . . .

Output without -O2:

10 million square roots running time: 870ms

10 million additions running time   : 66ms

Press any key to continue . . .

Results:

Square root is about 4 times slower than addition using -O2,
            or about 13 times slower without using -O2
*/

int main(int argc, char *argv[]) {

    const int cycles = 100000;
    const int subcycles = 10000;

    double squares[cycles];

    for ( int i = 0; i < cycles; ++i ) {
        squares[i] = rand();
    }

    std::clock_t start = std::clock();

    for ( int i = 0; i < cycles; ++i ) {
        for ( int j = 0; j < subcycles; ++j ) {
            squares[i] = sqrt(squares[i]);
        }
    }

    double time_ms = ( ( std::clock() - start ) / (double) CLOCKS_PER_SEC ) * 1000;

    std::cout << "1 billion square roots running time: " << time_ms << "ms" << std::endl;

    start = std::clock();

    for ( int i = 0; i < cycles; ++i ) {
        for ( int j = 0; j < subcycles; ++j ) {
            squares[i] = squares[i] + squares[i];
        }
    }

    time_ms = ( ( std::clock() - start ) / (double) CLOCKS_PER_SEC ) * 1000;

    std::cout << "1 billion additions running time   : " << time_ms << "ms" << std::endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}

【讨论】:

  • @ZacharyKraus:如果您查看编辑历史记录,您会发现我的评论适用于问题的原始版本,该版本缺少关于 CPU、编译器、平台等的重要细节。Douglas 是好心随后更新答案,以便它现在包含所有相关细节,这使得答案更加有用。我很乐意删除我的评论,因为它不再与当前版本的答案相关。
  • 对不起,我不明白评论是什么,我也会删除我的,因为我不确定它是否会在帖子中添加任何内容。
  • 您测量的不是 sqrt() 的速度,而是 sqrt() + 循环管理 + 内存访问的速度。单独的循环管理就是一个加法、比较和跳跃。因此,“sqrt 比加法慢 4 倍”的说法是错误的结论。
【解决方案3】:

平方根需要几个周期,但如果它不在缓存中,则访问内存需要几个数量级。因此,试图通过从内存中获取预先计算的结果来避免计算实际上可能会损害性能。

很难在摘要中说您是否会有所收获,因此,如果您想确定,请尝试对这两种方法进行基准测试。

下面是 MSVC 的编译器开发人员 Eric Brummer 的精彩演讲:http://channel9.msdn.com/Events/Build/2013/4-329

【讨论】:

  • 啊,但是从内存中获取预计算结果至少在一种情况下节省了硬件演示。
  • 你能举一个获取预计算结果的例子吗?我不明白你的意思。但我一定会在以后检查该幻灯片。看起来很刺激。
  • 我的意思是通过预先计算来避免动态计算某些东西(如平方根),将结果放在内存中的某个位置(在数组、哈希表等中),然后访问该结果当您在实际计算中需要它时。访问实际上可能比实际的平方根慢得多。
  • @Asik 这真的取决于场景。首先,即使您不预先计算存储它,您也需要从某个地方获取原始值。那里涉及内存访问,但您可以将 sqrt-ed 值存储在(或代替)原始值旁边。如果内存大小是一个问题,您也可以用 sqrt-ed 值替换原始值,因为计算平方比平方根便宜得多。这一切都取决于场景。
  • @ZacharyKraus 请记住,读取不在缓存中的内容,因此必须从内存中获取,可能会慢 50 或 100 倍。例如,一次缓存读取可能需要 2 到 3 个周期,这基本上是免费的,但从内存中读取最多可能需要 100 或 200 个周期。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-02
  • 1970-01-01
  • 2022-07-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-12
相关资源
最近更新 更多