假设您想要计算项目差异的平方和:
$ \ sum_ {i = 1} ^ {N-1}(x_i - x_ {i + 1})^ 2 $
最简单的代码(输入为std::vector<double> xs
,输出sum2
)为:
double sum2 = 0.;
double prev = xs[0];
for (vector::const_iterator i = xs.begin() + 1;
i != xs.end(); ++i)
{
sum2 += (prev - (*i)) * (prev - (*i)); // only 1 - with compiler optimization
prev = (*i);
}
我希望编译器在上面的注释中进行优化。如果N
的长度为xs
,则您有 N-1
次乘法和2N-3
总和(总和表示+
或{{1} })。
现在假设你知道这个变量:
$ x_1 ^ 2 + x_N ^ 2 + 2 \ sum_ {i = 2} ^ {N-1} x_i ^ 2 $
并将其称为-
。扩展二项式平方:
$ sum_i ^ {N-1}(x_i-x_ {i + 1})^ 2 = sum
- 2 \ sum_ {i = 1} ^ {N-1} x_i x_ {i + 1 } $
所以代码变成:
sum
这里我有 N次乘法和N-1次加法。在我的情况下,N大约是100。
好吧,用double sum2 = 0.;
double prev = xs[0];
for (vector::const_iterator i = xs.begin() + 1;
i != xs.end(); ++i)
{
sum2 += (*i) * prev;
prev = (*i);
}
sum2 = -sum2 * 2. + sum;
编译我没有加速(我尝试调用内联函数2M次),为什么?
答案 0 :(得分:2)
在执行时间方面,乘法比加法要多得多。此外,根据处理器的增加和乘法将并行完成。 IE浏览器。它会在进行加法时开始下一次乘法(见http://en.wikipedia.org/wiki/Out-of-order_execution)。
因此减少添加次数对性能没有多大帮助。
您可以做的是让编译器更容易对代码进行矢量化,或者自己进行矢量化。 为了使编译器更容易矢量化,我会使用常规的双精度数组,使用下标而不是指针。
编辑:N = 100也可能是一个小数字,以查看执行时间的差异。尝试N更大。
脏代码但显示了性能提升。输出:
1e+06
59031558
1e+06
18710703
你得到的加速是~3倍。
#include <vector>
#include <iostream>
using namespace std;
unsigned long long int rdtsc(void)
{
unsigned long long int x;
unsigned a, d;
__asm__ volatile("rdtsc" : "=a" (a), "=d" (d));
return ((unsigned long long)a) | (((unsigned long long)d) << 32);;
}
double f(std::vector<double>& xs)
{
double sum2 = 0.;
double prev = xs[0];
vector<double>::const_iterator iend = xs.end();
for (vector<double>::const_iterator i = xs.begin() + 1;
i != iend; ++i)
{
sum2 += (prev - (*i)) * (prev - (*i)); // only 1 - with compiler optimization
prev = (*i);
}
return sum2;
}
double f2(double *xs, int N)
{
double sum2 = 0;
for(int i = 0; i < N - 1; i+=1) {
sum2 += (xs[i+1] - xs[i])*(xs[i+1] - xs[i]);
}
return sum2;
}
int main(int argc, char* argv[])
{
int N = 1000001;
std::vector<double> xs;
for(int i=0; i<N; i++) {
xs.push_back(i);
}
unsigned long long int a, b;
a = rdtsc();
std::cout << f(xs) << endl;
b = rdtsc();
cout << b - a << endl;
a = rdtsc();
std::cout << f2(&xs[0], N) << endl;
b = rdtsc();
cout << b - a << endl;
}
答案 1 :(得分:1)
当x + = a * b完成时,可以免费添加。如果架构支持,编译器应该能够在第一个版本中找到它。
数学可能与*i
同时发生,这可能会更慢。
除非您希望返回值发生变化,否则不要在每次循环迭代时调用xs.end()
。如果编译器无法对其进行优化,那么它将使循环的其余部分相形见绌。