动态分配 SIMD 向量数组是不是安全?

Posted

技术标签:

【中文标题】动态分配 SIMD 向量数组是不是安全?【英文标题】:Is it safe to dynamically allocate an array of SIMD vectors?动态分配 SIMD 向量数组是否安全? 【发布时间】:2021-06-21 02:01:04 【问题描述】:

在普通 C++ 中,我们可以使用标准库函数 mallocnew 关键字动态分配浮点数组。 当我们想到 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 向量数组是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章

C ++中动态分配的向量中的分段错误

将值从向量分配给动态数组(C++)

您可以在 C++ 中动态分配带有向量作为字段的类吗?

使用 new[] 安全地分配动态数组

彻底销毁动态分配对象的向量

动态分配包含要动态分配的向量的向量