私はベクトル化について学ぼうとしており、車輪を再投資するのではなく、Agner Fog のベクトル ライブラリを使用しています。
ここに私のオリジナルの C++/STL コードがあります
#include <vector>
#include <vectorclass.h>
template<typename T>
double mean_v1(T begin,T end) {
float mean = 0;
std::for_each(begin,end,[&mean](const double& d) { mean+=d; });
return mean / std::distance(begin,end);
}
double mean_v2(T begin,T end) {
float mean = 0;
const int distance = std::distance(begin,end); // This is expensive
const int loop = ( distance >> 2)+1; // divide by 4
const int partial = distance & 2; // remainder 4
Vec4d vec;
for(int i = 0; i < loop;++i) {
if(i == (loop-1)) {
vec.load_partial(partial,&*begin);
mean = horizontal_add(vec);
}
else {
vec.load(&*begin);
mean = horizontal_add(vec);
begin+=4; // This is expensive
}
}
return mean / distance;
}
int main(int argc,char**argv) {
using namespace boost::assign;
std::vector<float> numbers;
// Note 13 numbers, which won't fit into a sse register perfectly
numbers+=39.57,39.57,39.604,39.58,39.61,31.669,31.669,31.669,31.65,32.09,33.54,32.46,33.45;
const float mean1 = mean_v1(numbers.begin(),numbers.end());
const float mean2 = mean_v2(numbers.begin(),numbers.end());
return 0;
}
v1 と v2 の両方が正しく機能し、どちらもほぼ同じ時間がかかります。ただし、プロファイリングすると std::distance() が表示され、反復子を移動するのに合計時間のほぼ 45% がかかります。ベクトルの追加はわずか 0.8% で、v1 よりも大幅に高速です。
Web を検索すると、すべての例が、SSE レジスターに正確に適合する完全な数の値を扱っているようです。たとえば、この例では、ループの設定に計算よりもはるかに長い時間がかかっています。
このシナリオに対処する方法については、ベスト プラクティスやアイデアが必要だと思います。
mean() のインターフェースを変更して float[] を使用することはできないが、反復子を使用する必要があると仮定します