首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Xeon减缩操作的内联装配

Xeon减缩操作的内联装配
EN

Stack Overflow用户
提问于 2015-12-23 03:14:58
回答 2查看 357关注 0票数 3

我正在寻找为Xeon添加减少操作的内联组装操作。我在英特尔内部网站(链接)上发现了链接内禀。然而,在网站上,他们没有提到它的实际装配操作。

有人能帮我在Xeon平台上建立减缩操作的内联装配吗?

谢谢

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-12-29 09:15:34

用KNC对16个整数进行约简是一个有趣的例子,可以说明它为什么与AVX512不同。

_mm512_reduce_add_epi32内部只支持英特尔的编译(当前)。这是一个恼人的许多指令本质,如在SVML。但是我想我理解为什么Intel实现了这个内在的,就像在这个例子中一样,因为KNC和AVX512的结果是非常不同的。

有了AVX512,我会做这样的事

代码语言:javascript
复制
__m256i hi8 = _mm512_extracti64x4_epi64(a,1);
__m256i lo8 = _mm512_castsi512_si256(a);
__m256i vsum1 = _mm256_add_epi32(hi8,lo8);

然后,我会做一个减少,就像在AVX2

代码语言:javascript
复制
__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位,如下所示:

代码语言:javascript
复制
vpermf32x4 $238, %zmm0, %zmm1

238值在基4中是3232,所以zmm1在四条128位车道上是(3,2,3,2).

接下来,它做一个向量和

代码语言:javascript
复制
vpaddd    %zmm0, %zmm1, %zmm3

给出了四条128位通道的(3+3, 2+2, 3+1, 2+0)

然后,它改变了第二条128位的车道,让(3+1, 3+1, 3+1, 3+1)像这样

代码语言:javascript
复制
vpermf32x4 $85, %zmm3, %zmm2

其中85是基4中的1111,然后将它们相加在一起

代码语言:javascript
复制
vpaddd    %zmm3, %zmm2, %zmm4 

因此,zmm4中的下128位车道包含四条128位车道(3+2+1+0)的总和.

此时,它需要改变每个128位车道内的32位值。同样,它使用了KNC的一个独特特性,允许它同时改变和添加(或者至少表示法是唯一的)。

代码语言:javascript
复制
vpaddd    %zmm4{badc}, %zmm4, %zmm5 

生产(a+b, a+b, c+d, c+d)

代码语言:javascript
复制
vpaddd    %zmm5{cdab}, %zmm5, %zmm6

产生(a+b+c+d , a+b+c+d , a+b+c+d, a+b+c+d)。现在这只是一个提取下32位的问题。

下面是AVX512的另一种解决方案,类似于KNC的解决方案

代码语言:javascript
复制
#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,这给出了。

代码语言:javascript
复制
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
ret

AVX512使用vshufi64x2代替vpermf32x4和KNC,将在车道内的排列和添加与{abcd}符号(例如vpaddd %zmm4{badc}, %zmm4, %zmm5)结合起来。这基本上就是使用_mm256_hadd_epi32实现的。

我忘了我已经看过AVX512的这个问题了。这是另一个解决方案

这里的价值在于KNC的本质(未经测试)。

代码语言:javascript
复制
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的一个优势。

票数 4
EN

Stack Overflow用户

发布于 2015-12-23 06:52:12

当涉及到读取程序集时,我几乎一无所知,所以我只是这样做了:

创建了如下所示的foo.c文件:

代码语言:javascript
复制
#include "immintrin.h"

int foo(__m512i a) {
    return _mm512_reduce_add_epi32(a);
}

我使用-mmic -S编写了英特尔编译器16.0.1版。它给了我以下的装配代码:

代码语言:javascript
复制
# -- 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

我想你应该能找到自己的路.

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34428061

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档