3. 使用 Intel 提供的软件开发工具


为了推广 AVX 指令,Intel 提供了一些免费的开发工具:

3.1 SDE 模拟器的使用

这里是 Intel 官方的使用说明:http://software.intel.com/en-us/articles/intel-software-development-emulator/

它的使用方式是:

sde [参数] -- <程序名>

只是简单的模拟执行,只需:

sde -- aos_soa_avx.exe

执行结果如下:

SDE 还附带一个 Intel 的 encode/decode 工具 XED

E:\visual studio work\aos_soa_avx\x64\Release>xed -n 20 -i aos_soa_avx.exe
# Intel64 format
WARNING: DBGHELP initialization failed. Please copy the appropriate
  (ia32,intel64) dbghelp.dll to the directory where your xed.exe exists.
   Version 6.9.3.113 or later is required.
# SECTION 0
XDIS 140001000: DATAXFER  BASE  48895C2408               mov qword ptr [rsp+0x8], rbx
XDIS 140001005: DATAXFER  BASE  4889742410               mov qword ptr [rsp+0x10], rsi
XDIS 14000100a: DATAXFER  BASE  48897C2418               mov qword ptr [rsp+0x18], rdi
XDIS 14000100f: PUSH      BASE  55                       push rbp
XDIS 140001010: PUSH      BASE  4154                     push r12
XDIS 140001012: PUSH      BASE  4155                     push r13
XDIS 140001014: BINARY    BASE  4881EC90060000           sub rsp, 0x690
XDIS 14000101b: MISC      BASE  488D6C2440               lea rbp, ptr [rsp+0x40]
XDIS 140001020: LOGICAL   BASE  4883E5E0                 and rbp, 0xffffffffffffffe0
XDIS 140001024: DATAXFER  BASE  488B05D51F0000           mov rax, qword ptr [rip+0x1fd5]
XDIS 14000102b: LOGICAL   BASE  4833C4                   xor rax, rsp
XDIS 14000102e: DATAXFER  BASE  48898540060000           mov qword ptr [rbp+0x640], rax
XDIS 140001035: DATAXFER  SSE   0F2805D4110000           movaps xmm0, xmmword ptr [rip+0x11d4]
XDIS 14000103c: MISC      BASE  488D8D40030000           lea rcx, ptr [rbp+0x340]
XDIS 140001043: LOGICAL   BASE  33D2                     xor edx, edx
XDIS 140001045: DATAXFER  BASE  41B800030000             mov r8d, 0x300
XDIS 14000104b: DATAXFER  SSE   0F298520030000           movaps xmmword ptr [rbp+0x320], xmm0
XDIS 140001052: DATAXFER  SSE   0F298530030000           movaps xmmword ptr [rbp+0x330], xmm0
XDIS 140001059: CALL      BASE  E858090000               call 0x1400019b6
XDIS 14000105e: MISC      BASE  4C8DA520030000           lea r12, ptr [rbp+0x320]
# Errors: 0
#Total decode cycles:        561520
#Total instructions decoded: 20
#Total cycles/instructions decoded: 28076.000000
#Bad times: 0
#Total input length bytes: 101
#Total output length bytes: 0
#Growth bytes: 0
#Shrinkage bytes: 0
#Growth/Shrinkage  bytes: -101
#Code size growth percent: -100.000000

上面例子反汇编了 aos_soa_avx.exe 的 20 条指令

3.2 代码分析器

IACA 的下载和说明在:http://software.intel.com/en-us/articles/intel-architecture-code-analyzer/

为了使 IACA 可以进行分析某一段代码,我们需要在代码中插入两个标记:

void foo ()
{
        IACA_START

        ... ...              /* 需要分析的代码放这里 */

        IACA_END
}

IACA_STARTIACA_END 在 Intel 提供的头文件 <iacaMarks.h> 里定义,在安装 IACA 时附上。

IACA_START 宏是两条指令,定义为:

mov ebx, 111             ; bb 6f 00 00
nop                      ; 64 67 90

IACA_END 宏定义为:

mov ebx, 222             ; bb de 00 00
nop                      ; 64 67 90

它们标记着要分析的代码的起止点,可以在 c/c++ 代码里插入 inline assembly 方面插入这个标记,如果编译器不支持 inline assembly 行为,只能以手工编辑 Hex 文件的方式,插入这两个 encode 序列。

为了分析 aos_soa_avx_iaca.exe 程序,我们可以使用下面的命令行:

E:\visual studio work\aos_soa_avx\x64\Release>iaca -64 -o iaca_out.txt aos_soa_avx_iaca.exe
Finished analyzing. Output written to - iaca_out.txt

结果如下:

Intel(R) Architecture Code Analyzer Version - 1.1.3
Analyzed File - aos_soa_avx_iaca.exe
Binary Format - 64Bit
Architecture  - Intel(R) AVX

Analysis Report
---------------
Total Throughput: 4 Cycles;             Throughput Bottleneck: Port4
Total number of Uops bound to ports:  11
Data Dependency Latency:    12 Cycles;  Performance Latency:    15 Cycles

Port Binding in cycles:
-------------------------------------------------------
|  Port  |  0 - DV |  1 |  2 -  D |  3 -  D |  4 |  5 |
-------------------------------------------------------
| Cycles |  2 |  0 |  1 |  2 |  2 |  2 |  2 |  4 |  2 |
-------------------------------------------------------

N  - port number, DV - Divider pipe (on port 0), D - Data fetch pipe (on ports 2 and 3)
CP - on a critical Data Dependency Path
N  - number of cycles port was bound
X  - other ports that can be used by this instructions
F  - Macro Fusion with the previous instruction occurred
^  - Micro Fusion happened
*  - instruction micro-ops not bound to a port
@  - Intel(R) AVX to Intel(R) SSE code switch, dozens of cycles penalty is expected
!  - instruction not supported, was not accounted in Analysis

| Num of |          Ports pressure in cycles          |    |
|  Uops  |  0 - DV |  1 |  2 -  D |  3 -  D |  4 |  5 |    |
------------------------------------------------------------
|   0*   |    |    |    |    |    |    |    |    |    |    | xor r11d, r11d
|   1    |    |    |  1 |    |    |    |    |    |    |    | lea eax, ptr [rsi+0xc]
|   1    |    |    |    |  1 |  2 |  X |  X |    |    | CP | vmovups ymm2, ymmword ptr [rbp+r11*1+0x320]
|   1    |    |    |    |  X |  X |  1 |  2 |    |    | CP | vmovups ymm1, ymmword ptr [rbp+r11*1+0x340]
|   1    |  1 |    |  X |    |    |    |    |    |  X |    | add r11, 0x20
|   1    |  1 |    |  X |    |    |    |    |    |  X |    | dec rax
|   1    |    |    |    |    |    |    |    |    |  1 | CP | vshufps ymm0, ymm2, ymm1, 0x88
|   1    |    |    |    |    |    |    |    |    |  1 | CP | vshufps ymm1, ymm2, ymm1, 0xdd
|   2    |    |    |    |  1 |    |  X |    |  2 |    | CP | vmovups ymmword ptr [rbp+r11*1-0x20], ymm0
|   2    |    |    |    |  X |    |  1 |    |  2 |    | CP | vmovups ymmword ptr [rbp+r11*1+0x170], ymm1

从这个结果我们可以得到下面的信息:

vmovups ymmword ptr [rbp+r11+0x170] 指令为例, 它的 AGU(地址生成单元)可以被分配到 port 2 或 port 3,实际执行时被分配在 port 3 的 AGU 管道。它的 store 操作被分配到 port 4,花费 2 个 cycle(每个 cycle 执行 128 位的 store),这条指令总共花费 2 cycles,它在译码时被译为 2 个 Uops(微操作码)。

而 add 与 dec 这类通用指令,可以被分配在 port 0,port 1 以及 port 5,这 3 个端口也是 64 位的 ALU 执行端口。

3.2 Intrinsics Guide 实用的指令手册

Intel c/c++ 和 visual c/c++ 这两个编译器都提供了 intrinsics 实现,类似于函数库提供,编译器将函数映射到相应的指令。visual studio 2010 sp1 上实现了对 AVX 指令的 intrinsics 库,它是兼容于 Intel 编译器提供的 intrinsics 库。

如上面所示:

这个 intrinsics 函数 _mm256_insertf128_ps() 将直接映射到 vinsertf128 指令,以 c/c++ 层面上,编译器会以函数形式检查它的使用,我们可以如下面这样使用它:

使用这种方式避免了直接在 c/c++ 插入 inline assembly 语句,增加了代码可读性可易用性,

__m256 ymm0, ymm1                             /* __m256 variable */
__m128 xmm0;                                  /* __m128 variable */


... ...

ymm0 = _mm256_insertf128_ps(ymm1, xmm0, 1);         /* vinsertf128 */


... ...

值得注意的是:

编译器只保证 _mm256_insertf128_ps() 映射到 vinsertf128 指令,并不保证参数和结果映射到哪个 xmm/ymm 寄存器上

版权 mik 所有,转载请注明出处