我正在尝试并行化一些IIR过滤代码,到目前为止使用gcc
7.2和gomp
。
以下是相关的代码部分:
void par_iir(std::vector<std::vector<double>>& data, const std::vector<double>& B, int order) {
int N = data[0].size();
const int CHUNK_SIZE = 32768;
int chunks = ceil((double)(N - 2) / (double)CHUNK_SIZE);
int gChunk;
#pragma omp parallel private(gChunk) shared(chunks, order, B, data)
{
for (gChunk = 0; gChunk < chunks + order - 1; gChunk++) {
#pragma omp for
for (int i = 0; i < order; i++) {
auto chunk = gChunk - i;
if (chunk >= 0 && chunk < chunks) {
auto sz = chunk == (chunks - 1) ? (N - 2) % CHUNK_SIZE : CHUNK_SIZE;
auto inp = data[i].data();
auto out = data[i + 1].data();
filter(inp + chunk * CHUNK_SIZE + 2, B.data() + i * 5, out + chunk * CHUNK_SIZE + 2, sz);
}
}
}
}
}
功能过滤器定义如下:
inline void filter(const double* in, const double* B, double* out, int sz) {
for (int i = 0; i < sz; ++i)
out[i] = in[i] * B[0] + in[i - 1] * B[1] + in[i - 2] * B[2] - out[i - 1] * B[3] - out[i- 2] * B[4];
}
par_iir
定义了级联IIR滤波器,filter
在级联中应用了单个双四极杆。 order
确定有多少级联双四边形组成过滤器。我的电脑有4个核心(8个线程),我选择了一个8阶的过滤器。
由于IIR过滤器具有循环依赖性,我选择将工作划分为块(目前固定大小为32768)。这个想法是,一旦级联中的双四元i
的块是完成后,双四元i
和i+1
可以并行完成。
我原以为这会导致接近所有最多使用的8个线程,所以接近8倍的改进。相反,我通常会在每个线程上看到大约33%的负载。
使用perf
我对代码进行了分析,发现约有63%的时间用于gomp_thread_start
。即使我按比例增加N
和CHUNK_SIZE
,这个~63%也不会改变。
这63%的开销来自哪里?其次,有没有更好的方法来编写这段代码?
有关IIR滤波器的一些细节,如果它有帮助:
就我的目的而言,IIR滤波器由多个双四极滤波器组成。每个双四进制过滤器执行以下操作,其中a
和b
值是常量:
out[n] = b0*in[n] + b1*in[n-1] + b2*in[n-2] - a1*out[n-1] - a2*out[n-2]
级联的每个部分都被赋予前一部分的输出作为其输入。这使得大多数并行方法变得不可行。