【问题标题】:How to efficiently do scattered summing with SSE/x86如何使用 SSE/x86 有效地进行分散求和
【发布时间】:2012-02-21 09:08:48
【问题描述】:

我的任务是编写一个程序,以可能的绝对最大速度将向量总和流式传输到分散的内存位置。输入数据是一个目标 ID 和一个 XYZ 浮点向量,如下所示:

[198, {0.4,0,1}],  [775, {0.25,0.8,0}],  [12, {0.5,0.5,0.02}]

我需要像这样将它们汇总到内存中:

memory[198] += {0.4,0,1}
memory[775] += {0.25,0.8,0}
memory[12]  += {0.5,0.5,0.02}

更复杂的是,将有多个线程同时执行此操作,从不同的输入流读取但汇总到相同的内存。我预计不会有很多争用相同的内存位置,但会有一些。数据集将非常大 - 我们将同时从多个 SSD 流式传输每个 10+ GB 的多个流,以获得尽可能高的读取带宽。我假设数学是 SSE,尽管它当然不必是这样。

结果暂时用不上,所以不需要污染缓存……不过我是在内存中求和,不只是写,所以不能用MOVNTPS之类的东西,对吧?但是由于线程不会互相踩踏那么多,我怎么能在没有大量锁定开销的情况下做到这一点?你会用内存围栏来做这个吗?

感谢您的帮助。我可以假设 Nehalem 及以上,如果这有影响的话。

【问题讨论】:

  • 我假设您访问的是内存中的一个紧密排列的数组。由于每个元素的宽度是 3 个浮点数,因此大多数元素不会与 16 字节边界对齐,这使得 SSE 移动非常缓慢。此外,正如您的示例所暗示的,数据访问背后没有简单的模式,因此很难预取。总之,这将使上交所的潜力难以发挥。可能最好的猜测是简单地在 C/++ 中执行此操作并让编译器发挥作用,我怀疑使用 SIMD 改进这一点的可能性有很多。
  • 我可以在内存中使用 4-float 向量来毫无问题地获得 16-bit 对齐。预取在这里有什么帮助?这肯定会使求和变得更容易,但是预取+求和+写使我面临各种竞争条件,而其他线程也在做同样的事情。
  • 预取内存时对齐无关紧要,但如果不是,您将不得不使用未对齐移动或确保数据对齐,当您访问元素所在的密集排列的数组时,您不能这样做有 3 个花车宽。

标签: streaming sum sse scatter


【解决方案1】:

您可以使用自旋锁同步访问数组元素(每个 ID 一个)并使用 SSE 进行求和。在 C++ 中,取决于编译器,内部函数可能可用,例如Streaming SIMD ExtensionsInterlockExchange 在 Visual C++ 中。

【讨论】:

    【解决方案2】:

    您的程序的性能将受到内存带宽的限制。除非您拥有多 CPU(而不仅仅是多核)系统,否则不要期望多线程能够显着提高速度。

    每个 CPU 启动一个线程。在这些线程之间静态分配目标数据。并为每个线程提供相同的输入数据。这允许更好地使用 NUMA 架构。并避免线程同步的额外内存流量。

    如果是单 CPU 系统,只使用一个线程访问目标数据。

    可能,在 CPU 中增加内核的唯一实际用途是使用额外的线程加载输入数据。

    一个明显的优化是将目标数据对齐 16 个字节(以避免在访问单个数据元素时接触两个缓存行)。

    您可以使用 SIMD 来执行加法,或者允许编译器自动向量化您的代码,或者完全不优化此操作 - 没关系,与内存带宽问题相比,这算不了什么。

    至于输出数据污染缓存,MOVNTPS 在这里无济于事,但您可以使用 PREFETCHNTA 提前几个步骤预取输出数据元素,同时最大限度地减少缓存污染。我不知道它会提高性能还是降低性能。它避免了缓存垃圾,但保留了大部分未使用的缓存。

    【讨论】:

      猜你喜欢
      • 2019-03-27
      • 2017-05-11
      • 2023-03-18
      • 2011-08-24
      • 1970-01-01
      • 2016-08-26
      • 2016-08-27
      • 2014-06-03
      • 1970-01-01
      相关资源
      最近更新 更多