我怎么能将这个for循环矢量化?

时间:2014-12-29 14:37:11

标签: ios c arm vectorization neon

我有这个循环

void f1(unsigned char *data, unsigned int size) {
    unsigned int A[256] = {0u};      
    for (register unsigned int i = 0u; i < size; i++) {
        ++A[data[i]];
    }
   ...

有没有办法手动对其进行矢量化?

2 个答案:

答案 0 :(得分:4)

由于data[i]中的多个条目可能包含相同的值,因此我不知道如何将其简化为向量化,因为可能存在竞争条件。矢量化的要点是每个元素独立于其他元素,因此可以并行计算。但是你的算法不允许这样做。 “向量化”与“让自己走得更快”并不是一回事。

您在这里构建的是一个直方图,iOS内置了优化的支持。您可以创建单通道单行图像并使用vImageHistogramCalculation_Planar8,如下所示:

void f1(unsigned char *data, unsigned int size) {
    unsigned long A[256] = {0u};

    vImage_Buffer src = { data, 1, size, size };
    vImage_Error err = vImageHistogramCalculation_Planar8(&src, A, kvImageDoNotTile);
    if (err != kvImageNoError) {
        // error
    }
    ...
}

要小心,假设这总是一场胜利。这取决于您的数据大小。进行函数调用非常昂贵,因此可能需要数百万字节的数据才能使其值得。如果你在较小的集合上计算它,那么一个简单的,编译器优化的循环通常是最好的方法。您需要在真实设备上对此进行分析,以确定哪种设备更快。

只需确保允许编译器通过启用-Ofast(最快,积极)来应用所有矢量化优化。在这种情况下,这无关紧要,因为你的循环不能简单地进行矢量化。但一般来说,-Ofast允许编译器在可能略微增加代码大小的情况下应用向量化优化(在默认的-Os下不允许)。 -Ofast也允许在执行浮点数学方面有点邋so,所以不应该在需要严格的IEEE浮点一致性的情况下使用(但iOS应用几乎不是这种情况,所以{{1}几乎总是正确的设置。)

答案 1 :(得分:3)

编译器在此尝试执行的优化是并行化++A[data[i]]

它不能这样做,因为A的内容取决于循环的前一次迭代。

您可以通过每种并行方式使用一个频率数组(A)来打破这种依赖性,然后在最后计算这些数据的总和。我假设你有两种并行方式,size是偶数。

void f1(const unsigned char * const data, unsigned int size) {
    unsigned int A0[256] = {0u};
    unsigned int A1[256] = {0u}; 


    for (unsigned int i = 0u; i < size /2u; i++) {
       ++A0[data[2*i]];
       ++A1[data[2*i+1]];
    }

    for (unsigned i=0u; i < 256; ++i){
        A0[i] = A0[i] + A1[i];

    }
}

这会赢得你多少?只有一种方法可以找到 - 尝试并测量结果。我怀疑Accelerate框架会比这更好,即使size上的值相对较小。它还针对目标架构在运行时进行了优化。

编译器很聪明,但是你可以用C或C ++来帮助编译器:

  • 尽可能应用const:显然哪些数据是不变的。
  • 使用restrict(C ++中的__restrict限定符标识指向非重叠内存区域的指针。在不知道这一点的情况下,编译器必须假设通过一个指针写入可能会改变可以用另一个指针读取的数据。 clang实际上会为重叠和非重叠的情况生成运行时检查和代码路径,但是会有限制,你可以通过显式来减少代码大小。

我怀疑register的{​​{1}}限定词有什么不同。