苹果加速框架:对向量进行缩放和归一化

3

我能在Accelerate.framework中使用哪些函数来对向量进行标量缩放和归一化?我在文档中找到了一个可能适用于缩放的函数,但我对它的操作感到困惑。

vDSP_vsma
Vector scalar multiply and vector add; single precision.

void vDSP_vsma (
   const float *__vDSP_A,
   vDSP_Stride __vDSP_I,
   const float *__vDSP_B,
   const float *__vDSP_C,
   vDSP_Stride __vDSP_K,
   float *__vDSP_D,
   vDSP_Stride __vDSP_L,
   vDSP_Length __vDSP_N
);
1个回答

9

规范化向量的最简单方法是:

int n = 3;
float v[3] = {1, 2, 3};
cblas_sscal(n, 1.0 / cblas_snrm2(n, v, 1), v, 1);

你需要:
#include <cblas.h>

或者

#include <vblas.h>

(或两者兼有)。请注意,当它们作用于向量时,其中几个函数位于“矩阵”部分。

如果您想使用vDSP函数,请参见“矢量标量除法”部分。您可以执行以下几个操作:

  • vDSP_dotpr()sqrt()vDSP_vsdiv()
  • vDSP_dotpr()vrsqrte_f32()vDSP_vsmul()(不过,vrsqrte_f32()是NEON GCC内置的,因此您需要检查是否编译为armv7)。
  • vDSP_rmsqv(),乘以sqrt(n),然后vDSP_vsdiv()

之所以没有矢量归一化函数,是因为vDSP中的“矢量”意味着“同时处理很多东西”(最多约4096/8192),而不是线性代数中的“矢量”。对于一个1024元素的向量来说,归一化几乎没有意义,而快速归一化3元素向量的函数并不会显著提高应用程序的速度,这就是为什么没有这样一个函数。

vDSP的预期用途更像是将102423元素向量归一化。我可以找到几种方法来实现这一点:

  • 使用vDSP_vdist()获取长度向量,然后使用vDSP_vdiv()。对于长度大于2的向量,您必须多次使用vDSP_vdist()
  • 使用vDSP_vsq()对所有输入进行平方,使用vDSP_vadd()多次添加所有输入,相当于vDSP_vsqrt()vDSP_vrsqrt(),然后使用vDSP_vmul()vDSP_vdiv()。编写等效的vDSP_vsqrt()vDSP_vrsqrt()应该不太难。
  • 各种假装输入是复杂向量的方式。不太可能更快。

当然,如果您没有需要归一化的1024个向量,请不要过度复杂化问题。

注:

  1. 我不使用“二维向量”和“三维向量”,以避免与相对论中的“四维向量”混淆。
  2. 一个好的选择是使你的L1数据缓存几乎填满。这并不困难;它们在过去十年左右基本上保持不变,为32K(它们可能在超线程CPU的虚拟核之间共享,并且一些旧的/便宜的处理器可能为16K),因此,在浮点数的原地操作中,你最多应该做到约8192。你可能需要减去一些堆栈空间,如果你正在进行几个连续的操作,你可能想把它都保存在缓存中;1024或2048似乎相当明智,任何更多的操作可能会带来递减收益。如果你在意,可以测量性能...

好的,谢谢你提供的信息。最近我看了一个苹果的视频,他们说对于向量中的成千上万个元素进行归一化处理是非常有效的,速度比正常代码快10倍。他们还使用了和我一样的三元素向量,速度比正常代码快2倍。所以我想我会跳过归一化处理。感谢你的帮助。 - Justin Meiners
1
他们进行了一个三元点积;那只是标准化中的一步。 - tc.
1
@Justin:如果这是你真正想要的功能,请提交一个错误报告来请求它。但是,请注意,没有任何库函数能够超越对短向量进行良好编写的内联操作。 - Stephen Canon
1
@Stephen:当然不是,但是你的“写得好”的内联函数必须使用“NEON”指令/向量类型,再加上(如果你不介意略微降低精度)“快速倒数平方根估算”,这比1/sqrt(x)要快得多。 - tc.
@Stephen:vrsqrte(我提到的 vrsqrte_f32() 内置函数可能使用它)可能执行相同可怕的黑客技巧。 - tc.
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接