发布于 2015-12-29 09:15:34
用KNC对16个整数进行约简是一个有趣的例子,可以说明它为什么与AVX512不同。
_mm512_reduce_add_epi32内部只支持英特尔的编译(当前)。这是一个恼人的许多指令本质,如在SVML。但是我想我理解为什么Intel实现了这个内在的,就像在这个例子中一样,因为KNC和AVX512的结果是非常不同的。
有了AVX512,我会做这样的事
__m256i hi8 = _mm512_extracti64x4_epi64(a,1);
__m256i lo8 = _mm512_castsi512_si256(a);
__m256i vsum1 = _mm256_add_epi32(hi8,lo8);然后,我会做一个减少,就像在AVX2
__m256i vsum2 = _mm256_hadd_epi32(vsum1,vsum1);
__m256i vsum3 = _mm256_hadd_epi32(vsum2,vsum2);
__m128i hi4 = _mm256_extracti128_si256(vsum3,1);
__m128i lo4 = _mm256_castsi256_si128(vsum3);
__m128i vsum4 = _mm_add_epi32(hi4, lo4);
int sum = _mm_cvtsi128_si32(vsum4);看看英特尔是如何用_mm512_reduce_add_epi32实现AVX512的,这将是很有趣的。
但是KNC指令集不支持AVX或SSE,所以一切都必须用KNC完成完整的512位向量。英特尔已经创建了KNC独有的指令来完成这一任务。
从贾尔斯的回答来看,我们可以看到它的作用。首先,它使用KNC特有的指令将上256位转换为下256位,如下所示:
vpermf32x4 $238, %zmm0, %zmm1238值在基4中是3232,所以zmm1在四条128位车道上是(3,2,3,2).
接下来,它做一个向量和
vpaddd %zmm0, %zmm1, %zmm3给出了四条128位通道的(3+3, 2+2, 3+1, 2+0)。
然后,它改变了第二条128位的车道,让(3+1, 3+1, 3+1, 3+1)像这样
vpermf32x4 $85, %zmm3, %zmm2其中85是基4中的1111,然后将它们相加在一起
vpaddd %zmm3, %zmm2, %zmm4 因此,zmm4中的下128位车道包含四条128位车道(3+2+1+0)的总和.
此时,它需要改变每个128位车道内的32位值。同样,它使用了KNC的一个独特特性,允许它同时改变和添加(或者至少表示法是唯一的)。
vpaddd %zmm4{badc}, %zmm4, %zmm5 生产(a+b, a+b, c+d, c+d)
和
vpaddd %zmm5{cdab}, %zmm5, %zmm6产生(a+b+c+d , a+b+c+d , a+b+c+d, a+b+c+d)。现在这只是一个提取下32位的问题。
下面是AVX512的另一种解决方案,类似于KNC的解决方案
#include <x86intrin.h>
int foo(__m512i a) {
__m512i vsum1 = _mm512_add_epi32(a,_mm512_shuffle_i64x2(a,a, 0xee));
__m512i vsum2 = _mm512_add_epi32(a,_mm512_shuffle_i64x2(vsum1,vsum1, 0x55));
__m512i vsum3 = _mm512_add_epi32(a,_mm512_shuffle_epi32(vsum2, _MM_PERM_BADC));
__m512i vsum4 = _mm512_add_epi32(a,_mm512_shuffle_epi32(vsum3, _MM_PERM_CADB));
return _mm_cvtsi128_si32(_mm512_castsi512_si128(vsum4));
}对于gcc -O3 -mavx512f,这给出了。
vshufi64x2 $238, %zmm0, %zmm0, %zmm1
vpaddd %zmm1, %zmm0, %zmm1
vshufi64x2 $85, %zmm1, %zmm1, %zmm1
vpaddd %zmm1, %zmm0, %zmm1
vpshufd $78, %zmm1, %zmm1
vpaddd %zmm0, %zmm1, %zmm1
vpshufd $141, %zmm1, %zmm1
vpaddd %zmm0, %zmm1, %zmm0
vmovd %xmm0, %eax
retAVX512使用vshufi64x2代替vpermf32x4和KNC,将在车道内的排列和添加与{abcd}符号(例如vpaddd %zmm4{badc}, %zmm4, %zmm5)结合起来。这基本上就是使用_mm256_hadd_epi32实现的。
我忘了我已经看过AVX512的这个问题了。这是另一个解决方案。
这里的价值在于KNC的本质(未经测试)。
int foo(__m512i a) {
__m512i vsum1 = _mm512_add_epi32(a,_mm512_permute4f128_epi32(a, 0xee));
__m512i vsum2 = _mm512_add_epi32(a,_mm512_permute4f128_epi32(vsum1, 0x55));
__m512i vsum3 = _mm512_add_epi32(a,_mm512_swizzle_epi32(vsum2, _MM_SWIZ_REG_BADC));
__m512i vsum4 = _mm512_add_epi32(a,_mm512_swizzle_epi32(vsum3, _MM_SWIZ_REG_CADB));
int32_t out[2];
_mm512_packstorelo_epi32(out, vsum4);
return out[0];
}我看不出KNC的_mm512_permute4f128_epi32(a,imm8和and 512的_mm512_shuffle_i32x4(a,a,imm8)在功能上有什么区别。
这种情况的主要区别是,_mm512_shuffle_epi32生成vpshufd,而_mm512_swizzle_epi32不生成。这似乎是KNC相对于AVX512的一个优势。
发布于 2015-12-23 06:52:12
当涉及到读取程序集时,我几乎一无所知,所以我只是这样做了:
创建了如下所示的foo.c文件:
#include "immintrin.h"
int foo(__m512i a) {
return _mm512_reduce_add_epi32(a);
}我使用-mmic -S编写了英特尔编译器16.0.1版。它给了我以下的装配代码:
# -- Begin foo
.text
# mark_begin;
# Threads 4
.align 16,0x90
.globl foo
# --- foo(__m512i)
foo:
# parameter 1: %zmm0
..B1.1: # Preds ..B1.0 Latency 53
.cfi_startproc
..___tag_value_foo.1:
..L2:
#3.20
movl $1, %eax #4.12 c1
vpermf32x4 $238, %zmm0, %zmm1 #4.12 c5
kmov %eax, %k1 #4.12 c5
vpaddd %zmm0, %zmm1, %zmm3 #4.12 c9
nop #4.12 c13
vpermf32x4 $85, %zmm3, %zmm2 #4.12 c17
vpaddd %zmm3, %zmm2, %zmm4 #4.12 c21
nop #4.12 c25
vpaddd %zmm4{badc}, %zmm4, %zmm5 #4.12 c29
nop #4.12 c33
vpaddd %zmm5{cdab}, %zmm5, %zmm6 #4.12 c37
nop #4.12 c41
vpackstorelps %zmm6, -8(%rsp){%k1} #4.12 c45
movl -8(%rsp), %eax #4.12 c49
ret #4.12 c53
.align 16,0x90
.cfi_endproc
# LOE
# mark_end;
.type foo,@function
.size foo,.-foo
.data
# -- End foo
.data
.section .note.GNU-stack, ""
// -- Begin DWARF2 SEGMENT .eh_frame
.section .eh_frame,"a",@progbits
.eh_frame_seg:
.align 8
# End我想你应该能找到自己的路.
https://stackoverflow.com/questions/34428061
复制相似问题