SSE2 8x8 字节矩阵转置代码在 Haswell+ 上的速度是 ivy Bridge 上的两倍

2023-12-29

我使用大量 punpckl、pextrd 和pinsrd 编写了代码,它们旋转 8x8 字节矩阵,作为使用循环平铺旋转黑白图像的较大例程的一部分。

我用 IACA 对其进行了分析,看看是否值得执行 AVX2 例程,令人惊讶的是,代码在 Haswell/Skylake 上的速度几乎是 IVB 上的两倍(IVB:19.8,HSW,SKL:36 个周期)。 (IVB+HSW 使用 iaca 2.1,skl 使用 3.0,但 hsw 给出与 3.0 相同的数字)

从 IACA 输出来看,我猜差异在于 IVB 使用端口 1 和 5 来执行上述指令,而 haswell 仅使用端口 5。

我用谷歌搜索了一下,但找不到解释。旧版 SSE 的 haswell 是否真的较慢,或者我只是遇到了一些极端的极端情况?任何躲避此子弹的建议(AVX2 除外,这是一个已知的选项,但由于将工具链更新到新版本而暂时推迟)

也欢迎一般性评论或建议的改进。

   // r8 and r9 are #bytes to go to the next line in resp. src and dest 
   // r12=3*r8 r13=3*r9  
  // load 8x8 bytes into 4 registers, bytes interleaved.
  movq xmm1,[rcx]
  movq xmm4,[rcx+2*r8]
  PUNPCKLBW xmm1,xmm4   // 0 2 0 2 0 2
  movq xmm7,[rcx+r8]
  movq xmm6,[rcx+r12]
  PUNPCKLBW xmm7,xmm6   // 1 3 1 3 1 3

  movdqa xmm2,xmm1
  punpcklbw xmm1,xmm7   // 0 1 2 3 0 1 2 3 in xmm1:xmm2
  punpckhbw xmm2,xmm7   
  lea rcx,[rcx+4*r8]

  // same for 4..7

  movq xmm3,[rcx]
  movq xmm5,[rcx+2*r8]
  PUNPCKLBW xmm3,xmm5
  movq xmm7,[rcx+r8]
  movq xmm8,[rcx+r12]
  PUNPCKLBW xmm7,xmm8

  movdqa xmm4,xmm3
  punpcklbw xmm3,xmm7
  punpckhbw xmm4,xmm7

  // now we join one "low" dword from XMM1:xmm2 with one "high" dword
  // from XMM3:xmm4

  movdqa  xmm5,xmm1
  pextrd  eax,xmm3,0
  pinsrd  xmm5,eax,1
  movq    [rdx],xmm5

  movdqa  xmm5,xmm3
  pextrd  eax,xmm1,1
  pinsrd  xmm5,eax,0
  movq    [rdx+r9],xmm5

  movdqa  xmm5,xmm1
  pextrd  eax,xmm3,2
  pinsrd  xmm5,eax,3
  MOVHLPS  xmm6,xmm5
  movq    [rdx+2*r9],xmm6

  movdqa  xmm5,xmm3
  pextrd  eax,xmm1,3
  pinsrd  xmm5,eax,2
  MOVHLPS  xmm6,xmm5
  movq    [rdx+r13],xmm6

  lea     rdx,[rdx+4*r9]

  movdqa  xmm5,xmm2
  pextrd  eax,xmm4,0
  pinsrd  xmm5,eax,1
  movq    [rdx],xmm5

  movdqa  xmm5,xmm4
  pextrd  eax,xmm2,1
  pinsrd  xmm5,eax,0
  movq    [rdx+r9],xmm5

  movdqa  xmm5,xmm2
  pextrd  eax,xmm4,2
  pinsrd  xmm5,eax,3
  MOVHLPS  xmm6,xmm5
  movq    [rdx+2*r9],xmm6

  movdqa  xmm5,xmm4
  pextrd  eax,xmm2,3
  pinsrd  xmm5,eax,2
  MOVHLPS  xmm6,xmm5
  movq    [rdx+r13],xmm6

  lea     rdx,[rdx+4*r9]

目的: 它实际上是出于图像视觉目的旋转来自相机的图像。在某些(较重的)应用程序中,旋转被推迟并仅显示(opengl),在某些应用程序中,旋转输入然后适应算法更容易。

更新的代码:我发布了一些最终代码here http://www.stack.nl/~marcov/rot8x8.txt加速很大程度上取决于输入的大小。与使用 32x32 平铺的循环 HLL 代码相比,在小图像上较大,但在较大图像上仍然是两倍。 (与链接的 asm 代码相同的算法)


TL:DR: use punpckl/hdq节省大量的洗牌次数在双字重新排列步骤中,与中的转置代码完全相同更好的 8x8 字节矩阵转置 SSE? https://stackoverflow.com/questions/42162270/a-better-8x8-bytes-matrix-transpose-with-sse/42222490#42222490

您的内存布局需要分别存储每个向量结果的低/高 8 字节,您可以使用以下命令高效地做到这一点movq [rdx], xmm / movhps [rdx+r9], xmm.


Haswell/Skylake 上的代码几乎是 IVB 上的两倍

您的代码在洗牌吞吐量上存在严重瓶颈。

Haswell 只有 1 个 shuffle 执行单元,位于端口 5。SnB/IvB 有 2 个整数 shuffle 单元(但仍然只有一个 FP 洗牌单元)。看Agner Fog 的指令表和优化指南/微架构指南 http://agner.org/optimize/.

我看到你已经发现了大卫·坎特的出色表现Haswell 微架构撰写 https://www.realworldtech.com/haswell-cpu/4/.

对于这样的代码,shuffle(或一般为 port5)吞吐量很容易出现瓶颈,并且使用 AVX / AVX2 时情况通常会变得更糟,因为许多 shuffle 仅在通道内。用于 128 位操作的 AVX 可能会有所帮助,但我认为您不会从改组为 256b 向量然后将它们再次改组为 64 位块中获得任何好处。如果您可以加载或存储连续的 256b 块,那么值得尝试。


即使在我们考虑重大更改之前,您也有一些简单的错过优化:

  MOVHLPS  xmm6,xmm5
  movq    [rdx+r13],xmm6

应该movhps [rdx+r13],xmm6。在 Sandybridge 和 Haswell 上,movhps是一个纯存储 uop,不需要洗牌 uop。

pextrd eax,xmm3,0总是比movd eax, xmm3;从不使用pextrd立即数为 0。(此外,pextrd直接记忆可能是一个胜利。你可以做一个64位的movq然后用 32 位覆盖一半pextrd。然后,您可能会成为商店吞吐量的瓶颈。另外,在桑迪布里奇,索引寻址模式不会保持微融合 http://stackoverflow.com/questions/26046634/micro-fusion-and-addressing-modes,因此更多的存储会损害您的总 uop 吞吐量。但 Haswell 对于存储没有这个问题,仅对于某些索引加载(取决于指令)。)如果您在某些地方使用更多的存储,而在其他地方使用更多的洗牌,则可以为单寄存器寻址模式使用更多的存储。


源和目标格式不是图像处理的自由度。

取决于你在做什么。 x264(开源 h.264 视频编码器)将 8x8 块复制到连续的在重复使用它们之前先进行缓冲,因此行之间的步幅是一个汇编时间常数。

这可以节省在寄存器中传递一步并做像您正在做的事情[rcx+2*r8] / [rcx+r8]。它还允许您用一行加载两行movdqa。它为您提供了良好的内存局部性来访问 8x8 块。

当然,如果这种轮换是这样的话,花时间复制进/出这种格式可能不是一个胜利。all您正在使用 8x8 像素块进行操作。 FFmpeg 的 h.264 解码器(使用许多与 x264 相同的 asm 原语)不使用此功能,但我不知道这是因为没有人费心移植更新的 x264 asm 或者只是不值得。


  // now we join one "low" dword from XMM1:xmm2 with one "high" dword
  // from XMM3:xmm4

从整数中提取/插入的效率不是很高;pinsrd and pextrd每个微指令有 2 个微指令,其中一个微指令是随机指令。您甚至可能会领先于当前的代码,只需使用pextrd以 32 位块的形式存储到内存中。

还可以考虑使用 SSSE3pshufb它可以将您的数据放在任何需要的地方,并将其他元素归零。这可以让您为合并做好准备por。 (你可能会使用pshufb代替punpcklbw).


另一种选择是使用shufps合并两个来源的数据。之后您可能需要再次洗牌。Or use punpckldq.

// "low" dwords from XMM1:xmm2
//  high dwords from XMM3:xmm4

;  xmm1:  [ a b c d ]   xmm2: [ e f g h ]
;  xmm3:  [ i j k l ]   xmm4: [ m n o p ]

; want: [ a i b j ] / [ c k d l ] / ... I think.

;; original: replace these with
;  movdqa  xmm5,xmm1     ; xmm5 = [ a b c d ]
;  pextrd  eax,xmm3,0    ; eax = i
;  pinsrd  xmm5,eax,1    ; xmm5 = [ a i ... ]
;  movq    [rdx],xmm5

;  movdqa  xmm5,xmm3       ; xmm5 = [ i j k l ]
;  pextrd  eax,xmm1,1      ; eax = b
;  pinsrd  xmm5,eax,0      ; xmm5 = [ b j ... ]
;  movq    [rdx+r9],xmm5

替换为:

   movdqa    xmm5, xmm1
   punpckldq xmm5, xmm3     ; xmm5 = [ a i b j ]
   movq     [rdx], xmm5
   movhps   [rdx+r9], xmm5  ; still a pure store, doesn't cost a shuffle

因此,我们将 4 个 shuffle uops 替换为 1 个,并将总 uop 计数从 12 个融合域 uops (Haswell) 减少到 4 个。(或者在 Sandybridge 上,从 13 个减少到 5 个,因为索引存储不会保持微融合) 。

Use punpckhdq for [ c k d l ],它甚至更好,因为我们正在替换movhlps以及。

 ;  movdqa  xmm5,xmm1       ; xmm5 = [ a b c d ]
 ; pextrd  eax,xmm3,2      ; eax = k
 ; pinsrd  xmm5,eax,3      ; xmm5 = [ a b c k ]
 ; MOVHLPS  xmm6,xmm5      ; xmm6 = [ c k ? ? ]  (false dependency on old xmm6)
 ; movq   [rdx+2*r9],xmm6

然后解压 xmm2 和 xmm4 的 lo/hi。

使用 AVX 或 AVX2 可以让您跳过movdqa,因为您可以解压到新的目标寄存器中,而不是复制+销毁。

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

SSE2 8x8 字节矩阵转置代码在 Haswell+ 上的速度是 ivy Bridge 上的两倍 的相关文章

  • 应用程序中 GC 长时间暂停

    我当前运行的应用程序需要最大堆大小为 16GB 目前我使用以下标志来处理垃圾收集 XX UseParNewGC XX UseConcMarkSweepGC XX CMSInitiatingOccupancyFraction 50 XX Di
  • 嵌入式系统:使用汇编语言时的内存布局

    根据我的理解 嵌入式系统运行机器代码 有多种方法可以生成此代码 一种是用 C 等高级语言编写程序 然后使用编译器获得这样的代码 另一种方法是用汇编语言为该嵌入式系统编写指令 并使用汇编器将其转换为机器代码 现在我们得到了加载到系统并执行的机
  • 您使用什么来通过其自定义协议来测试(功能/负载/压力)您的网络服务?

    我最近创建了一个回合制游戏服务器 可以接受数十万个并发客户端连接 长话短说 Linux 上的 epoll 通信基于简单 定制 基于线路的协议 该服务器允许客户端连接 寻找游戏比赛中的其他玩家 玩所述游戏 发送动作 聊天消息等 并在游戏结束时
  • 方法与管道

    在 Angular 应用程序中的模板插值中使用管道和方法有区别吗 例如 h1 name toLowerCase h1 vs h1 name lowercase h1 就性能而言 是有真正的收获还是只是个人喜好 我知道调用模板中的方法通常会降
  • n的渐近增长选择下限(n/2)

    如何找到 n select Floor n 2 的渐近增长 我试过 使用扩展并得到它等于 n n 1 floor n 2 1 n floor n 2 知道我该如何从那里去吗 感谢任何帮助 更喜欢提示而不是答案 我同意上面的答案 但想提供更多
  • 如何找到 Ruby 应用程序中的性能瓶颈?

    我编写了一个 Ruby 应用程序 它可以解析来自不同格式 html xml 和 csv 文件的源的大量数据 如何找出代码的哪些区域花费时间最长 有没有关于如何提高 Ruby 应用程序性能的好资源 或者您是否有始终遵循的性能编码标准 例如 您
  • 在 qemu 中将扇区加载到 RAM

    我编写了一个简单的程序 将扇区 扇区编号 2 加载到 RAM 但什么也没打印 首先 我尝试了以下引导扇区代码 org 0x7c00 mov ax 0x1000 ES BX 1000 0000 mov es ax mov bx 0x00 Lo
  • 如何在大空间尺度上加速A*算法?

    From http ccl northwestern edu netlogo models community Astardemo http ccl northwestern edu netlogo models community Ast
  • 将 numpy 数组写入文本文件的速度

    我需要将一个非常 高 的两列数组写入文本文件 而且速度非常慢 我发现如果我将数组改造成更宽的数组 写入速度会快得多 例如 import time import numpy as np dataMat1 np random rand 1000
  • NASM:如何正确访问SSD驱动器?

    我需要使用 NASM 16 位代码访问 SSD 驱动器 访问普通硬盘时 需要设置寄存器AX DX CX来选择柱面 磁道 扇区 扇区数 AH 选择读扇区功能 DL 选择驱动器号 CH 选择气缸 DH 选择磁盘上的一侧 CL 选择步入正轨的部门
  • 如何加速我的 Perl 程序?

    这确实是两个问题 但它们非常相似 为了简单起见 我想我应该把它们放在一起 Firstly 给定一个已建立的 Perl 项目 除了简单的代码优化之外 还有哪些不错的方法可以加速它 Secondly 用Perl从头开始编写程序时 有哪些好的方法
  • 如何在汇编中使用 ReadString?

    mov edx offset Prompt1 call WriteString mov ecx 32 mov edx offset String1 call ReadString 现在 我该如何访问String1 如何将其移入寄存器以便对其
  • 在 R 中,为什么 sum 与其他方法(例如 cumsum)相比如此慢?

    我正在尝试实现一个需要非常快的函数 主要是因为它一遍又一遍地处理巨大的数据帧 R 总是让我感到困惑 为什么它有时有点慢 而有时又慢得离谱 不幸的是 它从来都不快 不管怎样 我一直认为 如果可能的话 当以某种方式推入 apply sapply
  • 为什么 GCC 在堆栈上压入额外的返回地址?

    我目前正在学习汇编的基础知识 在查看 GCC 6 1 1 生成的指令时遇到了一些奇怪的情况 这是来源 include
  • 每个存储桶的最大沙发底座视图数

    假设存储桶中有大量数据 gt 100GB gt 100M 文档 gt 12 种文档类型 并且假设每个视图仅适用于一种文档类型 那么每个存储桶有多少视图就太多了 或者以另一种方式问 在什么时候应该将某些文档类型拆分到单独的存储桶中 以节省处理
  • x86:寄存器操作为内存内容和内存地址?

    寄存器 gt 内存地址 gt 内存内容 内存地址 gt 内存内容 上面的模型正确吗 而且 如果是的话 你能建议我是否认为正确吗 movl eax ebx gt 它将 eax 的内存地址移动到 ebx 这也会导致内容移动 movl eax e
  • 用 OpenCL C 编写快速线性系统求解器

    我正在编写一个 OpenCL 内核 它将涉及求解线性系统 目前我的内核太慢了 提高线性系统部分的性能似乎是一个不错的起点 我还应该注意 我并没有尝试使我的线性求解器并行 我正在研究的问题在宏观层面上已经是令人尴尬的并行 以下是我编写的 C
  • _MM_TRANSPOSE4_PS 在 GCC 中导致编译器错误?

    我第一次在 GCC 而不是 MSVC 中编译我的数学库 并经历了所有的小错误 我遇到了一个根本没有意义的错误 Line 284 error lvalue required as left operand of assignment 284号
  • 如何调试性能问题/优化您的流星应用程序

    我刚刚将 Meteor 应用程序部署到 Digital Ocean 上的生产服务器上 我注意到 对于大约 7500 个文档 完全获取对象 有选择地仅获取 3 个字段 并填充自动完成数据大约需要 3 5 秒 我相信对于如此数量的数据来说 它应
  • 为什么在 this 方法中添加 If 语句会大大降低速度?

    我在中遇到过这个回答另一个问题 https stackoverflow com questions 12233594 faster way to apply alpha to a jpeg in an android app 我试图诊断哪些

随机推荐

  • 内置帮助程序将 User.Identity.Name 解析为 Domain\Username

    是否有任何内置实用程序或帮助程序可以解析HttpContext Current User Identity Name e g domain user单独获取域名 如果存在 和用户 或者还有其他类可以这样做吗 我知道打电话很容易String
  • Vue:处理多个 API 调用的最佳实践

    我发现自己在 vuex 操作中进行了多个 API 调用 这让我想知道处理这种情况的最佳方法是什么 多个 API 调用的最佳实践 让我们从我的代码开始 我有一个操作 从不同的 API 端点 后端为 Laravel 收集所有帖子和所有帖子类别
  • Spring MVC 5 中的默认消息转换器

    我试图理解为什么我的spring v 5 0 4 RELEASE无法正确加载默认消息转换器 我从 servlet xml 中删除了所有声明 我希望找到正确加载的所有默认转换器AbstractMessageConverterMethodPro
  • GORM 中有“not in”的对应词吗?

    这可以在 createCriteria 中转换吗 SELECT FROM node WHERE node type act AND nid NOT IN SELECT nid FROM snbr act community LIMIT 10
  • 将自定义 SQL 函数(类似于 date_trunc)注释为 Django ORM 查询集

    我在用timescaledb这基本上只是一个扩展postgres 它带有一个名为的 SQL 函数time bucket https docs timescale com latest api api timescaledb time buc
  • 系统通知 Phonegap (Android)

    我最近刚刚开始做一些 Android 应用程序开发 基于 Phonegap 由于我基于 Web 的背景 PHP MySQL 和 jQuery mobile 第一次测试很有希望 但下一个级别对我来说有点太多了 我正在尝试集成这个插件 http
  • MemFree 和 MemAvailable 之间的区别

    使用 Ubuntu 14 02 并运行命令 cat proc meminfo 我得到以下信息 MemTotal 1007796 kB MemFree 64248 kB MemAvailable 64876 kB 我想知道 MemFree 和
  • 在实体框架中播种多对多数据

    我首先使用代码 并且书名和类别之间存在多对多关系 在开发过程中播种数据的最佳方法是什么 如果我在同一类别中添加两本书 种子逻辑会将该类别两次添加到类别表中 我可以将类别单独添加到类别表中 但是如何指定图书关键字集合中的现有类别记录 我相信这
  • 如何使用 React-native-element 复选框和 FlatList React Native 处理从 json 获取的复选框?

    我正在尝试创建动态复选框 其名称从 json 获取 这个问题 https github com react native training react native elements issues 603看起来与我需要的相同 但如果没有代码
  • 使用@IdClass存储具有复合主键的实体,但无法持久化

    我的 id 类如下 public class EmployeeId implements Serializable public EmployeeId public EmployeeId Integer id String country
  • 未知的 $rootElementProvider:Qunit + angularjs 集成 [已关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 当我尝试在单元测试 qunit 中注入 location 服务时 出现错误 未知 rootElementProvider
  • 如何获取复制文件进度

    我有代码 但不知道如何找到复制文件进度 我应该怎么做才能接收文件复制进度 public sealed class FileRoutines public static void CopyFile FileInfo source FileIn
  • 用户界面中的文本大写

    例如 我想问您是否有理由将菜单中的所有项目等大写 文件 gt 页面设置 编辑 gt 全选 帮助 gt 技术支持 为什么我不应该将这些项目标记为 文件 gt 页面设置 等 这种大写对我来说似乎是错误的 但我不是以英语为母语的人 所以我可能不会
  • 为什么 python 解码会替换编码字符串中的无效字节?

    尝试解码无效编码的 utf 8 html 页面会产生不同的结果 蟒蛇 火狐和铬 测试页面中的无效编码片段看起来像 PREFIX xe3 xabSUFFIX gt gt gt fragment PREFIX xe3 xabSUFFIX gt
  • Svelte 3、async onMount 还是有效的替代方案?

    我需要的是使用async await在斯韦尔特onMount 或者也许你可以建议我哪里出了问题以及我可以使用什么替代方案 重现 到这里 https svelte dev repl 000ae69c0fe14d9483678d4ace8747
  • GPS android 卡尔曼滤波器 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 为了从 GPS 获得更准确的数据 建议使用卡尔曼滤波器 但我找不到任何关于如何为 GPS android 实现卡尔曼滤波器的教程 GPS
  • 重放上一条成功消息中的 Kafka 主题

    使用 Spring Cloud Stream 上通道的标准配置 消息会重试 3 次 然后被跳过 如果以下消息处理成功完成 则提交偏移量 这意味着在短暂的异常情况下消息可能会丢失 是否可以更改此行为 以便通道卡在失败消息上 直到瞬态条件得到修
  • Rails App 中的 JS 仅在第一次加载

    我在应用程序主页上的某些链接上使用了淡入淡出效果 这是通过一些 JQuery 来完成的 javascripts pages js document ready function home tile text hide removeClass
  • 检查数组中的整数

    我在大学里有一个练习要做 其中一部分包括制作一副纸牌 然后必须将其洗牌 我将卡片放在一个数组中 未洗牌 并且想要将它们洗牌并将它们推入自制的堆栈中 我可以从中弹出卡片来处理它们 我的问题是我想检查我生成的随机数 代表数组中的一张卡 是否已经
  • SSE2 8x8 字节矩阵转置代码在 Haswell+ 上的速度是 ivy Bridge 上的两倍

    我使用大量 punpckl pextrd 和pinsrd 编写了代码 它们旋转 8x8 字节矩阵 作为使用循环平铺旋转黑白图像的较大例程的一部分 我用 IACA 对其进行了分析 看看是否值得执行 AVX2 例程 令人惊讶的是 代码在 Has