在数字过滤C ++应用程序中,我使用std::inner_product
(带std::vector<double>
和std::deque<double>
)来计算每个数据样本的滤波器系数和输入数据之间的点积。在对我的应用程序进行概要分析后,我发现在std::inner_product
中花费了不少于85%的执行时间!
在GCC中,std::inner_product
优化的范围是多少?
它是否使用SIMD指令?它是否执行循环展开?如何确保?
基于此,是否值得实现自定义点积函数(特别是如果系数的数量很少)? (但我希望尽可能保持函数的通用性)
更具体地说,这是我用来应用过滤器的代码:
std::deque<double> in(filterNum.size(), 0.0);
std::deque<double> out(filterDenom.size() - 1, 0.0);
const double gain = filterDenom.back();
for (unsigned int s = 0, size = data.size(); s < size; ++s) {
in.pop_front();
in.push_back(data[s] / gain);
data[s] = inner_product(in.begin(), in.end(), filterNum.begin(),
-inner_product(out.begin(), out.end(), filterDenom.begin(), 0.0));
out.pop_front();
out.push_back(data[s]);
}
通常,我使用二阶带通IIR滤波器,这意味着filterNum
和filterDenom
(滤波器的分子和分母系数)的大小为5. data
是向量包含输入样本。
答案 0 :(得分:1)
如果您直接编写代码,那么从中获取额外因子2应该不难。其中一部分可能来自于删除inner_product的一些通用性,但有些也可能来自消除deques的使用 - 如果你只是将一个指针放入你的输入数组,你可以将其索引并关闭过滤器数组。内部循环,并将指针递增到外部循环中的输入数组。
每个inner_products必须通过deques使用迭代器,
然后,大多数(编码)努力都会处理边缘条件。
从那里取出那个分区 - 它应该是在循环外计算的常数的乘法。
内部产品本身非常有效(没有太多可做的事情),但它需要在每次通过内部循环时增加两个迭代器。没有显式的循环展开,但是一个好的编译器可以展开一个简单的循环。并且编译器更有可能知道在遇到指令缓存问题之前展开循环的距离。
Deque迭代器在纯指针上的效率不如++。每个++至少有一个测试,可能有不止一个任务。
这是简单(FIR)滤波器的样子,不包括边缘条件的代码(在循环之外)
double norm = 1.0/sum;
double *p = data.values(); // start of input data
double *q = output.values(); // start of output buffer
int width = data.size() - filter.size();
for( int i = 0; i < width; ++i )
{
double *f = filter.values();
double accumulator = ( f[0] * p[0] );
for( int j = 1; j < filter.size(); ++j )
{
accumulator += ( f[i] * p[i] );
}
*q++ = accumulator * norm;
}
请注意,遗漏了细节,这与您的过滤器不同,但它提供了这个想法。外环内部的内容很容易适应现代指令缓存。内循环可以由编译器展开。大多数现代架构可以并行添加和增加。
答案 1 :(得分:0)
您可以要求GCC在并行模式下计算<algorithms>
和<numeric>
中的大多数算法,如果您的数据集非常高,它可能会提升性能(我认为它实际上只使用它) OpenMP里面。)
然而,在小型数据集上,它可能会影响性能。
与其他解决方案的比较非常受欢迎!
http://gcc.gnu.org/onlinedocs/libstdc++/manual/parallel_mode.html