Visual Studio 使用VS自动并行化

xzlaal3s  于 2023-04-07  发布在  其他
关注(0)|答案(1)|浏览(251)

我试图理解自动并行化是如何工作的,以加快我正在编写的程序的执行速度。我创建了一个更简单的示例:

#include <iostream>
#include <vector>
#include <chrono>

using namespace std;
using namespace std::chrono;

class matrix
{
public:
   matrix(int size, double value) 
   {
       A.resize(size, vector<double>(size, value));
       B.resize(size, vector<double>(size, value));
   };
   void prodScal(double valore)
   {
        for (int m = 0; m < A.size(); m++)
            for (int n = 0; n < A.size(); n++)
            {
                B[m][n] = A[m][n] * valore;
            };
    };
    double elemento(int riga, int column) { return B[riga][column]; }

protected:
    vector<vector<double>> A, B;
};

void main()
{
    matrix* M;
    M = new matrix(1000, 174.9);
    high_resolution_clock::time_point t1 = high_resolution_clock::now();

    #pragma loop(hint_parallel(4))
    for (int i = 0; i < 1000; i++)
        M->prodScal(567.3);

    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    auto duration = duration_cast<milliseconds>(t2 - t1).count();
    cout << "execution time [ms]: " << duration << endl;
 }

当我尝试使用cl main.cpp /O2 /Qpar /Qpar-report:2编译这段代码时,我得到以下消息:
c:\users\utente\documents\visual studio 2017\projects\parallel\parallel\main.cpp(39):信息C5012:ciclo non parallelizzato a causa del motivo '500'
c:\users\utente\documents\visual studio 2017\projects\parallel\parallel\main.cpp(39):信息C5012:ciclo non parallelizzato a causa del motivo '500'
c:\users\utente\documents\visual studio 2017\projects\parallel\parallel\main.cpp(38):信息C5012:ciclo non parallelizzato a causa del motivo '1000'
你能帮我用正确的方法来并行化这个循环吗?谢谢。

yi0zb3m4

yi0zb3m41#

自动并行化工作(或信念?)是一把双刃剑:

机器只能在一定程度上“猜测”意图(并且可以放弃,每当这样的意图是不清楚的一套预先有线的转型战略),所以,而不是期望任何聪明的伎俩在大规模的不同的方法可能的。营销人员将敲他们所有的鼓,吹他们所有的口哨来销售自动“思考”产品,但现实是不同的。即使是最好的最好的承认,最好的性能来自指令级分析,有时他们甚至避免超标量流水线处理器编织技巧,以便获得最后几纳秒,在CPU uop指令流的最后一级的并行代码性能中损失。所以,最好永远不要指望仅仅通过使用#pragma代码段就能获得这样的专业知识,相信“机器”会发明一种最聪明的方法。

所以,测试它(总是彻底地):

尝试将最外层的for(){...}“并行化”并不是最好的开始步骤。无论是性能方面还是资源方面。让我们从另一个Angular 来处理这种情况,即计算本身:

#include <iostream>              // https://stackoverflow.com/questions/48033769/auto-parallelization-with-vs
#include <vector>
#include <chrono>                // g++ FLAGS.ADD: -std=c++11
#include <omp.h>                 // g++ FLAGS.ADD: -fopenmp -lm
#define   OMP_NUM_OF_THREADS 4

using namespace std;
using namespace std::chrono;

class matrix {
public:
   matrix( int size, double value ) {
           A.resize( size, vector<double>( size, value ) );
           B.resize( size, vector<double>( size, value ) );
   }
   void prodScal( double aScalarVALORE ) {
     // #pragma loop( hint_parallel(4) )                                           // matrix_(hint_parallel(4)).cpp:18:0: warning: ignoring #pragma loop  [-Wunknown-pragmas]
        #pragma omp parallel num_threads( OMP_NUM_OF_THREADS )                     // _____ YET, AGNOSTIC TO ANY BETTER CACHE-LINE RE-USE POLICY        
        for (     unsigned int m = 0; m < A.size(); m++ )
            for ( unsigned int n = 0; n < A.size(); n++ )
                  B[m][n] = A[m][n] * aScalarVALORE;
   }
   double elemento( int riga, int column ) { return B[riga][column]; }

protected:
    vector<vector<double>> A, B;
};

int main() {                              // matrix_(hint_parallel(4)).cpp:31:11: error: ‘::main’ must return ‘int’

    matrix* M;
    M = new matrix( 1000, 174.9 );
    high_resolution_clock::time_point t1 = high_resolution_clock::now();
 // *******************
 // DEFINITELY NOT HERE
 // *******************
 // #pragma loop(hint_parallel(4))       // JUST A TEST EXECUTION, NOT ANY PARALLELISATION BENEFIT FOR A PROCESS-PER-SE PERFORMANCE
    for ( int i = 0; i < 1000; i++ )
        M->prodScal( 567.3 );

    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    auto duration = duration_cast<milliseconds>( t2 - t1 ).count();
    cout << "execution time [ms]: " << duration << endl;
  /* 
   * execution time [ms]: 21601
     ------------------
    (program exited with code: 0)
   * */
    return 0;
 }

一旦有了一个工作代码,性能调整,以获得最大,是下一个障碍
更好地遍历for(){...}可以显著提高所有MEM获取的成本总和(为每个非缓存引用支付~ +100 [ns])v/s CACHE重用(为任何缓存重用仅支付~ +1.5 [ns])。
它取决于矩阵的全局大小、L3、L2和L1高速缓存大小以及高速缓存线长度/关联性,更不用说如果代码要在虚拟设备上运行的附加性能偏差。

静态大小调整和近似的NUMA拓扑可以使用lstopo(在没有智能hwloc服务的情况下为lscpu)来描述。
在这里,您可以读取该高速缓存容量,它可以保存矩阵单元,以实现智能重用的任何潜在加速(遵守for(){...}索引的缓存行跨越)。
通过调整for()循环步进可以获得最佳性能,最好接近CPU硬件可用的ILP级别(使用来自CPU指令级并行性的另一种可能的并行度,允许用于协同执行的微指令链(参考有关这些详细信息的英特尔CPU出版物),并且在目标平台上经过最佳测试(交叉编译将无法允许在目标CPU架构上没有性能基准的情况下进行这种优化,最好在体内目标平台上)。
细节远远超出了这个媒体格式的有限范围,在Stack Overflow上,但如果对性能调优感兴趣,你会发现这两个来源和你自己的实验实践经验将指导你进一步的步骤。我做了一个大矩阵线性代数项目,完成了几个[TB]矩阵的处理,从126小时到几分钟(不算加载阶段,将矩阵数据放入RAM),正确的并行代码设计非常仔细,所以确实值得做的设计“正确”。
对于甚至更高的性能,还必须避免O/S驱逐昂贵的预取数据,因此需要更多的努力来获得最终性能,而不仅仅是依赖于自动化的“自动并行化”玩具。

结语

如果仍然有疑问,如果这确实是可能的,为什么HPC中心仍然照顾和培养HPCMaven设计最终性能的代码,如果“自动并行化”玩具会做得更好,或者至少和这些Maven书呆子一样?他们不会,如果他们确实可以。

相关问题