动态分配 SIMD 向量数组是不是安全?
Posted
技术标签:
【中文标题】动态分配 SIMD 向量数组是不是安全?【英文标题】:Is it safe to dynamically allocate an array of SIMD vectors?动态分配 SIMD 向量数组是否安全? 【发布时间】:2021-06-21 02:01:04 【问题描述】:在普通 C++ 中,我们可以使用标准库函数 malloc
或 new
关键字动态分配浮点数组。
当我们想到 SIMD 向量时,像 float32x4_t
(对于 ARM neon)这样的编译器扩展,像这样动态分配这样的 SIMD 向量数组是否安全:
uint32_t number_req = 32;
float32x4_t *simd_arr = (float32x4_t *)malloc(sizeof(float32x4_t) * number_req);
我正在尝试限制代码中加载存储指令的数量。 如果以上不是合法的方法,那么实施它的正确方法是什么? 每一个帮助将不胜感激! 提前非常感谢您!
【问题讨论】:
你可以这样做(但需要注意的是它必须正确对齐,但我不会专注于此)。但是,如果您的数组真的很大,它将不适合寄存器,并且您不会看到任何性能提升。但这也适用于静态已知大小的非常大的数组。当您编写一个对此类数组进行操作的函数时,您应该使用预取并检查生成的程序集(和配置文件)以确定您的代码是否符合您的预期。 一般来说,动态分配(数组)类型(如float32x4_t
)是安全的,只要您不摆弄对齐。在 C++ 中,通常最好使用 new
表达式而不是 malloc()
,除非相关文档(例如,对于您的编译器,或者 - 在您的情况下 - ARM 开发指南)另有说明。这是否会影响加载和存储指令是您需要以另一种方式检查的内容(例如,通过编译器检查汇编器输出)。
【参考方案1】:
我正在尝试限制代码中加载存储指令的数量。
以这种方式减少代码中加载/存储 intrinsics 的数量无济于事。
取消引用 float32x4_t*
完全等同于加载或存储内部函数,实际上可能是如何实现 1 向量对齐加载内部函数的。
何时可以将向量类型保存在向量寄存器中取决于编译器,就像将int
对象保存在普通整数寄存器中一样。
加载/存储内部函数主要用于向编译器传达对齐信息,并使其对类型感到满意;查看编译器生成的 asm 以了解实际情况。
【讨论】:
感谢您的回复!目前,我有一个正常的动态分配的float
数组并使用内部函数我必须调用vld1q_f32(...)
(从浮点数组加载float32x4_t,对于ARM NEON)每次执行操作,比如向量点积,然后我必须使用 vst1q_f32()
将结果存储到结果数组(传统 C++ float
数组)。有什么办法可以限制这种不断调用与加载/存储操作相关的函数!?
@VivekanandV:当前架构上唯一可行的解决方案是在每次遍历数组时做更多的工作,而数据已经存在于寄存器(局部变量)中。例如如果您想要三个向量之间的所有点积组合,您将进行一次加载每个元素一次的传递,而不是 3 个单独的点积的 3 次传递。或与生成它的通行证结合使用。每次加载/存储(或每次将数据拉入 L1d 缓存)的 ALU 工作量称为“计算强度”,与内存带宽相比,增加它是具有强大 ALU 的 CPU 性能的关键。
(更不用说占用管道槽的实际加载/存储)。基本上,在我们拥有可随内存扩展的计算内存或类似的 ALU 之前,Von Neumann Bottleneck 并不是你可以通过玩 C 类型的技巧来避免的。就像arr[i] += 1;
将要求编译器发出加载和存储指令一样,使用内部函数也会做同样的事情。
@PeterCordes oof没看到手臂:/我的错!删除我的 cmets,因为它们与此 Q/A 完全无关:***.com/questions/52147378/…
@PeterCordes 说的是here【参考方案2】:
您可能需要aligned_alloc
,它是在 C11 中引入的,以替代 malloc
用于此类情况。
【讨论】:
【参考方案3】:或 memalign() 在所有 Linux libc 库中都可用
【讨论】:
以上是关于动态分配 SIMD 向量数组是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章