如何在NEON汇编中写入double值的乘法?

时间:2014-08-14 19:35:41

标签: ios arm multiplication neon

有问题的行包含在内:

w00 * ptr[0] + w01 * ptr[stride] + w10 * ptr[1] + w11 * ptr[stride+1]

考虑到这些变量是double(但我可以降级到float),我想我可以为每个寄存器传递一个值?直接使用2x2矩阵W会更有效吗?

编辑1:

此行位于循环内,每秒触发数百次并具有实时要求。仪器表示这条线需要占循环时间的60%。

编辑2:

这是我正在谈论的循环:

for (int x=startingX; x<endingX; ++x)
{
    for (int y=startingY; y<endingY; ++y)
    {
        Matx21d position(x,y);

        // warp patch
        uint8_t *data;
        [self backwardWarpPatchWithWarpingMatrix:warpingMatrix withWarpData:&data withReferenceImage:_initialView withCenter:position];

        // check that the backward patch was successful
        if (!data)
            continue;

        // calculate zero mean (on the patch) sum of squared differences
        int ssd = [self computeZMSSDScoreWithX:x withY:y withCurrentTargetPatch:data];

        if (fabs(ssd) < bestSSD)
        {
            bestPosition = position;
            bestSSD = ssd;
        }
    }
}

backwardWarpPatchWithWarpingMatrix:

Matx22d warpingMatrixInverse = warpingMatrix.inv();
double wmi0 = warpingMatrixInverse(0,0), wmi1 = warpingMatrixInverse(0,1), wmi2 = warpingMatrixInverse(1,0), wmi3 = warpingMatrixInverse(1,1);

if (isnan(wmi0))
{
    warpingMatrixInverse = Matx22d::eye();
}

// Perform the warp on a larger patch.
int LEVEL_REF = 0, halfPatchSize = PATCH_SIZE/2;
Matx21d centerInLevel = center * (1.0 / (1<<LEVEL_REF));
__block Mat warped(PATCH_SIZE, PATCH_SIZE, CV_8UC1);


dispatch_apply(PATCH_SIZE, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t y)
{
    for (int x=0; x<PATCH_SIZE; ++x)
    {
        double pp0 = x - halfPatchSize, pp1 = (double)y - halfPatchSize;
        Matx21d multiplication(wmi0 * pp0 + wmi1 * pp1, wmi2 * pp0 + wmi3 * pp1);
        Matx21d px(multiplication(0) + centerInLevel(0), multiplication(1) + centerInLevel(1));
        double warpedPixel = [self interpolatePointInImage:referenceImage withU:px(0) withV:px(1)];
        warped.at<uchar>(y,x) = (uint8_t)warpedPixel;
    }
});

computeReferencePatchScores:

int x = (int)u;
int y = (int)v;
float   subpixX = u - x,
        subpixY = v - y,
        oneMinusSubpixX = 1.0 - subpixX,
        oneMinusSubpixY = 1.0 - subpixY;


float   w00 = oneMinusSubpixX * oneMinusSubpixY,
        w01 = oneMinusSubpixX * subpixY,
        w10 = subpixX * oneMinusSubpixY,
        w11 = 1.0f - w00 - w01 - w10;

const int stride = (int)image.step.p[0];
uchar* ptr = image.data + y * stride + x;

return w00 * ptr[0] + w01 * ptr[stride] + w10 * ptr[1] + w11 * ptr[stride+1];

2 个答案:

答案 0 :(得分:1)

您通常不会将一行代码转换为汇编代码。为了值得在汇编中编写,您必须首先假设您可以生成比编译器更好的汇编。有时候NEON上的矢量化代码也是如此,但通常是因为你对复杂循环有特殊的了解。你不可能在一行代码上显着击败编译器(并且可能会丢失)。这一行是您已经分析并确定为主要瓶颈的循环的一部分吗?你有没有试过Accelerate?您是否分析了编译器生成的程序集并发现了它所犯的错误。


尝试在ObjC ++中执行此操作效率非常低。 ObjC ++是一种将C ++和ObjC结合在一起的粘合语言;在同一个文件中执行这两个操作会产生一些性能成本,尤其是ARC。在性能关键的内循环内部调用ObjC方法在任何情况下都是非常昂贵的(即使没有混合的C ++)。你绝不应该在一个紧密的内循环中进行任何类型的函数调用(最不是所有的ObjC方法调度)。目前尚不清楚你实际上在哪里打computeReferencePatchScores。在这里使用GCD可能比帮助更多地伤害你(因为它阻止了编译器应用某些向量优化)。

这就是说:如何将特定代码行编译到程序集中,这是您在此代码中遇到的最少问题。它的结构正在与clang的优化者作斗争。

第一步是退后一步,询问您要执行的计算,然后通读Core Image Programming GuidevImage Programming Guide并验证它尚未可用。您也可以查看OpenGL ES,但OpenGL通常是一种完整的绘图方法(因此它更多的是承诺)。看起来您已经在使用OpenCV,因此请确保它没有可用的功能来执行您想要的操作。 (我在那里看到的大部分内容看起来都是内置于OpenCV和vImage中的内容。)

在不转向更强大的框架的情况下提高性能的最简单方法是将整个循环移动到单个C ++函数中。然后优化器可以看到所有代码并自行应用向量操作。但下一步是利用现有的高级高性能框架。

在任何情况下,您都需要坐下来仔细地完成您需要执行的计算(我通常在纸上手工完成)。确保您没有复制任何内容,您需要执行每项计算,并且您所做的每项更改仍会产生相同的结果。

答案 1 :(得分:1)

这看起来是2x2卷积。如果数据集很大,则vImageConvolve_PlanarF与3x3内核(其中包含零填充)将完成此任务。它试图跳过关于0的内核元素的工作。您需要将数据集转换为单精度。

如果数据集很小,那么您可能会遇到标量代码性能问题。如果可以的话,内联函数。也许你可以弄清楚如何将一堆这些聚合在一起,以利用更重的高性能程序。

但是,如果权重从像素变为像素,则卷积不起作用。如果您的数据集不是很大,您可以查看vImage / Transform.h中的N维查找表功能。

我有点怀疑,时间真的花在那条线上。最好查看仪器中的装配视图,以查看样品的确定位置。