PC平台的SIMD支持檢測


如果我們希望在用SIMD來提升程序處理的性能,首先需要做的就是檢測程序所運行的平台是否支持相應的SIMD擴展。平台對SIMD擴展分為兩部分的支持:

  • CPU對SIMD擴展的支持。SIMD擴展是隨着CPU的發展不斷改進的,CPU為SIMD擴展提供了硬件上的最基本支持。
  • 操作系統對SIMD擴展的支持。目前PC上運行的基本都是多任務操作系統,也就是會“同時”運行着多個程序,這些程序依靠操作系統進行任務調度以使得多個程序看起來是在同時運行,在進行任務調度時需要進行程序上下文的切換,其中就包括寄存器內容的保存與恢復。操作系統對SIMD擴展的支持總的來說主要是在任務上下文切換時,對該SIMD擴展所使用的寄存器的保存與恢復。

 

 

CPU Identification

檢查平台對SIMD擴展的支持,必不可少的就是指令CPUIDCPUID即CPU Identification,通過該指令能獲取到CPU相關的各種信息,其中包括CPU制造商、CPU版本、CPU串號、對各種擴展的支持、Cache相關信息等等。

如果EFLAGS寄存器的ID flag(bit 21)能被置1或者置零的話,則表明能使用CPUID指令。CPUID沒有任何操作數,不過CPUID卻是一個功能性的指令,有輸入以及輸出。

  • 輸入用的寄存器為EAX(有時也會用到ECX作為擴展輸入),用於指定CPUID的功能。在執行CPUID指令前需要往EAX寄存器寫入相應的值。
  • 輸出用的寄存器為EAX、EBX、ECX、EDX共四個。在CPUID指令執行后可以從這四個寄存器中獲取到所需要的信息。

如下所示為CPUID的一些基本功能

Information Returned by CPUID Instruction
Initial EAX
Value
Information Provided about the Processor
Basic CPUID Information
00H
EAX Maximum Input Value for Basic CPUID information.
EBX "Genu"
ECX "ntel"
EDX "inel"
01H
EAX Version Information: Type,Family,Model, and Stepping ID.
EBX Bits 07-00: Brand Index.
Bits 15-08: CLFLUSH line size(Value*8 = cache line size in bytes; used also by CLFLUSHOPT).
Bits 23-16: Maximum number of addressable IDs for logical processors in this physical package.
Bits 31-24: Initial APIC ID.
ECX Feature Information.
EDX Feature Information.
02H
EAX Cache and TLB Information.
EBX Cache and TLB Information.
ECX Cache and TLB Information.
EDX Cache and TLB Information.
03H
EAX Reserved.
EBX Reserved.
ECX Bit 00-31 of 96 bit processor serial number.(Available in P3 only, otherwise reserved.)
EDX Bit 32-64 of 96 bit processor serial number.(Available in P3 only, otherwise reserved.)
...

 

 

檢查CPU對SIMD擴展的支持

通過指令CPUID可以檢測CPU對SIMD擴展的支持。在輸入為EAX=01H的情況下執行CPUID,可以使得ECX以及EDX返回如下信息:

image

image

 

其中與SIMD擴展硬件支持的相關bit如下,當相應的bit為1時表示支持該擴展:

SIMD Feature Returned by CPUID Instruction
Register Bit Feature
ECX 0 SSE3
9 SSSE3
12 FMA
19 SSE4.1
20 SSE4.2
25 AES
28 AVX
EDX 23 MMX
25 SSE
26 SSE2

 

在輸入為EAX=07H的情況下執行CPUID,可以通過返回的寄存器EBX上的bit5以及bit16分別檢測CPU對AVX2以及AVX512的支持狀況。

SIMD Feature Returned by CPUID Instruction
Register Bit Feature
EBX 5 AVX2
16 AVX512

 

 

檢查操作系統對SIMD擴展的支持

程序通過訪問寄存器XCR0(eXterned Control Register)可以得到操作系統對SIMD擴展的支持信息。該寄存器通過XSETBV進行設置,通過XGETBV進行讀取。

回顧上一小節,可以看到EAX=01H CPUID.ECX的26、27bit分別為XSAVE以及OSXSAVE。其中XSAVE為1則表示存在XCR0寄存器,並且可以通過XSETBV以及XGETBV訪問該寄存器。對操作系統來說,會先查看處理器是否支持XSAVE,如果支持,則會根據操作系統自身的實現情況去設置XCR0寄存器。不過,操作系統與一般的程序有不同的權限,操作系統可以通過設置CR4寄存器的bit8(CR4.OSXSAVE)來控制一般程序對XCR0的訪問權限,CPUID得到的OSXSAVE(bit27)表示的就是操作系統是否設置了允許一般程序去訪問XCR0寄存器,我們一般的程序只需要去判斷這一個bit就知道是否能訪問XCR0。

XSAVE Feature Returned by CPUID Instruction
Register Bit Feature Description
ECX 26 XSAVE 為1則表明該處理器支持XSAVE/XRSTOR,支持擴展state,支持XSETBV/XGETBV之類,支持XCR0寄存器
27 OSXSAVE 為1則表明操作系統允許一般程序通過XSETBV/SGETBV訪問XCR0寄存器,允許一般程序通過XSAVE/XRSTOR訪問擴展state

 

如果OSXSAVE=1,則可以通過XGETBV指令訪問XCR0寄存器,得到系統對SIMD擴展的支持信息。指令XGETBV同樣也沒有任何的操作數,不過也存在輸入與輸出。其中輸入為ECX,用於指定XCR寄存器,一般只有XCR0,即ECX=0。輸出有64bit,保存於EDX:EAX。

XGETBV的輸出,即返回值的各個bit有如下指示:

image

XCR0 SIMD Feature
Register Bit Feature Description
EAX 0 MMX/FPU 這個bit必為1,表明操作系統支持MMX以及ST寄存器
1 SSE 為1則表明操作系統支持XMM寄存器,32位時為XMM0~XMM7,64位時為XMM0~XMM15
2 AVX 為1則表明操作系統支持YMM寄存器,32位時為YMM0~YMM7,64位時為YMM0~YMM15
6 ZMM_Hi265 為1則表明操作系統支持ZMM寄存器,32位時為ZMM0~ZMM7,64位時為ZMM0~ZMM15
7 Hi16_ZMM 為1則表明如果CPU工作在64位模式,則操作系統支持ZMM16~ZMM31

 

 

總結

查看平台對SIMD擴展的支持需要分別檢查處理器以及操作系統對SIMD擴展的支持。

  • 處理器:調用EAX=1 CPUID,然后查看所返回的ECX或EDX寄存器相應的bit。
  • 操作系統:調用EAX=1 CPUID,然后查看ECX.OSXSAVE(bit27),如果為1則調用XGETBV去獲取XCR0,最后查看返回值EAX上相應的bit。

GCC:

#include <stdio.h>

inline static void cpuid (unsigned int output[4], unsigned int EAX, unsigned int ECX) {	
    unsigned int a, b, c, d;
    __asm("cpuid"  : "=a"(a),"=b"(b),"=c"(c),"=d"(d) : "a"(EAX),"c"(ECX) : );
    output[0] = a;
    output[1] = b;
    output[2] = c;
    output[3] = d;
}

inline static unsigned int xgetbv (unsigned int ECX) {
    unsigned int ret = 0;
    __asm("xgetbv" : "=a"(ret) : "c"(ECX) : );
    return ret;
}

int main(){
    unsigned int CPUInfo[4] = {0}, ECX = 0, EDX = 0, XCR0_EAX = 0;
    cpuid(CPUInfo, 1, 0);
    ECX = CPUInfo[2];
    EDX = CPUInfo[3];

    if(EDX & 0x00800000)
        printf("CPU Support MMX\n");
    if(EDX & 0x02000000)
        printf("CPU Support SSE\n");
    if(EDX & 0x04000000)
        printf("CPU Support SSE2\n");
    if(ECX & 1)
        printf("CPU Support SSE3\n");
    if(ECX & 0x00000200)
        printf("CPU Support SSSE3\n");
    if(ECX & 0x00080000)
        printf("CPU Support SSE4.1\n");
    if(ECX & 0x00100000)
        printf("CPU Support SSE4.2\n");
    if(ECX & 0x02000000)
        printf("CPU Support AES\n");
    if(ECX & 0x10000000)
        printf("CPU Support AVX\n");
    if(ECX & 0x08000000)
        printf("OS Support XSAVE\n");
    else{
        printf("OS not Support XSAVE, OS not Support SIMD\n");
        return -1;
    }

    XCR0_EAX = xgetbv(0);
    if(XCR0_EAX & 0x00000002)
        printf("OS Support SSE/SSE2/SSE3/SSE4\n");
    if(XCR0_EAX & 0x00000004)
        printf("OS Support AVX\n");
    if(XCR0_EAX & 0x00000040)
        printf("OS Support AVX-512\n");

    printf("ECX=%x, EDX=%x, XCR0_EAX=%x\n", ECX, EDX, XCR0_EAX);
    return 0;
}

YASM:

global SIMD_Support

;this code just check SSE3 support
;bit0:SSE3, bit27:OSXSAVE
%define CPU_SUPPORT_CONST_ECX 0x08000001
;bit23:MMX, bit25:SSE, bit26:SSE2
%define CPU_SUPPORT_CONST_EDX 0x06800000

;bit2:XMM
%define OS_SUPPORT_CONST 2

SIMD_Support: 
    ;processor supports
    mov eax, 1
    cpuid
    and ecx, CPU_SUPPORT_CONST_ECX
    cmp ecx, CPU_SUPPORT_CONST_ECX
    jne not_supported 
    and edx, CPU_SUPPORT_CONST_EDX
    cmp edx, CPU_SUPPORT_CONST_EDX
    jne not_supported 

    ;OS supports
    mov ecx, 0
    XGETBV; result in EDX:EAX
    and eax, OS_SUPPORT_CONST
    cmp eax, OS_SUPPORT_CONST
    jne not_supported

    mov eax, 0 ; return 0
    ret

not_supported:
    mov eax, -1 ; return -1
    ret

 

Reference:

Intel 64 and IA-32 Architectures Software Developer's Manual


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM