OpenMP 共享数据

OpenMP shared data

本文关键字:数据 共享 OpenMP      更新时间:2023-10-16

我对OpenMP有点陌生,但总体上具有并行处理的经验。我以前和boost::threads一起工作过,现在我正在使用openmp进行测试。

问题是我不知道如何处理共享数据访问,因为我真的不知道 openmp 在内部对并行循环中的共享数据对象做了什么。

我现在正在做什么(到目前为止这是有效的):我使用 mmap 将文件从磁盘读取到内存中。我在内存映射部分之后收到一个关于字符的指针。

OpenMP

现在可以在 OpenMP 并行 for 循环中使用此指针,并在线程之间共享数据。我现在能够在映射和共享文件中搜索正则表达式匹配项,多个线程根据(相当长的)正则表达式列表检查每个字符串。

我在 openmp 循环中将此列表(包含正则表达式的向量)设为私有,因此每个线程都有自己的此列表副本。

问题来了:

为了显着提高应用程序的性能,我需要能够在匹配字符串后从此向量中删除 (regex-) 项。

现在,所有其他活动线程也需要尽快从其列表中删除此项。

所以我使这个列表成为openmp循环中的共享数据对象,但现在当我尝试将(vector.erase(item#))写入列表时,我在运行时出现分段错误。

使用

boost::threads,我只会使用互斥锁在写入/读取此对象时锁定此对象。

但是 openmp 似乎可以处理大部分同步本身,所以现在我想知道在使用对我来说很新的 openmp 时处理这个问题的正确方法是什么。

对于同步,您可以使用#pragma omp critical也可以使用 OpenMP 锁定例程 ( omp_{init,set,unset,destroy}_lock )。

#pragma omp critical的好处是简单,并且当已知并行区域由单个线程执行时,能够忽略杂注。缺点是仅适用于单个并行区域,以及该区域内的全局效应:没有其他线程可以执行该区域中的任何其他关键部分。

OpenMP 锁例程类似于大多数其他可用的锁,例如 pthreads 或 Boost(RAII 除外)的锁。您初始化一个锁定对象,然后使用它来保护某些关键部分,并在不必要的情况下销毁。这些锁可用于保护对来自不同并行区域的数据的访问,构建分布式锁定方案等;但是总是会产生一定的开销,并且与#pragma omp critical相比,使用肯定更"毛茸茸"。

但是,我会挑战并行解决方案的设计。从向量中间擦除元素会使所有迭代器失效,并移动元素。擦除被认为是一种罕见的操作(否则,我认为即使在串行代码中,矢量的选择也是有问题的),但由于上述影响,您还必须保护矢量的所有读取,这可能会很昂贵。读/写锁可以减轻一些负担,但这些锁在 OpenMP 中不可用,因此您需要使用特定于平台的接口或第三方库。

我认为以下内容可能会更好:

    您将正则表达式向量
  • 保密,并添加相同大小的共享标志向量,以指示某个正则表达式是否仍然有效。
  • 在从私有向量应用某个正则表达式之前,代码会在共享向量中检查此正则表达式是否未被其他线程"擦除"。如果是,则跳过正则表达式。
  • 找到匹配项后,代码将与当前正则表达式对应的共享向量的元素标记为"擦除",以便从现在开始将其忽略。

在这个方案中,存在读取/写入标志的竞赛:一个标志可能会在被另一个线程读取为"有效"的下一刻被设置为"擦除"。因此,两个不同的线程可能会同时找到同一正则表达式的匹配项。但是,我相信这个问题存在于您当前的解决方案中,其中所有正则表达式容器都是私有的,以及具有共享容器和锁或 RW 锁的解决方案中,除非非 RW 锁也保护给定正则表达式的操作。如果多场比赛是一个问题,这一切都应该重新考虑。

您可以通过创建关键部分来实现此目的。

#pragma omp critical
{
   ...some synchronized code...
}

编辑:删除了有关"#pragma omp 原子"的部分,因为它无法以原子方式执行所需的操作。