繁体   English   中英

为什么除法比C ++中的移位慢?

[英]Why is dividing slower than bitshifting in C++?

我编写了两段代码,一段将随机数除以二,另一段将同一随机数右移一次。 据我了解,这应该产生相同的结果。 但是,当我同时处理这两段代码时,我始终会得到数据,说明移位速度更快。 这是为什么?

移位代码:

double iterations = atoi(argv[1]) * 1000;
int result = 0;
cout << "Doing " << iterations << " iterations." << endl;
srand(31459);
for(int i=0;i<iterations;i++){
    if(i % 2 == 0){
        result = result + (rand()>>1);
    }else{
        result = result - (rand()>>1);
    }
}

划分代码:

double iterations = atoi(argv[1]) * 1000;
int result = 0;
cout << "Doing " << iterations << " iterations." << endl;
srand(31459);
for(int i=0;i<iterations;i++){
    if(i % 2 == 0){
        result = result + (rand() / 2);
    }else{
        result = result - (rand() / 2);
    }
}

时间和结果:

$ time ./divide 1000000; time ./shift 1000000
Doing 1e+09 iterations.

real    0m12.291s
user    0m12.260s
sys     0m0.021s
Doing 1e+09 iterations.

real    0m12.091s
user    0m12.056s
sys     0m0.019s

$ time ./shift 1000000; time ./divide 1000000
Doing 1e+09 iterations.

real    0m12.083s
user    0m12.028s
sys     0m0.035s
Doing 1e+09 iterations.

real    0m12.198s
user    0m12.158s
sys     0m0.028s

附加信息:

  • 编译时我没有使用任何优化
  • 我在Fedora 20的虚拟安装内核上运行此代码:3.12.10-300.fc20.x86_64

不是; 在您正在运行的架构上速度较慢。 它几乎总是比较慢,因为移位后的硬件微不足道,而除法则是一场噩梦。 在基础10中,对您来说更简单的是78358582354 >> 3或78358582354/85? 不管输入如何,指令通常花费相同的时间执行,在您这种情况下,将/2转换为>>1编译器的工作。 CPU只是按照提示执行。

它实际上并不慢。 我已经使用nonius运行您的基准测试, 如下所示:

#define NONIUS_RUNNER
#include "Nonius.h++"

#include <type_traits>
#include <random>
#include <vector>

NONIUS_BENCHMARK("Divide", [](nonius::chronometer meter)
{
    std::random_device rd;
    std::uniform_int_distribution<int> dist(0, 9);

    std::vector<int> storage(meter.runs());
    meter.measure([&](int i) { storage[i] = storage[i] % 2 == 0 ? storage[i] - (dist(rd) >> 1) : storage[i] + (dist(rd) >> 1); });
})

NONIUS_BENCHMARK("std::string destruction", [](nonius::chronometer meter)
{
    std::random_device rd;
    std::uniform_int_distribution<int> dist(0, 9);

    std::vector<int> storage(meter.runs());
    meter.measure([&](int i) { storage[i] = storage[i] % 2 == 0 ? storage[i] - (dist(rd) / 2) : storage[i] + (dist(rd) / 2); });
})

这些是结果: 在此处输入图片说明

如您所见,它们都是并驾齐驱的。

(您可以在此处找到html输出)

PS:看来我忘了重命名第二项考试。 我的错。

结果的差异似乎正在波及结果之间,因此您无法真正分辨出结果是否不同。 但一般来说,单次操作无法完成除法,而移位可以,因此通常移位速度应该更快。

但是,因为您的代码中有文字2,所以我猜想即使没有优化,编译器也会生成相同的代码。

请注意, rand返回int并将int (默认情况下为有符号)除以2与按1移位是不同的。您可以轻松地检查生成的asm并查看差异,也可以简单地检查生成的二进制大小:

> g++ -O3 boo.cpp -c -o boo # divide
> g++ -O3 foo.cpp -c -o foo # shift
> ls -la foo boo
... 4016 ... boo # divide
... 3984 ... foo # shift

现在添加static_cast补丁:

if (i % 2 == 0) {
  result = result + (static_cast<unsigned>(rand())/2);
}
else {
  result = result - (static_cast<unsigned>(rand())/2);
}

并再次检查大小:

> g++ -O3 boo.cpp -c -o boo # divide
> g++ -O3 foo.cpp -c -o foo # shift
> ls -la foo boo
... 3984 ... boo # divide
... 3984 ... foo # shift

确保您可以验证两个二进制文件中生成的asm相同

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM