问题描述
基本上,我正在尝试将小于uint8_t
寄存器的uint16_t
或__m256i
数组加载到__m256i
寄存器中并填充所有位在目标__m256i
中,数组未填充1。
我想要使用AVX512
的示例是:
#define ARR_SIZE_EPI8 (some_constant_value < 32)
// partial load for uint8_t
partial_load_epi8(uint8_t * arr) {
__m256i ones = _mm256_set1_epi64x(-1)
return _mm256_mask_loadu_epi8(ones,(1 << ARR_SIZE_EPI8) - 1,arr);
}
#define ARR_SIZE_EPI16 (some_constant_value < 16)
// partial load for uin16_t
partial_load_epi16(uint16_t * arr) {
__m256i ones = _mm256_set1_epi64x(-1)
return _mm256_mask_loadu_epi16(ones,(1 << ARR_SIZE_EPI16) - 1,arr);
}
如果AVX2
我可以使用,则仅使用ARR_SIZE * sizeof(T) % sizeof(int) == 0
:
partial_load_epi16_avx2(uint16_t * arr) {
__m256i mask_vec = _mm256_set_epi32( /* proper values for ARR_SIZE_EPI16 elements */ );
__m256i fill_vec = _mm256_set_epi16( /* 1s until ARR_SIZE_EPI16 * sizeof(uint16_t) */ );
__m256i load_vec = _mm256_maskloadu_epi32((int32_t *)arr,mask_vec);
return _mm256_or_si256(load_vec,fill_vec);
}
这使用了大约.rodate,但是似乎并不昂贵。另一方面,当ARR_SIZE * sizeof(T) % sizeof(int) != 0
即uint16_t
和ARR_SIZE_EPI16
时,我能想到的最好的方法是
partial_load_epi16_avx2_not_aligned(uint16_t * arr) {
__m256i mask_vec = _mm256_set_epi32( /* proper values for ARR_SIZE_EPI16 elements */ );
uint32_t tmp = 0xffff0000 | arr[ARR_SIZE_EPI16];
__m256i fill_vec = _mm256_set_epi32( /* 1s until ARR_SIZE_EPI16 * sizeof(uint16_t) / sizeof(int32_t) */,tmp,/* 0s */ );
__m256i load_vec = _mm256_maskloadu_epi32((int32_t *)arr,fill_vec);
}
// or
partial_load_epi16_avx_not_aligned(uint16_t * arr) {
__m256i fill_v = _mm256_set1_epi64x(-1);
__m256i pload = _mm256_maskload_epi32((int32_t *)arr,_mm256_set_epi32( /* Assume proper mask */ ));
fill_v = _mm256_insert_epi16(fill_v,arr[ARR_SIZE_EPI16],ARR_SIZE_EPI16);
return _mm256_blend_epi32(fill_v,pload,(1 << ((ARR_SIZE_EPI16 / 2) - 1)));
}
添加了vextractsi128
,vpinsrw
和vinsertsi128
。我想知道是否有没有那么多开销的更好方法。
谢谢!
编辑:
内存将由用户提供,我无法对是否可以访问arr
之前或之后arr + ARR_SIZE
进行任何假设。
用例:实现分类网络。实施2级幂的排序网络的指令通常比2级幂(尤其是字节/ 2字节值)的排序网络效率要高得多,因此我想做的是加载用户数组,然后填充它具有最大值(现在就做无符号的情况),这样我就可以将排序网络的大小四舍五入到2的下一个幂。
编辑: VPBLENDD和VPBLENDVB不能替代VMOVDQU
编辑:
有趣的是,我发现的最佳解决方案是将数组作为操作数3内联 请勿这样做 vpblendvb
。
Edit2:
测试程序以查看vpblendd
和vpblendvb
是否引起额外的页面错误。
#include <immintrin.h>
#include <stdint.h>
#include <sys/mman.h>
#include <utility>
#define N 5
template<uint32_t... e>
constexpr __m256i inline __attribute__((always_inline))
load_N_kernel2(std::integer_sequence<uint32_t,e...> _e) {
return _mm256_set_epi8(e...);
}
template<uint32_t... e>
constexpr __m256i inline __attribute__((always_inline))
load_N_kernel(std::integer_sequence<uint32_t,e...> _e) {
return load_N_kernel2(
std::integer_sequence<uint32_t,((((31 - e) / 4) < N) << 7)...>{});
}
constexpr __m256i inline __attribute__((always_inline)) load_N() {
return load_N_kernel(std::make_integer_sequence<uint32_t,32>{});
}
__m256i __attribute__((noinline)) mask_load(uint32_t * arr) {
__m256i tmp;
return _mm256_mask_loadu_epi32(tmp,(1 << N) - 1,arr);
}
__m256i __attribute__((noinline)) blend_load(uint32_t * arr) {
__m256i tmp;
asm volatile("vpblendd %[m],(%[arr]),%[tmp],%[tmp]\n\t"
: [ tmp ] "=x"(tmp)
: [ arr ] "r"(arr),[ m ] "i"(((1 << N) - 1))
:);
return tmp;
}
__m256i __attribute__((noinline)) blend_load_epi8(uint32_t * arr) {
__m256i tmp = _mm256_set1_epi8(uint8_t(0xff));;
__m256i mask = load_N();
asm volatile("vpblendvb %[mask],%[tmp]\n\t"
: [ tmp ] "+x"(tmp)
: [ arr ] "r"(arr),[ mask ] "x"(mask)
:);
return tmp;
}
void __attribute__((noinline)) mask_store(uint32_t * arr,__m256i v) {
return _mm256_mask_storeu_epi32(arr,v);
}
#define NPAGES (1000)
#define END_OF_PAGE (1024 - N)
#ifndef LOAD_METHOD
#define LOAD_METHOD blend_load
#endif
int
main() {
uint32_t * addr = (uint32_t *)
mmap(NULL,NPAGES * 4096,PROT_READ | PROT_WRITE,MAP_ANONYMOUS | MAP_PRIVATE,-1,0);
for(uint32_t i = 0; i < NPAGES; i += 2) {
mask_store(addr + 1024 * i + END_OF_PAGE,LOAD_METHOD(addr + END_OF_PAGE));
}
}
Ran:
$> perf stat -e page-faults,page-faults ./partial_load
结果与LOAD_METHOD
,blend_load
,mask_load
和blend_load_epi8
相同:
Performance counter stats for './partial_load':
548 page-faults
548 page-faults
0.002155974 seconds time elapsed
0.000000000 seconds user
0.002276000 seconds sys
Edit3:
注意是使用clang编译的,它不使用vpblendd
来实现_mm256_mask_loadu_epi32
。
以下是函数的汇编:
0000000000401130 <_Z9mask_loadPj>:
401130: b0 1f mov $0x1f,%al
401132: c5 fb 92 c8 kmovd %eax,%k1
401136: 62 f1 7e a9 6f 07 vmovdqu32 (%rdi),%ymm0{%k1}{z}
40113c: c3 retq
40113d: 0f 1f 00 nopl (%rax)
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)