为什么std :: count比MSVC编译器的循环慢,但与GCC相等?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么std :: count比MSVC编译器的循环慢,但与GCC相等?相关的知识,希望对你有一定的参考价值。

我正在测试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(在这种情况下,生成的代码也较短),也更易于向量化。

另一答案

嗯,这似乎是一个迭代器问题。

我进行了扩展测试:

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


int std_count(const std::vector<int>& v, int val) 
    return std::count(v.begin(), v.end(), val);


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

    for (int i = 0; i < v.size(); i++) 
        if (v[i] == val) 
            num++;
        
    

    return num;


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

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

    return num;


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

    for (auto i = v.begin(); i < v.end(); i++) 
        if (*i == val) 
            num++;
        
    

    return num;



int main()

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

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

    int val = 1;

    int num_iters = 1000;

    int total_count = 0;

    for (int a = 0; a < 3; a++) 
        
            auto start = high_resolution_clock::now();
            for (int i = 0; i < num_iters; i++) 
                total_count += std_count(v, val);
            
            auto stop = high_resolution_clock::now();

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

        
            auto start = high_resolution_clock::now();
            for (int i = 0; i < num_iters; i++) 
                total_count += my_count_for(v, val);
            
            auto stop = high_resolution_clock::now();

            auto duration = duration_cast<microseconds>(stop - start);
            std::cout << "my_count_for time:     " << duration.count() << std::endl;
        

        
            auto start = high_resolution_clock::now();
            for (int i = 0; i < num_iters; i++) 
                total_count += my_count_for_in(v, val);
            
            auto stop = high_resolution_clock::now();

            auto duration = duration_cast<microseconds>(stop - start);
            std::cout << "my_count_for_in time:  " << duration.count() << std::endl;
        

        
            auto start = high_resolution_clock::now();
            for (int i = 0; i < num_iters; i++) 
                total_count += my_count_iter(v, val);
            
            auto stop = high_resolution_clock::now();

            auto duration = duration_cast<microseconds>(stop - start);
            std::cout << "my_count_iter time:    " << duration.count() << std::endl;
        

        std::cout << std::endl;
    

    std::cout << total_count << std::endl;
    std::cin >> total_count;

这就是我得到的:

std::count time:       679683
my_count_for time:     235269
my_count_for_in time:  228185
my_count_iter time:    650714

std::count time:       656192
my_count_for time:     231248
my_count_for_in time:  231050
my_count_iter time:    652598

std::count time:       660295
my_count_for time:     238812
my_count_for_in time:  225893
my_count_iter time:    648812

仍然似乎很奇怪,STL函数不是解决任务的最快方法。如果有人知道详细的答案,请与我分享。

以上是关于为什么std :: count比MSVC编译器的循环慢,但与GCC相等?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用system()函数(MSVC)编译c ++代码?

如何使用 system() 函数 (MSVC) 编译 c++ 代码?

C++:在 MSVC 2010 中使用 #if std::is_fundamental<T>::value 进行条件编译

MSVC 2017 支持 std::any 吗?

如何在Windows 7上改进Qt + MSVC编译时间?

MSVC 2015 无法编译 constexpr atan