不同运营商的执行时间

时间:2015-04-11 03:41:26

标签: c optimization cpu timing cpu-architecture

我正在阅读Knuth的计算机编程艺术,我注意到他指出DIV命令比他的MIX汇编语言中的ADD命令长6倍。

为了测试与现代架构的相关性,我编写了以下代码片段:

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


int main(int argc, char *argv[])
{
    clock_t start;
    unsigned int ia=0,ib=0,ic=0;
    int i;
    float fa=0.0,fb=0.0,fc=0.0;
    int sample_size=100000;

    if (argc > 1)
        sample_size = atoi(argv[1]);

#define TEST(OP) \
    start = clock();\
    for (i = 0; i < sample_size; ++i)\
        ic += (ia++) OP ((ib--)+1);\
    printf("%d,", (int)(clock() - start))
    TEST(+);
    TEST(*);
    TEST(/);
    TEST(%);
    TEST(>>);
    TEST(<<);
    TEST(&);
    TEST(|);
    TEST(^);
#undef TEST

//TEST must be redefined for floating point types
#define TEST(OP) \
    start = clock();\
    for (i = 0; i < sample_size; ++i)\
        fc += (fa+=0.5) OP ((fb-=0.5)+1);\
    printf("%d,", (int)(clock() - start))
    TEST(+);
    TEST(*);
    TEST(/);
#undef TEST

    printf("\n");
    return ic+fc;//to prevent optimization!
}

然后我使用此命令行生成了4000个测试样本(每个样本包含每种类型的100000个操作的样本大小):

for i in {1..4000}; do ./test >> output.csv; done

最后,我用Excel打开了结果并绘制了平均值。我发现的是相当令人惊讶的。以下是结果图: graph of results

实际平均值(从左至右):463.36475,437.38475,806.59725,821.70975,419.56525,417.85725,426.35975,425.9445,423.792,549.91975,544.11825,543.11425

总的来说,这就是我的预期(除法和模数都很慢,浮点结果也是如此)。

我的问题是:为什么整数和浮点乘法的执行速度都比其对应的快?这是一个很小的因素,但它在许多测试中都是一致的。在TAOCP中,Knuth将ADD列为2个单位时间,而MUL则为10.从那以后,CPU体系结构发生了什么变化?

3 个答案:

答案 0 :(得分:2)

不同的指令在同一个CPU上占用不同的时间;并且相同的指令可能在不同的CPU上花费不同的时间。例如,对于英特尔最初的Pentium 4移位相对昂贵且加法速度相当快,因此向寄存器添加寄存器比将寄存器移位1更快;并且对于英特尔最近的CPU转换和添加大致相同的速度(转换速度比原来的奔腾4更快,并且在“周期”方面加速更慢)。

为了使事情更复杂,不同的CPU可能会同时做更多或更少的事情,并且还有其他影响性能的差异。

理论上(并不一定在实践中):

移位和布尔运算(AND,OR,XOR)应该是最快的(每个位可以并行完成)。加法和减法应该是下一个(相对简单,但结果的所有位不能并行完成,因为从一对位进位到下一位)。

乘法应该慢很多,因为它涉及许多添加,但其中一些添加可以并行完成。对于一个简单的例子(使用十进制数字而不是二进制),像12 * 34(有多个数字)的东西可以分解成“单个数字”形式,变成2 * 4 + 2 * 3 * 10 + 1 * 4 * 10 + 1 * 3 * 100;所有“单位”乘法可以并行完成,然后可以并行完成2次加法,然后可以完成最后一次加法。

分部主要是“比较和减去,如果更大,重复”。它是最慢的,因为它不能并行完成(下一次比较需要减法的结果)。模数是除法的余数,与除法基本相同(对于大多数CPU来说,它实际上是相同的指令 - 例如DIV指令给出了商和余数。

浮点;每个数字都有2个部分(有效数和指数),因此事情变得复杂一些。浮点移位实际上是增加或减去指数(并且应该与整数加法/减法大致相同)。对于浮点加法,减法和布尔运算,您需要均衡指数,然后单独对有效位数进行操作(并且“均衡”和“执行操作”不能并行完成)。乘法是乘以有效数并添加指数(并调整偏差),其中两个部分可以并行完成,因此总成本是最慢的(乘以有效数);所以它和整数乘法一样快。除法是划分有效数并减去指数(并调整偏差),其中两个部分可以并行完成,总成本是最慢的(划分有效数);所以它和整数除法一样快。

注意:我已经在各个地方进行了简化,以便更容易理解。

答案 1 :(得分:1)

要测试执行时间,请查看汇编列表中生成的指令,并查看处理器的文档以获取这些指令,并注意FPU是否正在执行操作,或者是否直接在代码中执行。

然后,将每条指令的执行时间加起来。

但是,如果cpu是流水线或多线程的,那么操作可能比计算的时间少很多。

答案 2 :(得分:0)

分割和模(分割操作)确实比加法慢。这背后的原因是ALU(算术逻辑单元)的设计。 ALU是并行加法器和逻辑电路的组合。通过重复减法执行除法,因此需要更多级别的减法逻辑使得除法比加法更慢。分裂中涉及的门的传播延迟增加了蛋糕上的樱桃。