微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

提高具有等距输入的 2D 直接查找表的性能嵌入式 C

如何解决提高具有等距输入的 2D 直接查找表的性能嵌入式 C

我在使用具有等距输入的直接查找表进行 2D 插值时缺乏性能

目标是找到表 z(x,y) 中的 4 个值 (z00,z01,z11,z10) 分别对应输入 x 和 y 的两个最接近的值,(x0

例如以下查找:

                                              x
                        
y 1 2 3
4 20 23 29
6 35 37 43
8 47 50 55

由以下数组表示:

const f32 lookup {20,35,47,23,37,50,29,43,55}

此外,连同数组的定义,一个结构提供以下数据以允许更有效的查找:

 lowest_x = 1;
 lowest_y = 4;
 step_x = 1;
 step_y = 2;
 length_x = 3;
 length_y = 3;

该算法最耗时的部分是找到与输入 x 和 y 前后值的交集对应的索引。

我目前的方法是按如下方式计算它们:

鉴于 x0 和 y0 是指数的倍数:

index_x0 = u32 ((x-lowest_x)/step_x);

index_y0 = u32 ((y-lowest_y)/step_y);

那么 x0,x1,y0 和 y1 是:

x0 = lowest_x + index_x0 * step_x ;
x1 = x0 + step_x ;
y0 = lowest_y + index_y0 * step_y ;
y1 = y0 + step_y ;

查找 z(x,y) 的 4 个必要值是:

   z00_index = index_x0*length_y0+index_y0;

    z00= lookup[z00_index]
    z01= lookup[z00_index+1]
    z10= lookup[z00_index+length_y0];
    z11= lookup[z00_index+length_y0+1];

然后将二维插值作为 x 沿 y0 和 y1 的两次插值和 y 的一次插值执行:

zxy0 = (x1-x)/(x1-x0)*z00 + (x-x0)/(x1-x0)*z10;

zxy1 = (x1-x)/(x1-x0)*z01 + (x-x0)/(x1-x0)*z11;

z = (y1-y)/(y1-y0)*zxy0 + (y-y0)/(y-y0)*zxy1;

关于如何改进这一点有什么建议吗?

解决方法

我不研究嵌入式,但有一些微优化的机会,特别是减少乘法和除法的次数,并避免再次重新计算相同的东西。

如果你先把指数计算成一个实数:

double ix = (x - x.base) / x.step;

您将获得一个“真实索引”,您可以将其转换为整数:

unsigned ix0 = ix;

现在,它们之间的区别为您提供了插值索引:

double r1 = ix - ix0;    // == (x - x0) / (x1 - x0)
double r0 = 1.0 - r1;    // == (x1 - x) / (x1 - x0)

您现在拥有 x 所需的一切:查找索引和插值系数。您不需要的是间隔开始和结束时的实际 x 值。对 y 执行相同操作以获得 iy0r0r1,您的插值是:

double z = r0 * (s0 * z00 + s1 * z01) + r1 * (s0 * z10 + s1 * z11);

这是每个坐标的一个除法,您可以通过预先计算因子 1.0 / x.step1.0 / y.step 将其转化为乘法。

这是原始代码的完整示例实现和建议的改进。在 PC 上测试,我获得了 30%-40% 的加速,但没有在嵌入式上测试。小心:代码不做任何边界检查!

#include <stdlib.h>
#include <stdio.h>

typedef struct Lookup Lookup;
typedef struct Range Range;

struct Range {
    double base;
    double step;
    unsigned length;
};

struct Lookup {
    Range x,y;
    double *data;
    double xdenom;
    double ydenom;
};

double lookup1(const Lookup *lo,double x,double y)
{
    unsigned index_x0 = (x - lo->x.base) / lo->x.step;
    unsigned index_y0 = (y - lo->y.base) / lo->y.step;

    double x0 = lo->x.base + index_x0 * lo->x.step;
    double x1 = x0 + lo->x.step;
    double y0 = lo->y.base + index_y0 * lo->y.step;
    double y1 = y0 + lo->y.step;

    double *p = lo->data + index_x0 * lo->y.length + index_y0;
    double z00 = p[0];
    double z01 = p[1];
    double z10 = p[lo->y.length];
    double z11 = p[lo->y.length + 1];

    double zxy0 = (x1 - x) / (x1 - x0)*z00 + (x - x0) / (x1 - x0)*z10;
    double zxy1 = (x1 - x) / (x1 - x0)*z01 + (x - x0) / (x1 - x0)*z11;

    double z = (y1 - y) / (y1 - y0)*zxy0 + (y - y0) / (y1 - y0)*zxy1;

    return z;
}

double lookup2(const Lookup *lo,double y)
{
    double ix = (x - lo->x.base) * lo->xdenom;
    double iy = (y - lo->y.base) * lo->ydenom;
    
    unsigned ix0 = ix;
    unsigned iy0 = iy;
    
    double r1 = ix - ix0;
    double r0 = 1.0 - r1;
    double s1 = iy - iy0;
    double s0 = 1.0 - s1;

    double *p = lo->data + (ix0 * lo->y.length + iy0);

    double z00 = p[0];
    double z01 = p[1];
    double z10 = p[lo->y.length];
    double z11 = p[lo->y.length + 1];

    double z = r0 * (s0 * z00 + s1 * z01)
             + r1 * (s0 * z10 + s1 * z11);

    return z;
}

double urand(void)
{
    return rand() / (1.0 + RAND_MAX);
}

enum {
    L = 1 << 24
};

int main(void)
{
    Lookup lo = {
        {10,0.1,80},{10,};
    
    unsigned i,j;
    double *p;
    unsigned l = L;
    double sum = 0;
    
    lo.xdenom = 1.0 / lo.x.step;
    lo.ydenom = 1.0 / lo.y.step;
    p = lo.data = malloc(lo.x.length * lo.y.length * sizeof(*lo.data));
    
    for (i = 0; i < lo.x.length; i++) {
        for (j = 0; j < lo.y.length; j++) {
            *p++ = urand();
        }
    }
    
    while (l--) {
        double x = lo.x.base + (lo.x.length * lo.x.step) * urand();
        double y = lo.y.base + (lo.y.length * lo.y.step) * urand();
        double z = lookup2(&lo,x,y);
        
        sum += z;
    }
    
    printf("%g\n",sum / L);
    
    free(lo.data);    

    return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。