c++ - 为什么std::count在使用MSVC编译器的循环中要慢得多,而在GCC中却如此?

标签 c++ vectorization

我正在测试C++标准库算法的性能,并且遇到了奇怪的事情。

这是我的代码,用于比较std::count与普通for循环的性能:

#include <algorithm>
#include <vector>
#include <iostream>
#include <chrono>
using namespace std::chrono;


int my_count(const std::vector<int>& v, int val) {
    int num = 0;

    for (int i: v) {
        if (i == val)
            num++;
    }

    return num;
}

int main()
{
    int total_count = 0;

    std::vector<int> v;
    v.resize(100000000);

    // Fill vector
    for (int i = 0; i < v.size(); i++) {
        v[i] = i % 10000;
    }

    int val = 1;

    {
        auto start = high_resolution_clock::now();
        total_count += std::count(v.begin(), v.end(), val);
        auto stop = high_resolution_clock::now();

        std::cout << "std::count time:   " << duration_cast<microseconds>(stop - start).count() << std::endl;
    }

    {
        auto start = high_resolution_clock::now();
        total_count += my_count(v, val);
        auto stop = high_resolution_clock::now();

        std::cout << "my_count   time:   " << duration_cast<microseconds>(stop - start).count() << std::endl;
    }

    // We need this so the compiler does not prune the code above
    std::cout << "Total items: " << total_count << std::endl;
}

使用MinGW,我得到以下信息:
std::count time:   65827
my_count   time:   64861

使用MSVC,我得到一个非常奇怪的结果:
std::count time:   65532
my_count   time:   28584

据我所知,MinGW的结果似乎是合理的,因为据我所知,STL计数函数大致等于普通的for循环,但是MSVC的结果似乎很奇怪-为什么普通的for循环的速度比std::count快2倍以上?

这些结果在我的机器上是可重现的-一次不会发生,但是每次我运行代码时都会发生。我什至尝试更改功能顺序,运行多个for循环以避免缓存或分支预测偏差,但是我仍然得到相同的结果。

有什么理由吗?

最佳答案

这是因为MSVC对您的手动编写的代码进行了矢量化处理,但是对std::count却无法做到。

这是矢量化代码的外观:

        movdqa  xmm5, XMMWORD PTR __xmm@00000001000000010000000100000001
        and     rcx, -8
        xorps   xmm3, xmm3
        xorps   xmm2, xmm2
        npad    3
$LL4@my_count:
        movdqu  xmm1, XMMWORD PTR [rax]
        add     r8, 8
        movdqa  xmm0, xmm5
        paddd   xmm0, xmm3
        pcmpeqd xmm1, xmm4
        pand    xmm0, xmm1
        pandn   xmm1, xmm3
        movdqa  xmm3, xmm0
        movdqa  xmm0, xmm5
        por     xmm3, xmm1
        paddd   xmm0, xmm2
        movdqu  xmm1, XMMWORD PTR [rax+16]
        add     rax, 32                             ; 00000020H
        pcmpeqd xmm1, xmm4
        pand    xmm0, xmm1
        pandn   xmm1, xmm2
        movdqa  xmm2, xmm0
        por     xmm2, xmm1
        cmp     r8, rcx
        jne     SHORT $LL4@my_count

您可以在开始时在xmm5寄存器中看到它是如何加载4个的。此值将用于维护4个单独的计数器,这些计数器跟踪第1个,第2个,第3个和第4个DWORD的结果。一旦计数完成,这四个值将被加在一起形成函数的结果。

MSVC矢量化器的问题似乎在于计数器,数据类型和参数类型应“兼容”:
  • 返回类型的大小应与数据类型
  • 匹配
  • 参数类型的大小应等于或小于数据类型

  • 如果不满足任何这些约束,则不会对代码进行矢量化处理。这就好像您的数据类型是32位宽,您必须在32位计数器上进行操作才能使它们一起工作是有道理的,因此,如果您的返回类型是64位宽,则需要一些其他操作(这就是GCC能够做到,但是与手动编写的循环相比,这仍然会降低std::count的速度)。

    在这种情况下,应该首选手动编写的循环,因为语义上的细微差异(返回int)使矢量化更容易(即使对于生成更短代码的GCC)。

    关于c++ - 为什么std::count在使用MSVC编译器的循环中要慢得多,而在GCC中却如此?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62116277/

    相关文章:

    c++ - Visual c++ Express - 恢复源代码

    c++ - 使用 Serial.Read 输入在 Arduino 上设置液晶光标

    python - 如何计算累积总和直到达到阈值并在达到阈值后重置它考虑Python中的pandas数据帧中的组?

    c++ - 为什么向量化失败?

    python - Python 中包含图像的 H5 文件 : Want to randomly select without replacement

    c++ - 类似于 `declval` 的概念

    c++ - 尝试理解 libstdc++ 对 std::multiset 的实现

    r - 给定向量和 0 沿反对角线排列生成 5x5 矩阵

    c++ - 随机不显示 OpenGL 纹理

    python - 根据每一行的第一个元素返回 NumPy 数组的子集