查找x86 SIMD向量中最大元素的索引
Find index of maximum element in x86 SIMD vector
我正在考虑为uint32_t的实现8-ary堆排序。要做到这一点,我需要一个函数来选择8个元素向量中最大元素的索引,以便我可以将其与父元素进行比较,并有条件地执行swap和进一步的siftDown步骤。
(8 uint32_ts可以更改为16 uint32_ts或8 uint64_t或任何x86 SIMD可以有效支持)。
我有一些关于如何做到这一点的想法,但我正在寻找比非矢量化代码更快的东西,特别是我正在寻找能够让我做快速堆排序的东西。
我有clang++ 3.3和Core i7-4670,所以我应该能够使用甚至是最新的x86 SIMD的东西。
(顺便说一句:这是一个更大的项目的一部分:https://github.com/tarsa/SortingAlgorithmsBenchmark,例如,第四元堆排序,所以在实现SIMD堆排序后,我可以立即比较它们)
重复- 问题是:在x86 SIMD向量中计算最大元素索引的最有效方法是什么?
PS:它不是链接问题的副本-请注意,我要求的是一个最大元素的索引,而不仅仅是元素值。
水平操作对于SIMD来说是个坏消息,特别是对于AVX,其中大多数256位指令实际上被分解为两个独立的128位操作。话虽如此,如果你真的必须在8个元素之间设置一个32位的水平最大值那么我认为一般的方法是:
- 查找最大值(通常是几个shift/permute和max操作)
- 在第二个矢量的所有8个元素上绘制最大值(可以与之前的操作结合)
- 比较原始矢量和最大矢量(
_mm256_cmpeq_epi32
) - 提取标量掩码(
_mm256_movemask_epi8
) - 将标量掩码转换为索引
这是我刚刚放在一起的AVX2实现的第一次通过-我对它进行了测试,并在2.6 GHz的Haswell上对其进行了基准测试,它的运行速度约为1.7 ns/vector(包括加载vector和存储结果索引):
uint8_t _mm256_hmax_index(const __m256i v)
{
__m256i vmax = v;
vmax = _mm256_max_epu32(vmax, _mm256_alignr_epi8(vmax, vmax, 4));
vmax = _mm256_max_epu32(vmax, _mm256_alignr_epi8(vmax, vmax, 8));
vmax = _mm256_max_epu32(vmax, _mm256_permute2x128_si256(vmax, vmax, 0x01));
__m256i vcmp = _mm256_cmpeq_epi32(v, vmax);
uint32_t mask = _mm256_movemask_epi8(vcmp);
return __builtin_ctz(mask) >> 2;
}
在n路SIMD向量上执行水平操作(点积,求和,max-index等)的最有效方法是一次执行n个操作,通过将它们转置并使用垂直操作来代替。某些SIMD架构对水平操作有更好的支持,但一般来说,块转置方法将更加灵活和高效。
使用Vc库我会写:
size_t maximumIndex(Vc::uint_v vec) {
const unsigned int max = vec.max();
return (max == vec).firstOne();
}
对于intrinsic,它应该是沿着这些行(这是AVX没有AVX2 - AVX2变得稍微容易):
size_t maximumIndex(_mm256i vec) {
__m128i lo = _mm256_castsi256_si128(vec);
__m128i hi = _mm256_extractf128_si256(vec, 1);
__m128i tmp = _mm_max_epu32(lo, hi);
tmp = _mm_max_epu32(tmp, _mm_shuffle_epi32(tmp, _MM_SHUFFLE(1, 0, 3, 2)));
tmp = _mm_max_epu32(tmp, _mm_shufflelo_epi16(tmp, _MM_SHUFFLE(1, 0, 3, 2))); // using lo_epi16 for speed here
const int max = _mm_cvtsi128_si32(tmp);
tmp = _mm_packs_epi16(_mm_packs_epi32(_mm_cmpeq_epi32(_mm_set1_epi32(max), lo),
_mm_cmpeq_epi32(_mm_set1_epi32(max), hi)),
_mm_setzero_si128());
return _bit_scan_forward(_mm_movemask_epi8(tmp));
}
顺便说一句,如果你想从SIMDized合并排序中得到一些灵感,看看这里:http://code.compeng.uni-frankfurt.de/projects/vc/repository/revisions/master/entry/src/avx_sorthelper.cpp
- 在对向量中查找元素的索引
- 当该数组的索引中没有元素时,指针指向什么?
- 如何在 STL 函数中找到传递给谓词的元素的索引?
- 如何在C++中递归地找到max元素的索引?
- 编译器(Visual C++)如何优化按索引访问矢量元素?
- 保持排序的数据结构,允许log N插入时间,并且可以返回我在log N中查找的元素的索引
- 使用推力根据索引更改某些元素的值
- 为什么 min_element() 返回最小元素的索引,而不是迭代器?
- CPtrList - 如何获取元素的索引?
- 获取结构 c++ 中元素的索引
- C++ STL 数据结构常时按索引推送/弹出/随机访问,并具有指向元素的可靠指针
- 矢量指针索引处的读取元素
- C++函数返回两个 char 数组的相同索引元素
- 特征:类似 numpy 的元素索引
- 如何在另一个数组中找到一个未排序数组的不同元素索引?
- C 无法使用嵌套环将数组的元素索引
- Opencv Mat的元素索引产生垃圾
- 在c++中,当元组的元素索引在运行时已知时,是否有可能获得该元素的类型?
- 按内容访问QVector元素索引
- C++ 矢量元素索引