如何为多项式求值函数实现带并行累加器的循环展开?
多项式求值的并行累加器优化问题
为利用CPU微架构的流水线设计,并行累加器result与result2必须完全独立才能实现并行执行,因此以下存在依赖的代码不可用:
for (i = degree - 1; i >= 1; i-=2) { result = a[i] + x * result; result2 = a[i-1] + x * result; // *存在依赖,无法并行* }
原多项式求值函数
double polyh(double a[], double x, long degree) { long i; double result = a[degree]; for (i = degree - 1; i >= 0; i--) { result = a[i] + x * result; } return result; }
错误的2级循环展开实现
尝试引入第二个变量result2实现2级循环展开,但poly_opt无法得到正确结果:
double poly_opt(double a[], double x, long degree) { long i; double result = a[degree]; double result_2 = 0; double result_array[2] = {result, result_2}; double xpwr_1 = x; // 1 double xpwr_2 = x * x; // 2 double xpwr_array[2] = {xpwr_1, xpwr_2}; for (i = degree - 1; i >= 1; i -= 2) { result = a[i] + xpwr_1 * result; result_2 = a[i - 1] + xpwr_2 * result_2; xpwr_1 = xpwr_1 * x * x; xpwr_2 = xpwr_2 * x * x; } // 处理循环展开后剩余的项 for (; i >= 0; --i) { result = a[i] + xpwr_1 * result; xpwr_1 = x * xpwr_1; } return result * result_2; }
性能有限的8级循环展开实现
不使用并行累加器的8级循环展开能得到正确结果,但性能提升有限,希望通过并行累加器进一步优化:
double poly_opt(double a[], double x, long degree) { long i; double result = a[degree]; for (i = degree - 1; i >= 8; i -= 9) { result = a[i - 8] + (a[i - 7] + (a[i - 6] + (a[i - 5] + (a[i - 4] + (a[i - 3] + ((a[i - 2] + (a[i - 1] + (a[i] + x * result) * x) * x) * x)) * x) * x) * x) * x) * x; } // 处理循环展开后剩余的项 for (; i >= 0; --i) { result = a[i] + x * result; } return result; }
测试数据集
double a[] = {1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0, 3.0}; double x = 0.5; long degree = 359; int main() { std::cout << polyh(a, x, degree) << std::endl; return 0; }
正确输出应为:3.1428571429
正确的并行累加器实现方案
错误根源在于对并行累加器的计算逻辑理解偏差,并行累加器需要将多项式拆分为奇数项和偶数项两个独立的子多项式,分别计算后再合并结果,而非引入无关变量相乘。
以下是正确的2级并行累加器优化实现:
double poly_opt(double a[], double x, long degree) { long i; double result_odd, result_even; double x_sq = x * x; // 初始化:根据degree的奇偶性分配初始值 if (degree % 2 == 0) { result_even = a[degree]; result_odd = a[degree - 1]; i = degree - 2; } else { result_odd = a[degree]; result_even = a[degree - 1]; i = degree - 2; } // 并行处理两个独立的子多项式 for (; i >= 0; i -= 2) { result_even = a[i] + x_sq * result_even; result_odd = a[i - 1] + x_sq * result_odd; } // 合并结果:偶数项子多项式结果 + x * 奇数项子多项式结果 return result_even + x * result_odd; }
实现说明
- 拆分逻辑:将原多项式拆分为两个独立的子多项式:
- 偶数项子多项式:
a[0] + a[2]x² + a[4]x⁴ + ... - 奇数项子多项式:
a[1] + a[3]x² + a[5]x⁴ + ...
- 偶数项子多项式:
- 并行计算:两个子多项式的计算完全独立,无数据依赖,可被CPU流水线并行执行
- 结果合并:原多项式结果 = 偶数项子多项式结果 + x * 奇数项子多项式结果
- 剩余项处理:通过初始的奇偶判断自动覆盖所有项,无需额外的剩余循环
该实现既保证计算正确性,又能充分利用CPU流水线提升性能,测试后可得到正确输出3.1428571429。
内容的提问来源于stack exchange,提问作者tomatto
相关产品推荐
相关产品推荐

