如何提高 OpenMP 代码的性能?

2024-02-26

我目前正在尝试提高我的代码的并行性能,但我对 OpenMP 仍然是新手。我必须迭代一个大容器,在每次迭代中从多个条目读取并将结果写入单个条目。下面是我正在尝试做的一个非常简单的代码示例。

data是一个指向数组的指针,其中存储了许多数据点。在并行区域之前我创建了一个数组newData,所以可以使用data作为只读和newData作为只写,之后我扔掉旧的data离开并使用newData以便进一步计算。 据我了解data and newData在线程之间共享,并且并行区域内声明的所有内容都是私有的。 可以读自data多线程导致性能问题?

我在用#critical用于将新值分配给newData以避免竞争条件。这是必要的吗,因为我访问了每个元素newData仅一次且从不通过多线程?

我也不确定日程安排。我是否必须指定是否需要static or dynamic日程?我可以用吗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;

我知道在第一次和最后一次迭代中previous and next无法读取data,在真正的代码中,这是被处理的,但对于这个最小的例子,你会得到多次读取的想法data.


首先,摆脱所有不必要的依赖关系。#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;

现在我们正在寻找瓶颈。我提出的潜在候选人名单如下:

  • 慢速分裂
  • 缓存颠簸
  • ILP(指令级并行)
  • 内存带宽限制
  • 隐藏的依赖项

那么让我们进一步分析它们。

慢速除法:有些 CPU 需要永远计算 double/double。要了解您的 CPU 的运行时间和吞吐量,您必须查看其规格。也许更换/3.0 with *0.3333..可能有帮助,但也许你的编译器已经这样做了。使用扩展指令集(如 SSE/AVX),您可以一次安排多个除法/乘法。

缓存抖动:由于您的 CPU 必须一次加载/存储一个缓存行,因此可能会发生冲突。想象一下,如果线程 1 尝试写入 (*newdata)[1],线程 2 尝试写入 (*newdata)[2],并且它们位于同一缓存行上。现在他们中的一个必须等​​待另一个。你可以用以下方法解决这个问题#pragma omp parallel for schedule(static, 64).

ILP:如果操作是独立的,CPU 可以将多个操作调度到管道中。为此,您必须展开循环。这可能看起来像这样:

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 (*data)。但如果从 i=3 到 i=4,您可能不需要加载任何内容,因为 (*data)[i+1] 位于先前加载的缓存行中。您可以节省一些 RAM 带宽。要利用它,请展开循环。使用 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;
}

然后进行基准测试。进行更多基准测试,再进行更多基准测试。只有基准测试才能告诉您哪些技巧有帮助。

PS:还有一件事需要考虑。如果您发现您的程序严重占用内存带宽。您会考虑更改算法。也许将两个步骤合二为一。就像从b[i] := (a[i-1] + a[i] + a[i+1]) / 3.0 to d[i] := (n[i-1] + n[i] + n[i+1]) / 3.0 = (a[i-2] + 2.0 * a[i-1] + 3.0 * a[i] + 2.0 * a[i+1] + a[i+1]) / 3.0。我想这其中的原因你自己就会找到的。

享受优化的乐趣;-)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何提高 OpenMP 代码的性能? 的相关文章

  • C++ 和序列化:有什么方法可以进行某种内省吗?

    我读过一些例子维基百科 http en wikipedia org wiki Type introspection C 2B 2B但我正在寻找一些现实生活中的例子 如何使用内省 为什么 它有助于编写干净的代码 以及代码本身 例如 有没有办法
  • 可变数量的(常量)引用参数

    我试图从我的高级代码 使用 C 11 中消除原始指针 并且我找到了引用 尤其是const 在许多情况下 当没有所有权转移时 是一个很好的替代品 但如果有的话该怎么办variable我想通过 常量 引用传递的参数数量 你不能创建一个std v
  • 为什么这个 oracle 批量插入不起作用?

    我正在尝试将一些数据批量插入到 oracle 数据库中 我按照文档中的示例进行操作 this DataBaseAccess new OracleConnection connString var dataAdapter new Oracle
  • WPF Dispatchertimer 延迟反应/冻结

    在我的 WPF 应用程序中 我使用 3 个不同的 DispatcherTimers 一种是用于显示当前时间 一种是每 5 秒运行一次数据库查询 第三个每 1 秒刷新一次自定义按钮的值 当我的程序运行时 有很多延迟 冻结 例如 时间开始正确计
  • 计算复杂数组的abs()值的最快方法

    我想计算 C 或 C 中复杂数组元素的绝对值 最简单的方法是 for int i 0 i lt N i b i cabs a i 但对于大向量来说 速度会很慢 有没有办法加快速度 例如使用并行化 语言可以是 C 或 C 鉴于所有循环迭代都是
  • 在目标 VS 安装时,VSIX 扩展内部使用的 WPF-Log4Net 未输出日志

    当 Log4net 在 VSIX 扩展中使用并安装在另一个目标 VS 上时 它不会记录日志 我有一个 WPF 解决方案 我下载了 log4net dll 添加了 log4net config 并将 复制到输出目录 值设置为 始终复制 log
  • 为什么将 char 传递给函数会改变它在 c 中的值?

    我目前正在关注本作业簿 http www cs bham ac uk exr lectures opsys 10 11 lectures os dev pdf关于构建操作系统 我的目的是写一个64位内核 我已经在文本模式下加载 内核 代码并
  • 如何删除实体框架6中的多对多关系

    如果将项目连接为多对多关系 则从数据库中删除项目时会出现问题 我的数据库看起来像 Project lt JobInProject gt Job ProjectID JobInProjectID JobID ProjectID JobID 主
  • ASP.NET中如何访问除wwwroot以外的位置

    我可以使用访问服务器的物理位置Server MapPath 这给了我内部的物理路径wwwroot文件夹 我想将一些数据保存到同一服务器的另一个驱动器中D 驾驶 我想我无法获取以下位置的物理位置D 驾驶使用Server MapPath因为它位
  • 如何从Web JavaScript应用程序获取桌面C#程序中的变量

    我遇到一个问题 有两个应用程序 一种是 C 中的桌面应用程序 另一种是 javascript 中的 Web 应用程序 运行桌面应用程序中的一些变量或信息需要传输到Web应用程序 有谁知道如何解决这个问题 有人愿意提供更多细节来解决这个问题吗
  • 三种 System.Drawing 方法表现出缓慢的绘制或闪烁:解决方案?或其他选择?

    我正在通过 System Drawing 进行一些绘图 但遇到了一些问题 我将数据保存在队列中 并将该数据绘制 绘制 到三个图片框中 此方法填充图片框 然后滚动图形 所以不要在以前的绘图上绘制 并且逐渐看起来更混乱 我找到了两种绘制图表的解
  • 为什么我无法调试动态加载的程序集?

    我正在开发一个 Web API 项目 该项目使用内部模拟框架 允许拦截和修改来自控制器的响应 它使用 MEF 加载包含某些先决条件匹配时执行的代码的程序集 我知道这是正常工作的 因为我可以在响应中看到模拟已被执行 但由于某种原因我无法调试动
  • 类型别名和不完整类型

    我可能已经超出了解决这个本应简单的问题的范围 我在这里开始这个问题 在编译时获取基类的类型 https stackoverflow com questions 17735852 getting type of a base class at
  • 如何在asp.net core 6中注入IConfiguration

    web api 应用程序中不再有 Startup cs 我们以前可以注入IConfiguration进入那个Startup class public class Startup public Startup IConfiguration c
  • C++ 联合数组和变量?

    在C 中没有办法做这样的事情吗 union Scalar x y Scalar v 2 Where x v 0 and y v 1 既然您使用的是 C 而不是 C 并且它们具有相同的类型 为什么不直接将 x 设为对 v 0 的引用 将 y
  • Gridview 错误:对 Bind 的调用格式不正确

    我有以下 gridview 代码
  • C# 3.0 中自动属性和公共字段的区别

    我无法理解为什么 C 3 0 中存在自动实现的属性语言功能 当你说的时候有什么区别 public string FirstName than public string FirstName get set 因为它们在生成的 IL 代码 和机
  • char[length]初始化并处理

    我定义了一个字符数组 char d 6 如果我在以下方面有误 请纠正我 此时没有为变量分配内存d 现在我要初始化它 d aaaaa 这种初始化之后 就不需要释放内存了 它将自动完成 我怎么知道是否char 被初始化了吗 我正在寻找类似的模式
  • asp.net mvc GET 请求上的 formcollection 应该为空

    我正在发布一个简单的操作 public void Login FormCollection formCollection 即使查询字符串值很少 formcollection Count is 0 是靠行为吗 FormCollection 使
  • 如何使用 Ioc Unity 注入依赖属性

    我有以下课程 public interface IServiceA string MethodA1 public interface IServiceB string MethodB1 public class ServiceA IServ

随机推荐

  • 在不使用内容辅助输入整行时,Eclipse 在光标前插入分号

    这种行为让我抓狂 我输入以下内容 fileName append c get Calendar HOUR OF DAY 现在 当我输入内容时 会弹出内容辅助 我没有从内容辅助中选择 HOUR OF DAY 也没有按 Enter 键自动填写
  • 在C中计算欧几里德距离矩阵

    我想将用 MATLAB 编写的代码转换为 C matrix 1 2 3 4 5 6 7 8 10 dis zeros 9 for i 1 3 for j 1 3 dis i j sqrt sum abs matrix i matrix j
  • 尝试从网络位置加载程序集,这会导致程序集被沙箱化

    Net 4 0 出现错误 尝试从网络位置加载程序集 会导致程序集被沙箱化 应用设置
  • 带有 IAM 的 AWS MSK - 超时异常

    我们正在使用 Aws MSK 最近他们宣布了 AWS MSK IAM AUTH https github com aws aws msk iam auth https github com aws aws msk iam auth 当我尝试
  • css动画为什么会改变z-index? [复制]

    这个问题在这里已经有答案了 为什么动画会改变 z index 如果您查看 jsfiddle 您会看到红色图像位于顶部 但如果注释掉动画 则蓝色图像位于顶部 即使有动画 如何才能使蓝色图像始终位于顶部 jsfiddle http jsfidd
  • 删除字符串中重复字符(单词)的最佳方法?

    删除字符串中任何重复字符和由空格分隔的字符集的最佳方法是什么 我认为这个例子更好地解释了这一点 foo h k k h2 h 应该变成 foo h k h2 order not important 其他例子 foo s s k become
  • 在 Windows 事件查看器中诊断 CLR 错误

    我们有一个 NET 桌面应用程序在生产中崩溃了 我们如何诊断错误 我想知道发生的异常类型 错误消息和堆栈跟踪 因为我们的代码没有处理异常 所以我们收到了 此应用程序遇到问题 需要关闭 Windows 消息框 唯一的选择就是关闭 没有调试按钮
  • 字符串匹配不同大小的 data.frames

    我有两个不同大小的 data frame 我正在寻找最有效的方法来将字符串从一个 data frame 匹配到另一个 data frame 并提取一些相关信息 这是一个例子 两个初始 data frames a 和 b 以及所需的结果 a
  • 如何使图像大小适应html表格中的行高

    我正在尝试制作一个强大的 html 签名以在 Thunderbird 中使用 我所说的稳健是指它不仅在 Thunderbird 中看起来正确 而且在我向其发送邮件的其他邮件客户端中也必须正确 例如 我使用 Gmail 进行了测试 布局非常简
  • ASP.Net Core 中的 AntiXSS

    Microsoft Web 保护库 AntiXSS https wpl codeplex com 已达到使用寿命 该页面指出 在 NET 4 0 中 框架中包含了一个 AntiXSS 版本 可以通过配置启用 在 ASP NET v5 中 基
  • PHP + Comet(长轮询)扩展/主机

    我使用 PHP MySQL 开发了一个应用程序 该应用程序使用了彗星技术 即长轮询 概念验证效果很好 但我预计流量会快速达到数千个同时连接 网络服务器上的数千个开放连接 我没有能力设置自己的服务器 例如使用 lighttpd 并且通常依赖简
  • 将自定义侦听器添加到 Kotlin 类的正确方法

    我是 Kotlin 菜鸟 第三天 正在尝试向我的班级添加一个侦听器 我目前正在使用 lambda 如下例所示 它们工作得很好 Declare callback class Controller var onAction gt Use cal
  • 有符号和无符号整数表达式的比较

    我刚刚开始使用 OpenGL 这是我的第一个代码 OpenGL hello program include
  • 如何确定复选框列表已选中/选中 -

    当我执行代码时 我得到 4 个复选框 并且我选中 选择了所有 4 个复选框 当我尝试调试代码时 它确实算作我有 4 个复选框 但所有 4 个复选框都被选中 false 我在代码中缺少什么
  • 检测项目中的循环依赖

    上次我在我的项目中发现了循环依赖问题 我解决了我的问题 但是我希望将来避免这种情况 我想到了在我的所有项目中发现循环依赖项并给我反馈的插件 Example 文件a js var functionFromA require b js cons
  • 如何增加 SwiftUI 选择器中显示的最大行数?

    我正在尝试创建一个 SwiftUI 选择器 用户可以使用它来选择 1000 到 20000 之间的数字 以 1000 为增量 例如 1000 2000 3000 20000 默认情况下 SwiftUI 选择器只能容纳 10 行文本 如何允许
  • 保存数据的最佳方法 - 首选项、sqlite、可序列化还是其他?

    我一直在研究在回合之间保存游戏数据的替代方法 并且想知道是否有人可以为我指明正确的方向 我有大约 32k 的数据必须在 onPause 期间保存 由于数据量巨大 我排除了偏好 我花了几天时间研究 SQLite 但无法在不到两秒的时间内保存数
  • Shellcode:执行 2 次 execve() 调用

    我正在尝试用汇编语言编写 shellcode 我需要执行一个 usr bin killall命令与 usr bin wget命令 我的两个命令都在 shellcode 中完美运行execve 系统调用 但现在我想将这两个结合起来 但这是不可
  • 在 Node.js 中从 SQLite 获取表列表

    下面的代码仅返回第一个表的名称 如何获取现有表中所有可用表名称的列表sqlite const sqlite3 require sqlite3 verbose const db new sqlite3 Database path to myd
  • 如何提高 OpenMP 代码的性能?

    我目前正在尝试提高我的代码的并行性能 但我对 OpenMP 仍然是新手 我必须迭代一个大容器 在每次迭代中从多个条目读取并将结果写入单个条目 下面是我正在尝试做的一个非常简单的代码示例 data是一个指向数组的指针 其中存储了许多数据点 在