gpt4 book ai didi

c++ - OpenMP 如何实现对临界区的访问?

转载 作者:太空宇宙 更新时间:2023-11-04 02:13:15 26 4
gpt4 key购买 nike

我想读取一个输入文件(在 C/C++ 中)并尽可能快地独立处理每一行。处理本身需要一些时间,所以我决定使用 OpenMP 线程。我有这段代码:

#pragma omp parallel num_threads(num_threads)
{
string line;
while (true) {
#pragma omp critical(input)
{
getline(f, line);
}
if (f.eof())
break;
process_line(line);
}
}

我的问题是,如何确定要使用的最佳线程数?理想情况下,我希望在运行时动态检测到它。我不明白 parallel 的 DYNAMIC schedule 选项,所以我不能说这是否有帮助。有什么见解吗?

此外,我不确定如何“手动”确定最佳数量。我为我的特定应用程序尝试了各种数字。我原以为 top 报告的 CPU 使用率会有帮助,但它没有(!)在我的例子中,CPU 使用率始终保持在 num_threads*(85-95) 左右。但是,使用 pv 来观察我读取输入的速度,我注意到最佳数量大约是 2-5;在此之上,输入速度变小。所以我的问题是——为什么我会在使用 10 个线程时看到 850 的 CPU 使用率?这可能是由于 OpenMP 处理等待进入临界区的线程的效率低下造成的吗?

编辑:这里有一些时间安排。我通过以下方式获得了它们:

for NCPU in $(seq 1 20) ; do echo "NCPU=$NCPU" ; { pv -f -a my_input.gz | pigz -d -p 20 | { { sleep 60 ; PID=$(ps gx -o pid,comm | grep my_prog | sed "s/^ *//" | cut -d " " -f 1) ; USAGE=$(ps h -o "%cpu" $PID) ; kill -9 $PID ; sleep 1 ; echo "usage: $USAGE" >&2 ; } & cat ; } | ./my_prog -N $NCPU >/dev/null 2>/dev/null ; sleep 2 ; } 2>&1 | grep -v Killed ; done

中央处理器=1[8.27MB/秒]使用率:98.4

中央处理器=2[12.5MB/秒]用法:196

中央处理器=3[18.4MB/秒]用法:294

中央处理器=4[23.6MB/秒]用法:393

中央处理器=5[28.9MB/秒]用法:491

中央处理器=6[33.7MB/秒]用法:589

中央处理器=7[37.4MB/秒]用法:688

中央处理器=8[40.3MB/秒]用法:785

中央处理器=9[41.9MB/秒]用法:884

中央处理器=10[41.3MB/秒]用法:979

中央处理器=11[41.5MB/秒]用法:1077

中央处理器=12[42.5MB/秒]用法:1176

中央处理器=13[41.6MB/秒]用法:1272

中央处理器=14[42.6MB/秒]用法:1370

中央处理器=15[41.8MB/秒]用法:1493

中央处理器=16[40.7MB/秒]用法:1593

中央处理器=17[40.8MB/秒]用法:1662

中央处理器=18[39.3MB/秒]用法:1763

中央处理器=19[38.9MB/秒]用法:1857

中央处理器=20[37.7MB/秒]用法:1957

我的问题是我可以在 785 CPU 使用率和 1662 CPU 使用率下达到 40MB/s。那些额外的周期去哪儿了??

EDIT2:感谢 Lirik 和 John Dibling,我现在明白我发现上面令人费解的时序的原因与 I/O 无关,而是与 OpenMP 实现关键部分的方式有关。我的直觉是,如果 CS 中有 1 个线程,并且有 10 个线程等待进入,当第一个线程退出 CS 时,内核应该唤醒另一个线程并让它进入。 timings 另有建议:线程是否会自行唤醒多次并发现 CS 已被占用?这是线程库或内核的问题吗?

最佳答案

"I want to read an input file (in C/C++) and process each line independently as fast as possible."

从文件中读取会使您的应用程序 I/O 受限,因此仅读取部分您能够实现的最大性能是以最大磁盘速度读取(在我的机器上,CPU 时间不到 10%)。这意味着,如果您能够完全从任何处理中释放读取线程,则需要处理时间少于剩余的 CPU 时间(我的计算机为 90%)。如果线路处理线程占用的时间超过了剩余的CPU时间,那么你就跟不上硬盘了。

在这种情况下有几种选择:

  1. 排队输入并让处理线程“工作”出队,直到它们 catch 呈现的输入(前提是您有足够的 RAM 来这样做)。
  2. 打开足够多的线程并最大限度地使用您的处理器,直到读取所有数据,这是您尽力而为的场景。
  3. 限制读取/处理,以免占用所有系统资源(以防您担心 UI 响应能力和/或用户体验)。

"...the processing takes a few ticks itself, so I decided to use OpenMP threads"

这是一个好兆头,但也意味着您的 CPU 使用率不会很高。这是您可以优化性能的部分,最好手动完成,正如 John Dibling 提到的那样。通常,最好将每一行都排入队列,让处理线程从队列中拉取处理请求,直到您没有其他要处理的内容为止。后者也称为生产者/消费者设计模式 - 并发计算中非常常见的模式。

更新

Why is there be a difference between

  • (i) each process get lock, pull data, release lock, process data; and
  • (ii) one process: pull data, get lock, enqueue chunk, release lock,
  • others: get lock, dequeue chunk, release lock, process data?

区别很小:在某种程度上,两者都代表消费者/生产者模式。在第一种情况 (i) 中,您没有实际的队列,但您可以将文件流视为您的生产者(队列),而消费者是从流中读取的线程。在第二种情况 (ii) 中,您显式地实现了消费者/生产者模式,该模式更健壮、可重用并为生产者提供了更好的抽象。如果您决定使用多个“输入 channel ”,那么后一种情况更好。

最后(可能也是最重要的),您可以使用 lock-free queue使用单个生产者和单个消费者,这将使 (ii) 在让您受到 i/o 约束方面比 (i) 快得多。使用无锁队列,您可以拉取数据入队dequque block 无需锁定

关于c++ - OpenMP 如何实现对临界区的访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10922558/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com