跨 AVX 通道的最佳方式是什么?

2024-04-26

有些问题具有类似的标题,但我的问题涉及其他地方未涵盖的一个非常具体的用例。

我有 4 个 __128d 寄存器(x0、x1、x2、x3),我想将它们的内容重新组合在 5 个 __256d 寄存器(y0、y1、y2、y3、y4)中,以准备其他计算:

on entry:
    x0 contains {a0, a1}
    x1 contains {a2, a3}
    x2 contains {a4, a5}
    x3 contains {a6, a7}
on exit:
    y0 contains {a0, a1, a2, a3}
    y1 contains {a1, a2, a3, a4}
    y2 contains {a2, a3, a4, a5}
    y3 contains {a3, a4, a5, a6}
    y4 contains {a4, a5, a6, a7}

我下面的实现非常慢。有没有更好的办法?

y0 = _mm256_set_m128d(x1, x0);

__m128d lo = _mm_shuffle_pd(x0, x1, 1);
__m128d hi = _mm_shuffle_pd(x1, x2, 1);
y1 = _mm256_set_m128d(hi, lo);

y2 = _mm256_set_m128d(x2, x1);

lo = hi;
hi = _mm_shuffle_pd(x2, x3, 1);
y3 = _mm256_set_m128d(hi, lo);

y4 = _mm256_set_m128d(x3, x2);

通过寄存器中的输入,您可以通过 5 个随机指令来完成:

  • 3x vinsertf128通过连接 2 个 xmm 寄存器来创建 y0、y2 和 y4。
  • 2x vshufpd(车道内洗牌)在这些结果之间创建 y1 和 y3。

请注意,y0 和 y2 的低通道包含 a1 和 a2,它们是 y1 的低通道所需的元素。同样的洗牌也适用于高车道。

#include <immintrin.h>

void merge(__m128d x0, __m128d x1, __m128d x2, __m128d x3,
     __m256d *__restrict y0, __m256d *__restrict y1,
     __m256d *__restrict y2, __m256d *__restrict y3, __m256d *__restrict y4)
{
    *y0 = _mm256_set_m128d(x1, x0);
    *y2 = _mm256_set_m128d(x2, x1);
    *y4 = _mm256_set_m128d(x3, x2);

    // take the high element from the first vector, low element from the 2nd.
    *y1 = _mm256_shuffle_pd(*y0, *y2, 0b0101);
    *y3 = _mm256_shuffle_pd(*y2, *y4, 0b0101);
}

编译得很好(与 gcc 和 clang-O3 -march=haswell在戈德螺栓上 https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAM1QDsCBlZAQwBtMQBGAOgAYB2ACwBOAKyCATAGZhsucIAcpAFZdSrZrVDIApBIBCe/aQDOqAK7FkHAOR6peWslbmsAah1SAwngC2vxwJiR24ET2wdXgBBSKiAN1Q8dDdfTGJgTAgAfSzfTgkFZIAPXlI3HLyC4s4yivzCtyKJWtz64qlSWLdu7oqJUQA2ZIAqHOJMEyC8ZAI3AE9S8tz%2BobdRrPHJ4Jn5mq6epd8VkbGJqZ255sPjtdOt6dm5juvBk42z7cfBAEpYnX5DNEDsMFh4pAARcr%2BFZZEyYAitKoQIo1Rq8X5SQFRYGXMGQ3JHQaw%2BGIwrIq4ojFYnGCPFQwkDYkIypkorPJpUv5AnoAeh5bgIzAA1pgBQhRQg8MAEG5MOxUvQ3FRiKhfGLRVQ8MRJm44pgZiQyqxUAB3WXyzCK5Wq9VuCS0dDcfbdEGcOkEmEmBDmKhUdhZAAO6AgIMWIKuvAARrxODHOdyXU93dCiV6fX7MIHg%2BGyiDBGUozG454sf9wVz4olkql0pnUKxg3UqmiWizqq22o0rk2GmzOgnestXrd3vcLos%2BsP1ptzo89gOXqtpx8HvNu0Ol3dZ/NnpPN6Pt3Mfn8Ac61qDPPiU4y4cy2sjUSV4zEBz3ksbk75Yd7ff6g8jFhRMpOGfA43zcSVP2/dM/2DICu2A0CeldT9PRJNsIElI1UGfM9w1Q1N0PvJoykpEsKwOD9LwgvByIHSDqIJaDf0zf8SMaZ4QLo7FkKTRjryZUlgywtxjVwgc8wIm8iKRPsu1w/hwRsb51BAGxRBsUhaDU3hNNQNSvCMIw3DMSxrA8aROE0ggdOUlShRAQRBG4KRRFEYReGEAZhEENzRE4TgBlUmxBE07SbF00h9JsTSTBAUobIi5TSDgWAkDQXwAzwdgyAoCAMqynKQGABRmk1VgCDSOKIEjWzSEjRxmGIOY1Ks0gMoVAgAHlaFYFqktILBfE0YB2Dq/BxhmPA9TigbMCKfVzEq1rNMCOU6qmXwVpUmh6CYNgOB4AQRHEaR5DkJQNC0FAjIMdQ8EjOLIBU1AAwIPA6FmgBaLqpDcL6AHU2FYf7hqsMIIQQZgTBNOVWFiiwrEOlTWDUjStLq6KigUAYvoGWlgGQZA3AUbgJDcCBcEIEgLKkVEvFVQq0lpkC3EMgwjGs2zvnskACj4AZ8l4OmpBkfh%2BGEKR%2BGC0KMYG6LYvi0hEt0lTUsQFBGeytJyEoArteIFAruAfheFKcrKu1ShaoGhraCa/q2o6y1ut6/rIqGkaxoGib9Xema6vmxblpsNq1tRgbNu20hdsYFgxqOoQxEkGRzsUdQRpujm7tYB6nogF63o%2B2hvt%2B/6gdYEGvrB5AIfBKGYbhhGzOR4L0fCyKsZxvHaWcEa3H4Pg%2BApqmiGIFmygZzKDdpiRvjZ279C5pKedIByJEH0RSoUBR%2BE4PfBAGbzxBlsLMbUxWEu5lG1IkM/5Yv5Xr9IPVtWLxygA) to:

merge(double __vector(2), double __vector(2), double __vector(2), double __vector(2), double __vector(4)*, double __vector(4)*, double __vector(4)*, double __vector(4)*, double __vector(4)*):
    vinsertf128     ymm0, ymm0, xmm1, 0x1
    vinsertf128     ymm3, ymm2, xmm3, 0x1
    vinsertf128     ymm1, ymm1, xmm2, 0x1
    # vmovapd YMMWORD PTR [rdi], ymm0
    vshufpd ymm0, ymm0, ymm1, 5
    # vmovapd YMMWORD PTR [rdx], ymm1
    vshufpd ymm1, ymm1, ymm3, 5
    # vmovapd YMMWORD PTR [r8], ymm3
    # vmovapd YMMWORD PTR [rsi], ymm0
    # vmovapd YMMWORD PTR [rcx], ymm1
    # vzeroupper
    # ret

我注释掉了内联时会消失的存储和内容,因此我们实际上只有 5 个随机播放指令,而不是您问题中的代码的 9 个随机播放指令。 (也包含在 Godbolt 编译器资源管理器链接中)。

This is very擅长 AND,其中vinsertf128是超级便宜的(因为 256 位寄存器被实现为 2x 128 位一半,所以它只是一个 128 位副本,不需要特殊的随机播放端口。)256 位通道交叉随机播放在 AMD 上很慢,但在 - Lane 256 位随机播放,例如vshufpd只是 2 uop。

在 Intel 上,这相当不错,但具有 AVX 的主流 Intel CPU 对于 256 位或 FP shuffle 仅具有 1 个每时钟 shuffle 吞吐量。 (Sandybridge 和更早版本的整数 128 位随机播放具有更高的吞吐量,但 AVX2 CPU 放弃了额外的随机播放单元,而且它们对此没有任何帮助。)

因此,Intel CPU 根本无法利用指令级并行性,但总共只有 5 uops,这很好。这是可能的最小值,因为您需要 5 个结果。


但特别是如果周围的代码也在洗牌上遇到瓶颈,值得考虑仅包含 4 个存储和 5 个重叠向量加载的存储/重新加载策略。或者也许 2xvinsertf128构建y0 and y4,然后 2x 256 位存储 + 3 次重叠重新加载。这可以让无序执行仅使用依赖指令开始y0 or y4而存储转发停顿已解决 y1..3。

特别是如果您不太关心英特尔第一代 Sandybridge,其中未对齐的 256 位矢量加载效率较低。 (请注意,您需要使用以下命令进行编译gcc -mtune=haswell关闭-mavx256-split-unaligned-load如果您使用的是 GCC,则默认/sandybridge 调整。无论编译器如何,-march=native如果使二进制文件在编译它的机器上运行,以充分利用指令集并设置调整选项,这是一个好主意。)

但如果前端的 uop 总吞吐量更多是瓶颈所在,那么 shuffle 实现是最好的。

(See https://agner.org/optimize/ https://agner.org/optimize/以及其他性能链接x86 标签维基 https://stackoverflow.com/tags/x86/info有关性能调整的更多信息。还预测现代超标量处理器上的操作延迟需要考虑哪些因素以及如何手动计算它们? https://stackoverflow.com/questions/51607391/what-considerations-go-into-predicting-latency-for-operations-on-modern-supersca,但实际上 Agner Fog 的指南是一个更深入的指南,它解释了吞吐量与延迟的实际含义。)


我什至不需要保存,因为数据也已经在连续内存中可用。

然后,简单地加载 5 个重叠负载几乎肯定是您可以做的最有效的事情。

Haswell 每个时钟可以从 L1d 执行 2 次加载,或者当任何跨越缓存线边界时加载更少。因此,如果您可以将块对齐 64,那么它的效率非常高,根本不需要缓存行分割。缓存未命中速度很慢,但从 L1d 缓存重新加载热数据非常便宜,并且支持 AVX 的现代 CPU 通常具有高效的未对齐加载支持。

(就像我之前说的,如果使用 gcc 请确保使用-march=haswell or -mtune=haswell, 不只是-mavx,以避免 gcc 的-mavx256-split-unaligned-load.)

4 负载 + 1vshufpd(y0, y2) 可能是平衡负载端口压力与 ALU 压力的好方法,具体取决于周围代码中的瓶颈。如果周围代码的 shuffle 端口压力较低,甚至可以是 3 个加载 + 2 个 shuffle。


它们位于先前计算的寄存器中,需要加载它们。

如果先前的计算仍然在寄存器中包含源数据,则您可以首先完成 256 位加载,然后仅将其 128 位低半部分用于先前的计算。(XMM 寄存器是相应 YMM 寄存器的低 128,读取它们不会干扰上层通道,因此_mm256_castpd256_pd128编译为零 asm 指令。)

对 y0、y2 和 y4 进行 256 位加载,并将它们的低半部分用作 x0、x1 和 x2。 (稍后使用未对齐的负载或洗牌构造 y1 和 y3)。

只有 x3 还不是您想要的 256 位向量的低 128 位。

理想情况下,当您执行以下操作时,编译器已经注意到这种优化_mm_loadu_pd and a _mm256_loadu_pd来自同一个地址,但可能你需要通过这样做来手持它

__m256d y0 = _mm256_loadu_pd(base);
__m128d x0 = _mm256_castpd256_pd128(y0);

等等,以及提取 ALU 内在函数 (_mm256_extractf128_pd)或 128 位负载x3,取决于周围的代码。如果只需要一次,那么最好将其折叠到内存操作数中,以供任何指令使用。

潜在的缺点:128 位计算开始之前的延迟稍高,或者如果 256 位负载与 128 位负载不交叉的缓存行交叉,则需要几个周期。但如果您的数据块按 64 字节对齐,则不会发生这种情况。

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

跨 AVX 通道的最佳方式是什么? 的相关文章

随机推荐

  • Vba访问错误91

    我尝试运行这段代码 Public Sub Production UpdateStatus ByVal lngProductionId As Long ByVal NewProductionStatus As eProductionStatu
  • Heroku 中没有运行 Django 的 Web 进程

    我试图在heroku中部署我的应用程序 但是当我最终尝试在heroku中运行我的应用程序时 我在浏览器中看到了这样的内容 Application Error An error occurred in the application and
  • RSelenium:抓取加载缓慢的动态加载页面

    我不确定是否是因为我的网速很慢 但我正在尝试抓取一个在您向下滚动页面时加载信息的网站 我正在执行一个转到页面末尾的脚本 并等待 Selenium Chrome 服务器加载附加内容 服务器确实更新并加载新内容 因为我能够抓取最初不在页面上的信
  • 如何在python中获得逻辑正确的二进制移位

    正如标题所示 JavaScript 中有一个特定的运算符 gt gt gt 例如 在 JavaScript 中我们将得到以下结果 1000 gt gt gt 3 536870787 1000 gt gt 3 125 1000 gt gt g
  • Glass 的 SpeechRecognizer 权限不足错误

    我正在使用 GDK 先睹为快构建一个应用程序 但在沉浸式应用程序中无法进行语音识别 这是我的第一个安卓项目 我试着遵循这个 如何在 Android 手机中使用语音识别而不出现烦人的对话框 https stackoverflow com qu
  • SQL-计算数字列值的增加百分比

    我想根据 SQL 数据库中获得的值计算一些趋势 我特别感兴趣的是获取一列中值的增加百分比 如果值减少 则该值应该为负数 有人可以建议我该怎么做吗 我无法使用存储过程 Thanks 我的表有以下列 日期 数字 月份 数字 订单 数字 价格 实
  • 如何静态断言可变参数模板的参数包中的值?

    我正在创建一个可变参数模板 假设我有这样的东西 template
  • Android 媒体播放器使用服务

    我创建了一个MediaPlayer播放 mp3 文件的类 一切都很好 然后我的播放器在后台停止播放 我发现我的问题是我没有在服务中创建它 所以我开始阅读有关服务的信息 以了解如何创建一个服务并将其用于我的播放器 我的问题是 在服务和应用程序
  • Susy:根据屏幕尺寸改变列数

    在 Compass Sass 插件中 Susy http susy oddbird net 您可以在 base scss 文件中设置列数 对于桌面视图 我喜欢有 12 列 然而 对于移动视图来说 这列太多了 有没有办法更改移动显示的列数 我
  • 奇怪的 if 语句

    我发现这很奇怪if 别人代码中的语句 if variable 1 0 我不明白 它应该有两个 right 有人可以解释一下吗 条件式是一个按位运算符 https wiki python org moin BitwiseOperators比较
  • Rails 管理自定义:将 html 视图嵌入到仪表板中

    我们已从 Rails 管理起始页面删除了仪表板和历史记录 现在我们想嵌入一个谷歌分析页面 供用户查看 GA 的仪表板 该页面由一些简单的 HTML 组成 问题 如何在 Rails 管理仪表板中嵌入 html 页面 答案很简单 我需要创建以下
  • Windows 上 python 的长路径 - os.stat() 对于相对路径失败?

    我想访问 Windows 上的一些长 UNC 路径 我知道我需要使用 UNC 前缀 即 UNC 如果你逃脱了斜线 效果很好 os stat UNC server example com that has long path aaaaaaaa
  • 静态方法和实例方法的方法参考

    对于 Java 中的实例方法 我无法理解方法引用的概念 例如 在下面的示例中 编译器在列表行中给出错误 我看过 String toUpperCase 的例子 我对这一点感到困惑 1 String是类 toUpperCase是实例方法 Jav
  • 当用户单击控制台窗口时代码停止执行

    我有一个控制台应用程序 无需用户交互即可执行我的代码 如果用户有意或无意地在控制台窗口内单击 所有执行都会停止 这与从控制台窗口复制文本有关 应用程序再次开始执行的唯一方法是用户选择文本 然后右键单击控制台窗口 将其复制到剪贴板 要查看实际
  • 新用户创建后使用 Dropzone.js 上传,发送标头

    我正在使用一个很棒的插件 dropzone js dropzonejs com 来让我的网站在注册新用户时更加漂亮 基本上 用户填写一个表单 将几个图像放入 dropzone 然后单击 提交 这会触发一个 ajax 调用 将表单发布到 ph
  • Safari 扩展弹出窗口链接

    是否可以在 Safari 弹出窗口中使用链接 我已经做了我能想到的所有事情 但似乎向弹出窗口添加链接只会改变外观 并且不会导致任何可点击的内容 无论是使用 href 还是 onclick 您可以在 href 或 div 上添加 onclic
  • asp.net mvc 树路径的复杂路由

    我想知道如何定义这样的路由映射 TreePath Action Id TreeMap 是从数据库动态加载的 如下所示 Gallery GalleryA SubGalleryA View 3 您可以创建自定义路由处理程序来执行此操作 实际路线
  • 当我从 Django 应用程序“打印”它们时,它们会去哪里?

    我在 Linux 服务器上有一个 Django 应用程序 在其中一种观点中 某种形式print命令被执行 并打印一些字符串 我怎样才能知道打印的字符串是什么 是否有一些日志保存这些东西 输出应该在 django 启动的终端中 如果你不直接启
  • Cassandra 中的强一致性

    根据datastax文章 可以保证强一致性 如果 R W gt N 在哪里 R是读操作的一致性级别 W为写操作的一致性级别 N 是副本数 这里的强一致性是什么意思呢 这是否意味着 每次 从数据库给出查询响应时 响应将 始终 是最后更新的值
  • 跨 AVX 通道的最佳方式是什么?

    有些问题具有类似的标题 但我的问题涉及其他地方未涵盖的一个非常具体的用例 我有 4 个 128d 寄存器 x0 x1 x2 x3 我想将它们的内容重新组合在 5 个 256d 寄存器 y0 y1 y2 y3 y4 中 以准备其他计算 on