优化贝塞尔过滤器实施

时间:2016-09-09 13:54:37

标签: c arrays caching optimization

我正在开发一个用于我的博士研究的信号处理代码,我正处于开始分析和优化代码的阶段。自从我上一次大优化(Optimizing Disk IO)以来,新的瓶颈占据了代码运行时间的50%,是我对数字低通贝塞尔滤波器的实现。

我正在寻找的是如何加快这种计算的建议。

代码如下,以及对正在发生的事情的细分和解释:

void filter_signal(double *signal, bessel *lpfilter, int64_t length)
{
    int64_t i;
    int64_t p;
    int64_t end;
    int64_t order = lpfilter->order;
    int64_t padding = lpfilter->padding;
    double *paddedsignal = lpfilter->paddedsignal;
    double *temp = lpfilter->temp;
    double *dcof = lpfilter->dcof;
    double *ccof = lpfilter->ccof;
    end = length+2*(order+padding);
    int64_t imax = order+padding;
    double padval = signal_average(signal,padding);

    memcpy(&paddedsignal[imax],signal,length*sizeof(double));

    for (i=0; i<imax; i++)
    {
        temp[i] = padval;
        paddedsignal[i] = padval;
        paddedsignal[end-1-i] = padval;
        temp[end-1-i] = padval;
    }
    for (i=order; i<end; i++)
    {
        temp[i] = ccof[0]*paddedsignal[i];
        for (p=1; p<=order; p++)
        {
            temp[i] += ccof[p]*paddedsignal[i-p] - dcof[p]*temp[i-p];
        }
    }
    padval = signal_average(&temp[order],padding);
    for (i=0; i<imax; i++)
    {
        paddedsignal[end-1-i] = padval;
        paddedsignal[i] = padval;
    }
    for (i=order; i<end; i++)
    {
        paddedsignal[end-1-i] = ccof[0]*temp[end-1-i];
        for (p=1; p<=order; p++)
        {
            paddedsignal[end-1-i] += ccof[p]*temp[end-1-i+p] - dcof[p]*paddedsignal[end-1-i+p];
        }
    }
    memcpy(signal,&paddedsignal[order+padding],length*sizeof(double));
}

首先:signal数组非常大(比如高达length = 1e7个条目,我可能会在一次运行中处理数千个这样的数组),所以我猜很多运行时只是将数据加载到缓存中,这可能会带来一些收益。贝塞尔滤波的工作原理如下:我们有一些系数(dcofccof),每个系列的长度为order,它们介于2到10之间。每个点的滤波信号为数组中先前点和已过滤数组中先前点的加权和,ccofdcof是权重。

有两个复杂情况:一个是对于有限长度阵列,这样的滤波会引入边缘效应。解决这个问题的方法是使用平均值填充数组并在过滤后丢弃填充,以便在实际数据开始时边缘效应消失。第二个复杂因素是滤波在数据中引入了相移(滤波后的数组将被原始数组中的一些样本偏移)。解决这个问题的方法是过滤两次:一次向前,这将消除噪声的高频分量并使数据相移,然后再向后移动,这对频率分量几乎没有影响,但是反转了相移。这两个修复程序都在下面实现。

更详细地介绍代码:

memcpy(&paddedsignal[imax],signal,length*sizeof(double));

for (i=0; i<imax; i++)
{
    temp[i] = padval;
    paddedsignal[i] = padval;
    paddedsignal[end-1-i] = padval;
    temp[end-1-i] = padval;
}

paddedsignal是一个临时数组,在中间部分保存signal,但在两端用order+padding个样本填充以避免边缘效应。 temp是一个与paddedsignal具有相同尺寸的临时数组,因为无法在此处进行前向和后向过滤。两者的填充部分填充了前几个样本的平均值,这减少了误差。

for (i=order; i<end; i++)
{
    temp[i] = ccof[0]*paddedsignal[i];
    for (p=1; p<=order; p++)
    {
        temp[i] += ccof[p]*paddedsignal[i-p] - dcof[p]*temp[i-p];
    }
}

这是前向过滤循环。完成后,temp将包含填充,正向滤波和相移信号。

for (i=order; i<end; i++)
{
    paddedsignal[end-1-i] = ccof[0]*temp[end-1-i];
    for (p=1; p<=order; p++)
    {
        paddedsignal[end-1-i] += ccof[p]*temp[end-1-i+p] - dcof[p]*paddedsignal[end-1-i+p];
    }
}

这是向后滤波循环,它可以撤消相移。完成后,paddedsignal将包含填充,过滤和非相移数据。然后,我们将中心部分存储回信号阵列并丢弃填充。

特别是,我想知道是否有任何干净的方法来优化它以避免缓存未命中。有几个地方可能会引起关注,但我希望在花费太多时间咆哮错误的树之前从更有经验的程序员那里得到意见:在实际的过滤循环中,样本i处的过滤数组的值取决于它自己的值和i-p处未过滤数组的值。我想知道这是否会缓存不友好?此外,第二个循环向后遍历阵列。这是一个问题,速度明智吗?

1 个答案:

答案 0 :(得分:1)

我建议尝试在开头和结尾消除select i.item_id, i.item_name, i.item_brand, i.quantity - coalesce(sum(ji.quantity),0) as remaining_quantity from item i left join job_item ji on i.item_id = ji.item_id group by i.item_id, i.item_name, i.item_brand, i.quantity having i.quantity - coalesce(sum(ji.quantity),0) <> 0 次来电。这些可能是昂贵的操作,似乎你可以做你的大部分工作&#34;就地&#34;在原始阵列上。我看到你关于填充的评论 - 所以我考虑通过在调用方法之前将填充构建到原始数组中或者添加一些逻辑来将pad值仅存储在此函数的本地数组中来处理。一般来说,尽可能避免复制。