Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何提高OpenMP代码的性能?_C++_Multithreading_Performance_Openmp - Fatal编程技术网

C++ 如何提高OpenMP代码的性能?

C++ 如何提高OpenMP代码的性能?,c++,multithreading,performance,openmp,C++,Multithreading,Performance,Openmp,我目前正在努力提高代码的并行性能,而且我还是OpenMP新手。我必须在一个大容器上迭代,在每次迭代中读取多个条目并将结果写入单个条目。下面是一个非常小的代码示例,说明了我正在尝试做的事情 data是指向数组的指针,其中存储了大量数据点。在并行区域之前,我创建了一个数组newData,因此可以将data用作只读,将newData用作只读,然后我将旧的数据扔掉,并使用newData进行进一步的计算。 据我所知,data和newData在线程之间共享,并且在并行区域内声明的所有内容都是私有的。 多线程

我目前正在努力提高代码的并行性能,而且我还是OpenMP新手。我必须在一个大容器上迭代,在每次迭代中读取多个条目并将结果写入单个条目。下面是一个非常小的代码示例,说明了我正在尝试做的事情

data
是指向数组的指针,其中存储了大量数据点。在并行区域之前,我创建了一个数组
newData
,因此可以将
data
用作只读,将
newData
用作只读,然后我将旧的
数据
扔掉,并使用
newData
进行进一步的计算。 据我所知,
data
newData
在线程之间共享,并且在并行区域内声明的所有内容都是私有的。 多线程读取数据是否会导致性能问题

我使用
#critical
newData
的元素指定新值,以避免竞争条件。这是必要的吗?因为我只访问
newData
的每个元素一次,而且从不通过多个线程访问

我也不确定日程安排。我是否必须指定我想要的是
静态
还是
动态
计划?既然所有线程都是相互独立的,我可以使用
nowait

array *newData = new array;

omp_set_num_threads (threads);

#pragma omp parallel
{
    #pragma omp for
    for (int i = 0;  i < range; i++)
    {
        double middle = (*data)[i];
        double previous = (*data)[i-1];
        double next = (*data)[i+1];

        double new_value = (previous + middle + next) / 3.0;
        #pragma omp critical(assignment)
        (*newData)[i] = new_value;
    }
}

delete data;
data = newData;
array*newData=新数组;
omp_设置_数量_线程(线程);
#pragma-omp并行
{
#pragma omp for
对于(int i=0;i
我知道在第一次和最后一次迭代中,
上一次
下一次
不能从
数据
中读取,在实际代码中,这一点得到了解决,但对于这个最小的示例,您可以从
数据
中多次读取

  • 由多个线程读取数组通常不会造成任何伤害
  • 如果多个线程处理完全相同的数据段,那么您只需要一个关键部分,这里每个线程访问数组的不同部分,因此您不需要它。关键部分对性能非常不利,因此仅在绝对必要时使用它们。通常,它们可以被原子行为所取代: 就像关键部分一样,如果每个线程访问不同的数据,它们就没有意义
  • 对于调度器来说,最好测试每一个并测量性能,因为对性能的预测通常是错误的。还可以尝试不同的块大小
  • 其他一些可能有帮助的事情:
    • 测量性能通常会受到电脑上其他任务的干扰,因此请进行多次测量并取其最小值(除非每次输入不同,然后取平均值并进行更多测量)
    • 你真的需要双精度吗?浮动要快得多
  • 编辑:nowait用于多个独立的循环:

  • 我假设你想用一维数组做卷积或中值模糊。简单的回答是:坚持默认的日程安排策略,彻底摆脱关键问题


    我可以告诉你,你是一个对并行性不感兴趣的新手,处理OpenMP指令有点混乱,比如nowait/private/reduce/critical/atomic/single等等。我认为你需要一本编写良好的教科书来澄清各种概念。如果您有扎实的知识,一个小时的OpenMP学习足以处理大多数日常编程

    首先,消除所有不必要的依赖关系<代码>#pragma omp critical(assignment)
    不是必需的,因为
    (*newData)
    的每个索引在每个循环中只写入一次,因此不存在争用条件

    您的代码现在可以如下所示:

    #pragma omp parallel for
    for (int i = 0; i < range; i++)
       (*newData)[i] = ((*data)[i-1] + (*data)[i] + (*data)[i+1]) / 3.0;
    
    assert(range % 4 == 0);
    #pragma omp parallel for
    for (int i = 0; i < range/4; i++) {
       (*newData)[i*4+0] = ((*data)[i*4-1] + (*data)[i*4+0] + (*data)[i*4+1]) / 3.0;
       (*newData)[i*4+1] = ((*data)[i*4+0] + (*data)[i*4+1] + (*data)[i*4+2]) / 3.0;
       (*newData)[i*4+2] = ((*data)[i*4+1] + (*data)[i*4+2] + (*data)[i*4+3]) / 3.0;
       (*newData)[i*4+3] = ((*data)[i*4+2] + (*data)[i*4+3] + (*data)[i*4+4]) / 3.0;
    }
    
    内存带限制: 对于非常简单的循环,请考虑以下内容。您必须加载多少内存,CPU将在多长时间内忙于处理它。您将加载大约1条缓存线,并计算一些解引用、一些指针加法、两个加法和一个除法。您达到的限制取决于您的CPU规格。 现在考虑缓存局部性。您能否修改代码以更好地利用缓存?如果一个线程在一次循环迭代中得到i=3,而在下一次循环迭代中得到i=7,则必须重新加载3(*数据)。但是如果从i=3变为i=4,则可能不必加载任何内容,因为(*data)[i+1]位于先前加载的缓存线中。你可以节省一些公羊。要利用此功能,请展开循环。同时使用float而不是double会增加这个机会

    隐藏的依赖项: 现在这部分我个人觉得很棘手。有时您的编译器不是舒尔,它可以重用一些数据,因为它不知道这些数据没有更改。使用
    const
    有助于编译器。但有时您需要一个
    restrict
    来向编译器提供正确的提示。但我不太明白这一点,无法解释它

    下面是我要尝试的:

    const double ONETHIRD = 1.0 / 3.0;
    assert(range % 4 == 0);
    #pragma omp parallel for schedule(static, 1024)
    for (int i = 0; i < range/4; i++) {
       (*newData)[i*4+0] = ((*data)[i*4-1] + (*data)[i*4+0] + (*data)[i*4+1]) * ONETHIRD;
       (*newData)[i*4+1] = ((*data)[i*4+0] + (*data)[i*4+1] + (*data)[i*4+2]) * ONETHIRD;
       (*newData)[i*4+2] = ((*data)[i*4+1] + (*data)[i*4+2] + (*data)[i*4+3]) * ONETHIRD;
       (*newData)[i*4+3] = ((*data)[i*4+2] + (*data)[i*4+3] + (*data)[i*4+4]) * ONETHIRD;
    }
    
    const-double-ONETHIRD=1.0/3.0;
    断言(范围%4==0);
    #计划的pragma omp并行(静态,1024)
    对于(int i=0;i
    然后进行基准测试。再基准一些,再基准一些。只有基准测试才能显示哪些技巧有用

    PPS:还有一件事要考虑。如果你看到你的程序用硬键敲击内存带。你可以考虑改变算法。也许把两个步骤融合成一个步骤。喜欢从