[英]C++ use SSE instructions for comparing huge vectors of ints
我有一个巨大的vector<vector<int>>
(18M x 128)。 我经常想要获取此向量的两行并通过此函数进行比较:
int getDiff(int indx1, int indx2) {
int result = 0;
int pplus, pminus, tmp;
for (int k = 0; k < 128; k += 2) {
pplus = nodeL[indx2][k] - nodeL[indx1][k];
pminus = nodeL[indx1][k + 1] - nodeL[indx2][k + 1];
tmp = max(pplus, pminus);
if (tmp > result) {
result = tmp;
}
}
return result;
}
如你所见,函数循环通过两个行向量做一些减法,最后返回一个最大值。 这个功能将被使用一百万次,所以我想知道它是否可以通过SSE指令加速。 我使用Ubuntu 12.04和gcc。
当然这是微优化,但如果你能提供一些帮助会有所帮助,因为我对SSE一无所知。 提前致谢
基准测试:
int nofTestCases = 10000000;
vector<int> nodeIds(nofTestCases);
vector<int> goalNodeIds(nofTestCases);
vector<int> results(nofTestCases);
for (int l = 0; l < nofTestCases; l++) {
nodeIds[l] = randomNodeID(18000000);
goalNodeIds[l] = randomNodeID(18000000);
}
double time, result;
time = timestamp();
for (int l = 0; l < nofTestCases; l++) {
results[l] = getDiff2(nodeIds[l], goalNodeIds[l]);
}
result = timestamp() - time;
cout << result / nofTestCases << "s" << endl;
time = timestamp();
for (int l = 0; l < nofTestCases; l++) {
results[l] = getDiff(nodeIds[l], goalNodeIds[l]);
}
result = timestamp() - time;
cout << result / nofTestCases << "s" << endl;
哪里
int randomNodeID(int n) {
return (int) (rand() / (double) (RAND_MAX + 1.0) * n);
}
/** Returns a timestamp ('now') in seconds (incl. a fractional part). */
inline double timestamp() {
struct timeval tp;
gettimeofday(&tp, NULL);
return double(tp.tv_sec) + tp.tv_usec / 1000000.;
}
FWIW我整理了一个纯SSE版本(SSE4.1),它似乎比Core i7上的原始标量代码快20%左右:
#include <smmintrin.h>
int getDiff_SSE(int indx1, int indx2)
{
int result[4] __attribute__ ((aligned(16))) = { 0 };
const int * const p1 = &nodeL[indx1][0];
const int * const p2 = &nodeL[indx2][0];
const __m128i vke = _mm_set_epi32(0, -1, 0, -1);
const __m128i vko = _mm_set_epi32(-1, 0, -1, 0);
__m128i vresult = _mm_set1_epi32(0);
for (int k = 0; k < 128; k += 4)
{
__m128i v1, v2, vmax;
v1 = _mm_loadu_si128((__m128i *)&p1[k]);
v2 = _mm_loadu_si128((__m128i *)&p2[k]);
v1 = _mm_xor_si128(v1, vke);
v2 = _mm_xor_si128(v2, vko);
v1 = _mm_sub_epi32(v1, vke);
v2 = _mm_sub_epi32(v2, vko);
vmax = _mm_add_epi32(v1, v2);
vresult = _mm_max_epi32(vresult, vmax);
}
_mm_store_si128((__m128i *)result, vresult);
return max(max(max(result[0], result[1]), result[2]), result[3]);
}
您可能可以让编译器为此使用SSE。 它会使代码更快吗? 可能不是。 原因是与计算相比存在大量内存访问。 CPU比内存快得多,并且当等待数据通过系统总线到达时,上面的一个简单的实现已经使CPU停止。 使CPU更快只会增加它的等待时间。
nodeL的声明可能会影响性能,因此为数据选择有效的容器非常重要。
有一个阈值,优化确实有一个好处,那就是当你在内存读取之间进行更多计算时 - 即内存读取之间的时间要大得多。 发生这种情况的重点在很大程度上取决于您的硬件。
但是,如果您有可以在并行运行的非内存约束任务,那么优化代码会很有帮助,这样CPU在等待数据时就会保持忙碌状态。
这会更快。 向量矢量的双重引用是昂贵的。 缓存其中一个解除引用将有所帮助。 我知道它没有回答发布的问题,但我认为这将是一个更有帮助的答案。
int getDiff(int indx1, int indx2) {
int result = 0;
int pplus, pminus, tmp;
const vector<int>& nodetemp1 = nodeL[indx1];
const vector<int>& nodetemp2 = nodeL[indx2];
for (int k = 0; k < 128; k += 2) {
pplus = nodetemp2[k] - nodetemp1[k];
pminus = nodetemp1[k + 1] - nodetemp2[k + 1];
tmp = max(pplus, pminus);
if (tmp > result) {
result = tmp;
}
}
return result;
}
有几件事要看。 一个是您传递的数据量。 这将导致比琐碎的计算更大的问题。
我试图在这里使用库使用SSE指令(AVX)重写它
我系统上的原始代码运行时间为11.5秒。随着Neil Kirk的优化,它降至10.5秒
编辑:使用调试器而不是在我脑海中测试代码!
int getDiff(std::vector<std::vector<int>>& nodeL,int row1, int row2) {
Vec4i result(0);
const std::vector<int>& nodetemp1 = nodeL[row1];
const std::vector<int>& nodetemp2 = nodeL[row2];
Vec8i mask(-1,0,-1,0,-1,0,-1,0);
for (int k = 0; k < 128; k += 8) {
Vec8i nodeA(nodetemp1[k],nodetemp1[k+1],nodetemp1[k+2],nodetemp1[k+3],nodetemp1[k+4],nodetemp1[k+5],nodetemp1[k+6],nodetemp1[k+7]);
Vec8i nodeB(nodetemp2[k],nodetemp2[k+1],nodetemp2[k+2],nodetemp2[k+3],nodetemp2[k+4],nodetemp2[k+5],nodetemp2[k+6],nodetemp2[k+7]);
Vec8i tmp = select(mask,nodeB-nodeA,nodeA-nodeB);
Vec4i tmp_a(tmp[0],tmp[2],tmp[4],tmp[6]);
Vec4i tmp_b(tmp[1],tmp[3],tmp[5],tmp[7]);
Vec4i max_tmp = max(tmp_a,tmp_b);
result = select(max_tmp > result,max_tmp,result);
}
return horizontal_add(result);
}
缺乏分支可以将速度提高到9.5秒,但数据仍然是最大的影响。
如果你想加快速度,尝试将数据结构更改为单个数组/向量而不是2D(ala std :: vector),因为这会降低缓存压力。
编辑我想到了一些东西 - 你可以添加一个自定义分配器,以确保你在一个连续的内存块中分配2 * 18M向量,这允许你保持数据结构并仍然快速通过它。 但是你需要对它进行分析才能确定
编辑2:用调试器测试代码,而不是在我脑海中! 对不起Alex,这应该会更好。 不确定它会比编译器的速度快。 我仍然认为这是内存访问的问题,所以我仍然会尝试单阵列方法。 尽管如此。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.