C++ 模运算符 Vs。移位运算符,哪个更快,为什么?

Use*_*ser 3 c++ time bit-shift

我正在编写代码来查找素数,在我的工作中,我很好奇 C++ 中的 % 操作究竟是如何在低级别工作的。

首先,我编写了一些代码来比较 '%' 运算符和 '>>' 运算符的经过时间。

#include <iostream>
#include <chrono>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

using namespace std;

bool remainder1(int x);
bool remainder2(int y);
void timeCompare(bool(*f)(int), bool(*g)(int));

// I want to check which one is faster, x % 2 Vs. (x >> 1) & 1
int main()
{
    srand(time(NULL));

    for (int i = 0; i < 10; i++) {
        timeCompare(remainder1, remainder2);
    }

    return 0;
}

// % 2 operation
bool remainder1(int x) {

    if (x % 128) return true;
    else return false;
}

bool remainder2(int x) {

    if ((x >> 7) & 1) return true;
    else return false;
}

void timeCompare(bool(*f)(int), bool(*g)(int)) {

    srand(time(NULL));

    auto start = chrono::steady_clock::now();

    for (int i = 0; i < 10000000; i++) {
        int x = rand();
        f(x);
    }

    auto end = chrono::steady_clock::now();

    cout << "Elapsed time in nanoseconds : "
         << chrono::duration_cast<chrono::nanoseconds>(end - start).count()
         << " ns";


    auto start2 = chrono::steady_clock::now();

    for (int i = 0; i < 10000000; i++) {
        int x = rand();
        g(x);
    }

    auto end2 = chrono::steady_clock::now();


    cout << " Vs. "
         << chrono::duration_cast<chrono::nanoseconds>(end2 - start2).count()
         << " ns" << endl;


}
Run Code Online (Sandbox Code Playgroud)

输出是这样的:

Elapsed time in nanoseconds : 166158000 ns Vs. 218736000 ns
Elapsed time in nanoseconds : 151776000 ns Vs. 214823000 ns
Elapsed time in nanoseconds : 162193000 ns Vs. 217248000 ns
Elapsed time in nanoseconds : 151338000 ns Vs. 211793000 ns
Elapsed time in nanoseconds : 150346000 ns Vs. 211295000 ns
Elapsed time in nanoseconds : 155799000 ns Vs. 215265000 ns
Elapsed time in nanoseconds : 148801000 ns Vs. 212839000 ns
Elapsed time in nanoseconds : 149813000 ns Vs. 226175000 ns
Elapsed time in nanoseconds : 152324000 ns Vs. 213338000 ns
Elapsed time in nanoseconds : 149353000 ns Vs. 216809000 ns 
Run Code Online (Sandbox Code Playgroud)

所以看起来移位操作在寻找余数时速度较慢。我猜原因是 shift 版本需要比 '%' 版本多一次比较......我说得对吗?

我真的很想知道 '%' 在较低级别是如何工作的!

Rei*_*ica 5

我真的很想知道 '%' 在较低级别是如何工作的!

如果你问它是如何实现的,那么答案是,机会是 您使用的CPU有一个单一的模指令%)。例如,拿这个 C++ 代码:

int main()
{
    int x = 100;

    int mod = x % 128;
    int shift = x >> 7;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

为它生成的x86汇编代码(Clang 6.0.0)是:

main:
    push    rbp
    mov     rbp, rsp
    xor     eax, eax
    mov     ecx, 128
    mov     dword ptr [rbp - 4], 0
    mov     dword ptr [rbp - 8], 100
    mov     edx, dword ptr [rbp - 8]  # Start of modulo boilerplater
    mov     dword ptr [rbp - 20], eax 
    mov     eax, edx
    cdq
    idiv    ecx                       # Modulo CPU instruction
    mov     dword ptr [rbp - 12], edx # End of modulo sequence
    mov     ecx, dword ptr [rbp - 8]  # Start of shift boilerplate
    sar     ecx, 7                    # Shift CPU instruction
    mov     dword ptr [rbp - 16], ecx # End of shift sequence
    mov     ecx, dword ptr [rbp - 20]
    mov     eax, ecx
    pop     rbp
    ret
Run Code Online (Sandbox Code Playgroud)

idiv指令称为Signed Divide,它将商放在 EAX/RAX 中,将余数放在 EDX/RDX 中(相应地为 x86/x64)。

我猜原因是 shift 版本需要比 '%' 版本多一次比较......我的说法正确吗?

在这种情况下没有进行比较,因为它是一条指令。