c - 3D vector 加速欧氏距离

标签 c matrix 3d euclidean-distance vdsp

我需要执行一个非常常见且简单的矩阵运算。
然而我需要它快,非常快......

我已经在考虑多线程实现,但现在我只想看看在单个处理器上实现它的速度有多快。

矩阵运算如下:

我正在计算点 vector (A) 和引用点 (B) 之间的欧几里德距离。
这些点位于 3D 空间中,每个点都有一组 X、Y 和 Z 坐标。
因此,点 vector 由三个 float 组来描述,其中保存每个点的 X、Y、Z 坐标。
输出是另一个长度为 N 的 vector ,保存数组中每个点与引用点之间的距离。

三个 XYZ 数组排列为 Nx3 矩阵的列。

x[0]      y[0]      z[0]
x[1]      y[1]      z[1]
x[2]      y[2]      z[2]
x[3]      y[3]      z[3]
.         .         .
.         .         .
.         .         .
x[N-1]    y[N-1]    z[N-1]

在内存中,矩阵按行主序排列为一维数组,依次包含 X、Y 和 Z 列的值。

x[0], x[1], x[2], x[3] . . . x[N-1], y[0], y[1], y[2], y[3] . . . y[N-1], z[0], z[1], z[2], z[3] . . . z[N-1]

整个事情有点复杂,因为我们需要在求平方根之前向矩阵的每个成员添加一个标量。

以下是简单 C 代码中的例程:

void calculateDistances3D(float *matrix, float Bx, float By, float Bz, float scalar, float *distances, int N)
{
    float *Ax = matrix;
    float *Ay = Ax + N;
    float *Az = Ay + N;
    int i;

    for (i = 0; i < N; i++) {

        float dx = Ax[i] - Bx;
        float dy = Ay[i] - By;
        float dz = Az[i] - Bz;

        float dx2 = dx * dx;
        float dy2 = dy * dy;
        float dz2 = dz * dz;

        float squaredDistance = dx2 + dy2 + dz2;
        float squaredDistancePlusScalar = squaredDistance + scalar;

        distances[i] = sqrt(squaredDistancePlusScalar);
    }
}

…这是简单的 Accelerate 实现(使用 vDSP 和 VecLib):
(请注意,所有处理都是就地执行的)

void calculateDistances3D_vDSP(float *matrix, float Bx, float By, float Bz, float scalar, float *distances, int N)
{
    float *Ax = matrix;
    float *Ay = Ax + N;
    float *Az = Ay + N;

    // for each point in the array take the difference with the reference point
    Bx = -Bx;
    By = -By;
    Bz = -Bz;
    vDSP_vsadd(Ax, 1, &Bx, Ax, 1, N);
    vDSP_vsadd(Ay, 1, &By, Ay, 1, N);
    vDSP_vsadd(Az, 1, &Bz, Az, 1, N);

    // square each coordinate
    vDSP_vsq(Ax, 1, Ax, 1, N);
    vDSP_vsq(Ay, 1, Ay, 1, N);
    vDSP_vsq(Az, 1, Az, 1, N);

    // reduce XYZ columns to a single column in Ax (reduction by summation)
    vDSP_vadd(Ax, 1, Ay, 1, Ax, 1, N);
    vDSP_vadd(Ax, 1, Az, 1, Ax, 1, N);

    // add scalar
    vDSP_vsadd(Ax, 1, &scalar, Ax, 1, N);

    // take sqrt
    vvsqrtf(distances, Ax, &N);
}

在 vDSP 库中,唯一可用于计算 vector 之间距离的函数是:

vDSP_vdist()
vDSP_distancesq()
vDSP_vpythg()

也许我遗漏了一些东西,但据我所知,它们都不支持计算 3D 距离所需的三个输入 vector 。

需要注意的几件事:
(1) 我不是在比较距离,所以我不能接受平方距离。我需要真实距离,因此计算平方根是绝对必要的。
(2) 如果您确实认为取倒数平方根可以使代码显着加快,那么这是一种可能。

我的印象是我没有充分利用 Accelerate 框架的潜力。
我正在寻找一些更智能、可能更简洁的东西,在更少的函数调用中完成更多的工作。以其他方式重新排列内存也可以,但我认为内存布局就这样就很好了。

我也愿意接受有关在英特尔处理器上工作的其他高度优化/矢量化线性代数库的建议。我不在乎它们是商业解决方案还是开源解决方案,只要它们的性能快速且强大即可。

问题是:Accelerate 框架中实现比上述更快的代码的最佳函数或函数组合是什么?

我正在运行 Mac OS X El Capitan 的 MacBook Pro(Retina,15 英寸,2014 年中)上的 Xcode 7 中进行开发。

谢谢。

最佳答案

试试这个。

  • 在 iMac 和 iPhone 上重复 1000 次时,N = 2^20 的性能提高约 20%
  • 此外,matrix 可以严格视为只读,因为仅操作距离
  • 它在代数上等价,但在数值上不等价于您的实现;预计输出差异约为 10^-6

在我看来,vDSP 对于进一步“有针对性”的优化而言级别太高。相反,您可以查看 Ray Wenderlich’s iOS Assembly Tutorial作为使用 NEON 的起点,并针对此特定问题编写您自己的 SIMD 指令。

根据问题的大小 N,您还可以通过使用 GPU 获得进一步的加速,例如使用 Metal .

void calculateDistances3D_vDSP(float *matrix, float Bx, float By, float Bz, float scalar, float *distances, int N)
{
  float *Ax = matrix;
  float *Ay = Ax + N;
  float *Az = Ay + N;

  float constants = Bx*Bx + By*By + Bz*Bz + scalar;

  Bx = -2.0f*Bx;
  By = -2.0f*By;
  Bz = -2.0f*Bz;

  vDSP_vsq(Ax, 1, distances, 1, N);                      // Ax^2
  vDSP_vma(Ay, 1, Ay, 1, distances, 1, distances, 1, N); // Ax^2 + Ay^2
  vDSP_vma(Az, 1, Az, 1, distances, 1, distances, 1, N); // Ax^2 + Ay^2 + Az^2
  vDSP_vsma(Ax, 1, &Bx, distances, 1, distances, 1, N);  // Ax^2 + Ay^2 + Az^2 - 2*Bx
  vDSP_vsma(Ay, 1, &By, distances, 1, distances, 1, N);  // Ax^2 + Ay^2 + Az^2 - 2*Bx - 2*By
  vDSP_vsma(Az, 1, &Bz, distances, 1, distances, 1, N);  // Ax^2 + Ay^2 + Az^2 - 2*Bx - 2*By - 2*Bz
  vDSP_vsadd(distances, 1, &constants, distances, 1, N); // ... + constants = (Ax-Bx)^2 + (Ay-By)^2 + (Az-Bz)^2 + scalar

  /*
  vDSP_vsq(Ax, 1, distances, 1, N);                      // Ax^2
  vDSP_vsma(Ax, 1, &Bx, distances, 1, distances, 1, N);  // Ax^2 - 2*Ax*Bx
  vDSP_vma(Ay, 1, Ay, 1, distances, 1, distances, 1, N); // Ax^2 - 2*Ax*Bx + Ay^2
  vDSP_vsma(Ay, 1, &By, distances, 1, distances, 1, N);  // Ax^2 - 2*Ax*Bx + Ay^2 - 2*Ay*By
  vDSP_vma(Az, 1, Az, 1, distances, 1, distances, 1, N); // Ax^2 - 2*Ax*Bx + Ay^2 - 2*Ay*By + Az^2
  vDSP_vsma(Az, 1, &Bz, distances, 1, distances, 1, N);  // Ax^2 - 2*Ax*Bx + Ay^2 - 2*Ay*By + Az^2 - 2*Az*Bz
  vDSP_vsadd(distances, 1, &constants, distances, 1, N); // ... + constants = (Ax-Bx)^2 + (Ay-By)^2 + (Az-Bz)^2 + scalar
   */

  // take sqrt
  vvsqrtf(distances, distances, &N);
}

关于c - 3D vector 加速欧氏距离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33744838/

相关文章:

c - 使用hiredis一次调用发送多条记录

c - 何时使用 waitpid() 来查找后台进程的状态

java - 矩阵 TFIDF 的降维

r - 跨多个列表应用操作

c++ - 为什么这个 XMVector3Transform 调用没有返回正确的结果?

c++ - 尝试从 C 加载 penlight lua 模块

iphone - 为什么执行选择器:withObject: methods can only take id?

c - 内存不足杀死

javascript - 应用透视矩阵顺序

c++ - 混合光的问题