为什么并行化会如此显着地降低性能?

2024-02-12

我有一个 OpenMP 程序(数千行,无法在这里重现),其工作原理如下:

它由工作线程和任务队列组成。
一个任务由一个卷积组成;每次工作线程从工作队列中弹出任务时,它都会执行所需的卷积,并可选择将更多卷积推送到队列中。
(没有特定的“主”线程;所有工作线程都是平等的。)

当我在自己的机器上运行这个程序时(4 核 HT 非 NUMA Core i7 http://ark.intel.com/products/75117),我得到的运行时间是:

(#threads: running time)
 1: 5374 ms
 2: 2830 ms
 3: 2147 ms
 4: 1723 ms
 5: 1379 ms
 6: 1281 ms
 7: 1217 ms
 8: 1179 ms

这是有道理的。

但是,当我在 NUMA 48 核 AMD Opteron 6168 计算机上运行它时,我得到以下运行时间:

 1: 9252 ms
 2: 5101 ms
 3: 3651 ms
 4: 2821 ms
 5: 2364 ms
 6: 2062 ms
 7: 1954 ms
 8: 1725 ms
 9: 1564 ms
10: 1513 ms
11: 1508 ms
12: 1796 ms  <------ why did it get worse?
13: 1718 ms
14: 1765 ms
15: 2799 ms  <------ why did it get *so much* worse?
16: 2189 ms
17: 3661 ms
18: 3967 ms
19: 4415 ms
20: 3089 ms
21: 5102 ms
22: 3761 ms
23: 5795 ms
24: 4202 ms

这些结果非常一致,这不是机器负载造成的。
所以我不明白:
是什么导致12核之后性能下降这么多?

我会理解如果性能饱和的在某种程度上(我可以将其归咎于有限的内存带宽),但我不明白它是如何做到的drop通过添加从 1508 毫秒到 5795 毫秒more线程。

这怎么可能?


这类情况可能很难弄清楚。关键是查看内存位置。如果没有看到您的代码,就不可能确切地说出了问题所在,但我们可以讨论一些“多线程不太好”的事情:

在所有 NUMA 系统中,当内存位于处理器 X 且代码在处理器 Y 上运行时(其中 X 和 Y 不是同一处理器),每次内存访问都会影响性能。因此,在正确的 NUMA 节点上分配内存肯定会有所帮助。 (这可能需要一些特殊的代码,例如设置关联掩码并至少向操作系统/运行时系统暗示您想要 Numa 感知的分配)。至少,确保您不只是简单地处理由“第一个线程,然后启动更多线程”分配的一个大数组。

另一件更糟糕的事情是共享或错误共享内存 - 因此,如果两个或更多处理器使用相同的缓存行,您将在这两个处理器之间进行乒乓比赛,其中每个处理器都会执行“我想要内存”在地址A”,获取内存内容,更新它,然后下一个处理器将做同样的事情。

事实上,仅在 12 个线程时结果就变差,这似乎表明这与“套接字”有关 - 要么您正在共享数据,要么数据位于“错误的节点上”。在 12 个线程时,您可能开始使用第二个套接字(更多),这将使此类问题更加明显。

为了获得最佳性能,您需要在本地节点上分配内存,不共享也不锁定。您的第一组结果看起来也并不“理想”。我有一些(绝对非共享)代码,可以使处理器数量精确n倍,直到我用完处理器(不幸的是,我的机器只有4个核心,所以它并没有好多少,但仍然好4倍)比 1 核,如果我拿到了 48 或 64 核机器,它在计算“奇怪的数字”时会产生 48 或 64 更好的结果)。

Edit:

“套接字问题”有两件事:

  1. 内存局部性:基本上,内存附加到每个套接字,因此如果内存是从属于“前一个”套接字的区域分配的,那么读取内存会产生额外的延迟。

  2. 缓存/共享:在处理器内,存在用于共享数据的“快速”链接(通常是“底层共享缓存”,例如 L3 缓存),这允许套接字内的内核比套接字中的内核更有效地共享数据。一个不同的插座。

所有这些都相当于维修汽车的工作,但你没有自己的工具箱,所以每次你需要工具时,你都必须向旁边的同事要螺丝刀、15毫米扳手或任何你需要的东西。当您的工作区域有点满时,请将工具归还给您。这不是一种非常有效的工作方式...如果您有自己的工具(至少是最常见的工具 - 每月只使用一次的特殊扳手不是一个大问题),那就更好了,但当然是常见的 10、12 和 15 毫米扳手和几把螺丝刀)。当然,如果有四个机械师共享同一个工具箱,情况会变得更糟。在这种情况下,在四套接字系统中“所有内存都分配在一个节点上”。

现在想象一下,你有一个“一盒扳手”,并且只有一个机械师可以使用这盒扳手,所以如果你需要一把 12 毫米的扳手,你必须等待你旁边的家伙使用完 15 毫米的扳手。如果你有“错误的缓存共享”,就会发生这种情况 - 处理器实际上并没有使用相同的值,但因为缓存行中有多个“东西”,所以处理器正在共享缓存行(扳手盒) 。

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

为什么并行化会如此显着地降低性能? 的相关文章

随机推荐

  • 机械化如何获取当前 url

    我有这个代码 require mechanize agent Mechanize new page agent get http something com page 1 next page page link with href gt p
  • Docker Tomcat容器无法访问Postgres容器

    我有一个带有 postgres 的 alpine docker 带有监听地址 并监听 5432 我正在使用它进行部署 docker run d name postgres me postgres v1 以及带有 oracle jre8 的
  • Sequelize 在查询中返回连接表

    我的 MSQL 表中这两个模型之间存在多对多关系 场所 代表可以有多个所有者 员工 的场所 员工 代表员工 可以是首席执行官或销售员工或任何其他人员 我在用续集 questions tagged sequelize像这样建立关系 关系员工
  • sed 命令中的破折号 e(-e) 是什么意思?

    我是 sed 的新手 总是在输入文件上执行一个命令 最近我尝试使用 e 要处理多个命令 但我无法弄清楚它是如何工作的 默认打印非常烦人 所以我无法弄清楚命令的执行顺序 sed e command 1 e command 2 input tx
  • 原子整数incrementAndGet()线程安全吗?

    原子整数incrementAndGet 方法线程安全吗 我没有看到其中使用同步关键字 我使用以下代码来生成唯一 ID public enum UniqueIdGenerator INSTANCE private AtomicLong ins
  • python:读取json并循环字典

    我正在学习 python 我像这样循环将 json 转换为字典 它可以工作 但这是正确的方法吗 谢谢 import json output file open output json read output json json loads
  • 启动集群时在 EMR 上配置 Zeppelin 的 Spark 解释器

    我正在 EMR 上创建集群并配置 Zeppelin 以从 S3 读取笔记本 为此 我使用一个 json 对象 如下所示 Classification zeppelin env Properties Configurations Classi
  • 无法使用 PowerMockRunner 运行 JUnit 测试

    我有一个基于 Gradle 的 Java 项目 我现在想使用 PowerMock 模拟私有方法 问题是我无法使用 PowerMockRunner 因为当我添加 RunWith org powermock modules junit4 Pow
  • 逻辑表达式解析器

    我正在尝试为以下表达式创建一个逻辑表达式解析器 变量A gt 变量B 而不是变量C 对于给定的变量值 解析器应该能够返回结果是 true 还是 false 基本上 表达式仅包含变量 逻辑运算符 或 与 蕴涵 等价 否定和括号 我想问实现这种
  • 将属性文件中的所有键和值作为 Spring 中的 Map 注入

    有人可以提供一些想法来注入属性文件中的所有动态键和值并将其传递为Map to DBConstants使用 Setter 注入和 Collection 的类 密钥事先未知并且可能会有所不同 Example Property File that
  • Maven 不执行任何单元测试

    我正在使用带有多模块的 Maven 有3个项目 foo the parent project foo core foo bar 我配置了所有依赖项和插件foo s pom
  • 我们什么时候以及为什么需要ApplicationRunner和Runner接口?

    我正在学习Spring引导 有哪些典型用例ApplicationRunner或任何跑步者界面 import org junit jupiter api Test import org springframework boot Applica
  • 在 python 中将文件路径作为命令行参数传递

    我需要在文件中写入一些内容 我通过 python 命令行传递这些内容 我正在使用下面的代码mycode py import csv import sys path sys argv 1 row 4 Danny New York with o
  • Spring MVC:进行 AJAX 调用后在对话框中显示数据

    我是 Spring 和 Web 技术的新手 我有一个表 其中包含带有超链接的列 当我单击一行的超链接时 我需要在对话框中显示该行数据以及其他详细信息 我的控制器方法返回一个ModelAndView其中包含我需要显示的数据和显示页面 问题 如
  • 三星中的InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS

    在我的应用程序中 我有一个EditText 带有一个按钮来更改键盘输入类型 代码 ToggleCambiarTeclado setOnClickListener new OnClickListener Override public voi
  • 曾经有效的相同代码现在返回异常

    我正在尝试在几个股票代码之间进行简单的关联 我之前运行过相同的代码并且有效 现在 它返回一个异常 该异常引用了一堆文件并包含我不理解的其他消息 此外 必须在控制台中手动停止该命令 我对 python 和一般编程都很陌生 我正在使用 Spyd
  • 子活动完成时有时不会调用 onActivityResult

    在测试过程中 我注意到有时子活动的 finish 不会执行 onActivityResult 大多数时候它工作正常 但我无法弄清楚这个问题何时以及为何发生 子活动开始 public void launchSubActivity Class
  • 使用Java通过FTP将所有目录复制到服务器

    我需要将一个目录从本地磁盘复制到服务器 该目录包含很多目录 子目录和文件 想想目录的层次结构树 以下是复制一个文件的示例 public void saveFilesToServer throws IOException FTPClient
  • “保留执行”的含义

    阅读答案来自在c标识符中使用下划线的规则是什么 https stackoverflow com questions 228783 what are the rules about using an underscore in a c ide
  • 为什么并行化会如此显着地降低性能?

    我有一个 OpenMP 程序 数千行 无法在这里重现 其工作原理如下 它由工作线程和任务队列组成 一个任务由一个卷积组成 每次工作线程从工作队列中弹出任务时 它都会执行所需的卷积 并可选择将更多卷积推送到队列中 没有特定的 主 线程 所有工