zoukankan      html  css  js  c++  java
  • [C] 跨平台使用Intrinsic函数范例3——使用MMX、SSE2指令集 处理 32位整数数组求和

    作者:zyl910

      本文面对对SSE等SIMD指令集有一定基础的读者,以32位整数数组求和为例演示了如何跨平台使用MMX、SSE2指令集。支持vc、gcc编译器,在Windows、Linux、Mac这三大平台上成功运行。

    一、关键讲解

      前文(http://www.cnblogs.com/zyl910/archive/2012/10/22/simdsumfloat.html)演示了如何使用SSE、AVX指令集 处理 单精度浮点数组求和。现在对其进行改造,使用MMX、SSE2指令集 处理 32位整数数组求和。因程序基本上差不多,文本就不详细讲解了,只说关键变化。


    1.1 指令集简介

      先来看看支持32位整数的SIMD的指令集——
    MMX指令集支持多种整数类型的运算。MMX定义了64位紧缩整数类型,,对应Intrinsic中的__m64类型,它能一次能处理2个32位整数。
    SSE指令集只支持单精度浮点运算,直到SSE2指令集才支持双精度浮点数运算。SSE2定义了128位紧缩整数类型,对应Intrinsic中的__m128i类型,它能一次能处理4个32位整数。
    AVX指令集只支持单精度和双精度浮点运算。据说2013年Haswell架构中的AVX2指令集才支持整数运算。

    1.2 改造为 SSE2的32位整数代码

      在使用Intrinsic函数时,将 SSE的单精度浮点代码 改造为 SSE2的32位整数代码是很方便的。对比前文与本文的数组求和代码,变更的地方有——

    float

    int32_t

    备注

    指令 Intrinsic Asm 指令 Intrinsic Asm
          MMX __m64 MMWORD 类型
        _mm_setzero_si64 PXOR 赋0
        * MOVQ 加载
        _mm_add_pi32 PADDD 加法
    SSE __m128 XMMWORD SSE2 __m128i XMMWORD 类型
    _mm_setzero_ps XORPS _mm_setzero_si128 PXOR 赋0
    _mm_load_ps MOVAPS _mm_load_si128 MOVQ 加载
    _mm_add_ps ADDPS _mm_add_epi32 PADDD 加法
    AVX __m256 YMMWORD       类型
    _mm256_setzero_ps VXORPS     赋0
    _mm256_load_ps VMOVAPS     加载
    _mm256_add_ps VADDPS     加法

      其次,还需要调整一下地址计算。因_mm_load_si128与_mm_load_ps不同,是直接采用__m128i指针一次性处理128位,而不是以元素宽度(如float、int32_t),所以循环与地址计算的代码有较大变化——
    1. p指针的类型由“const float*”变为“const __m128i*”。为了适应_mm_load_si128。
    2. q指针的含义发生了变化。现在作为单个数据处理时所用指针,即处理SIMD结果的合并,又处理剩下的数据。
    3. p指针移动时直接“p++”。而四路循环版中移动指针是“p+=4”,加载时可以写成“_mm_load_si128(p+1)”,地址计算也很方便。

      例如sumfloat_sse与sumint_sse——

    // 单精度浮点数组求和_SSE版.
    float sumfloat_sse(const float* pbuf, size_t cntbuf)
    {
        float s = 0;    // 求和变量.
        size_t i;
        size_t nBlockWidth = 4;    // 块宽. SSE寄存器能一次处理4个float.
        size_t cntBlock = cntbuf / nBlockWidth;    // 块数.
        size_t cntRem = cntbuf % nBlockWidth;    // 剩余数量.
        __m128 xfsSum = _mm_setzero_ps();    // 求和变量。[SSE] 赋初值0
        __m128 xfsLoad;    // 加载.
        const float* p = pbuf;    // SSE批量处理时所用的指针.
        const float* q;    // 将SSE变量上的多个数值合并时所用指针.
    
        // SSE批量处理.
        for(i=0; i<cntBlock; ++i)
        {
            xfsLoad = _mm_load_ps(p);    // [SSE] 加载
            xfsSum = _mm_add_ps(xfsSum, xfsLoad);    // [SSE] 单精浮点紧缩加法
            p += nBlockWidth;
        }
        // 合并.
        q = (const float*)&xfsSum;
        s = q[0] + q[1] + q[2] + q[3];
    
        // 处理剩下的.
        for(i=0; i<cntRem; ++i)
        {
            s += p[i];
        }
    
        return s;
    }
    
    // 32位整数数组求和_SSE版.
    int32_t sumint_sse(const int32_t* pbuf, size_t cntbuf)
    {
        int32_t s = 0;    // 求和变量.
        size_t i;
        size_t nBlockWidth = 4;    // 块宽. SSE寄存器能一次处理4个int32_t.
        size_t cntBlock = cntbuf / nBlockWidth;    // 块数.
        size_t cntRem = cntbuf % nBlockWidth;    // 剩余数量.
        __m128i xidSum = _mm_setzero_si128();    // 求和变量。[SSE2] PXOR. 赋初值0.
        __m128i xidLoad;    // 加载.
        const __m128i* p = (const __m128i*)pbuf;    // SSE批量处理时所用的指针.
        const int32_t* q;    // 单个数据处理时所用指针.
    
        // SSE批量处理.
        for(i=0; i<cntBlock; ++i)
        {
            xidLoad = _mm_load_si128(p);    // [SSE2] MOVDQA. 加载.
            xidSum = _mm_add_epi32(xidSum, xidLoad);    // [SSE2] PADDD. 32位整数紧缩环绕加法.
            p ++;
        }
        // 合并.
        q = (const int32_t*)&xidSum;
        s = q[0] + q[1] + q[2] + q[3];
    
        // 处理剩下的.
        q = (const int32_t*)p;
        for(i=0; i<cntRem; ++i)
        {
            s += q[i];
        }
    
        return s;
    }

    1.3 改造为 MMX版

      将SSE2版代码 改造为 MMX版代码也很方便,按照上一节的表格换用不同的数据类型和函数名,然后再调整一下地址计算就差不多了。

      只不过有两点要注意——
    1. MMX运算结束后,要记得调用_mm_empty(EMMS)清理MMX状态,使后续的浮点运算(FPU)能正常运行。
    2. MMX Intrinsic中没有提供_mm_load_si64这样的函数,要想从内存中加载数据到__m64变量,可以直接使用“*(指针)”运算符加载数据,但要保证地址是按8字节对齐的。


      例如sumint_mmx函数(可与上一节的sumint_sse函数进行比较)——

    // 32位整数数组求和_MMX版.
    int32_t sumint_mmx(const int32_t* pbuf, size_t cntbuf)
    {
        int32_t s = 0;    // 求和变量.
        size_t i;
        size_t nBlockWidth = 2;    // 块宽. MMX寄存器能一次处理2个int32_t.
        size_t cntBlock = cntbuf / nBlockWidth;    // 块数.
        size_t cntRem = cntbuf % nBlockWidth;    // 剩余数量.
        __m64 midSum = _mm_setzero_si64();    // 求和变量。[MMX] PXOR, 赋初值0.
        __m64 midLoad;    // 加载.
        const __m64* p = (const __m64*)pbuf;    // MMX批量处理时所用的指针.
        const int32_t* q;    // 单个数据处理时所用指针.
    
        // MMX批量处理.
        for(i=0; i<cntBlock; ++i)
        {
            midLoad = *p;    // [MMX] MOVQ. 加载.
            midSum = _mm_add_pi32(midSum, midLoad);    // [MMX] PADDD. 32位整数紧缩环绕加法.
            p ++;
        }
        // 合并.
        q = (const int32_t*)&midSum;
        s = q[0] + q[1];
    
        // 处理剩下的.
        q = (const int32_t*)p;
        for(i=0; i<cntRem; ++i)
        {
            s += q[i];
        }
    
        // 清理MMX状态.
        _mm_empty();    // [MMX] EMMS.
    
        return s;
    }


    1.4 环境检查

      最后,别忘了检查环境——
    INTRIN_MMX、INTRIN_SSE2 宏是 zintrin.h 提供的,可用来在编译时检测编译器是否支持MMX、SSE2指令集。
    simd_mmx、simd_sse_level函数是 ccpuid.h 提供的,可用来在运行时检测当前系统环境是否支持MMX、SSE2指令集。


    二、全部代码

    2.1 simdsumint.c

      全部代码——

    #define __STDC_LIMIT_MACROS    1    // C99整数范围常量. [纯C程序可以不用, 而C++程序必须定义该宏.]
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    
    #include "zintrin.h"
    #include "ccpuid.h"
    
    
    // Compiler name
    #define MACTOSTR(x)    #x
    #define MACROVALUESTR(x)    MACTOSTR(x)
    #if defined(__ICL)    // Intel C++
    #  if defined(__VERSION__)
    #    define COMPILER_NAME    "Intel C++ " __VERSION__
    #  elif defined(__INTEL_COMPILER_BUILD_DATE)
    #    define COMPILER_NAME    "Intel C++ (" MACROVALUESTR(__INTEL_COMPILER_BUILD_DATE) ")"
    #  else
    #    define COMPILER_NAME    "Intel C++"
    #  endif    // #  if defined(__VERSION__)
    #elif defined(_MSC_VER)    // Microsoft VC++
    #  if defined(_MSC_FULL_VER)
    #    define COMPILER_NAME    "Microsoft VC++ (" MACROVALUESTR(_MSC_FULL_VER) ")"
    #  elif defined(_MSC_VER)
    #    define COMPILER_NAME    "Microsoft VC++ (" MACROVALUESTR(_MSC_VER) ")"
    #  else
    #    define COMPILER_NAME    "Microsoft VC++"
    #  endif    // #  if defined(_MSC_FULL_VER)
    #elif defined(__GNUC__)    // GCC
    #  if defined(__CYGWIN__)
    #    define COMPILER_NAME    "GCC(Cygmin) " __VERSION__
    #  elif defined(__MINGW32__)
    #    define COMPILER_NAME    "GCC(MinGW) " __VERSION__
    #  else
    #    define COMPILER_NAME    "GCC " __VERSION__
    #  endif    // #  if defined(_MSC_FULL_VER)
    #else
    #  define COMPILER_NAME    "Unknown Compiler"
    #endif    // #if defined(__ICL)    // Intel C++
    
    
    //////////////////////////////////////////////////
    // sumint: 32位整数数组求和的函数
    //////////////////////////////////////////////////
    
    // 32位整数数组求和_基本版.
    //
    // result: 返回数组求和结果.
    // pbuf: 数组的首地址.
    // cntbuf: 数组长度.
    int32_t sumint_base(const int32_t* pbuf, size_t cntbuf)
    {
        int32_t s = 0;    // 求和变量.
        size_t i;
        for(i=0; i<cntbuf; ++i)
        {
            s += pbuf[i];
        }
        return s;
    }
    
    #ifdef INTRIN_MMX
    // 32位整数数组求和_MMX版.
    int32_t sumint_mmx(const int32_t* pbuf, size_t cntbuf)
    {
        int32_t s = 0;    // 求和变量.
        size_t i;
        size_t nBlockWidth = 2;    // 块宽. MMX寄存器能一次处理2个int32_t.
        size_t cntBlock = cntbuf / nBlockWidth;    // 块数.
        size_t cntRem = cntbuf % nBlockWidth;    // 剩余数量.
        __m64 midSum = _mm_setzero_si64();    // 求和变量。[MMX] PXOR, 赋初值0.
        __m64 midLoad;    // 加载.
        const __m64* p = (const __m64*)pbuf;    // MMX批量处理时所用的指针.
        const int32_t* q;    // 单个数据处理时所用指针.
    
        // MMX批量处理.
        for(i=0; i<cntBlock; ++i)
        {
            midLoad = *p;    // [MMX] MOVQ. 加载.
            midSum = _mm_add_pi32(midSum, midLoad);    // [MMX] PADDD. 32位整数紧缩环绕加法.
            p ++;
        }
        // 合并.
        q = (const int32_t*)&midSum;
        s = q[0] + q[1];
    
        // 处理剩下的.
        q = (const int32_t*)p;
        for(i=0; i<cntRem; ++i)
        {
            s += q[i];
        }
    
        // 清理MMX状态.
        _mm_empty();    // [MMX] EMMS.
    
        return s;
    }
    
    // 32位整数数组求和_MMX四路循环展开版.
    int32_t sumint_mmx_4loop(const int32_t* pbuf, size_t cntbuf)
    {
        int32_t s = 0;    // 返回值.
        size_t i;
        size_t nBlockWidth = 2*4;    // 块宽. MMX寄存器能一次处理2个int32_t,然后循环展开4次.
        size_t cntBlock = cntbuf / nBlockWidth;    // 块数.
        size_t cntRem = cntbuf % nBlockWidth;    // 剩余数量.
        __m64 midSum = _mm_setzero_si64();    // 求和变量。[MMX] PXOR, 赋初值0.
        __m64 midSum1 = _mm_setzero_si64();
        __m64 midSum2 = _mm_setzero_si64();
        __m64 midSum3 = _mm_setzero_si64();
        __m64 midLoad;    // 加载.
        __m64 midLoad1;
        __m64 midLoad2;
        __m64 midLoad3;
        const __m64* p = (const __m64*)pbuf;    // MMX批量处理时所用的指针.
        const int32_t* q;    // 单个数据处理时所用指针.
    
        // SSE批量处理.
        for(i=0; i<cntBlock; ++i)
        {
            midLoad = *p;    // [MMX] MOVQ. 加载.
            midLoad1 = *(p+1);
            midLoad2 = *(p+2);
            midLoad3 = *(p+3);
            midSum = _mm_add_pi32(midSum, midLoad);    // [MMX] PADDD. 32位整数紧缩环绕加法.
            midSum1 = _mm_add_pi32(midSum1, midLoad1);
            midSum2 = _mm_add_pi32(midSum2, midLoad2);
            midSum3 = _mm_add_pi32(midSum3, midLoad3);
            p += 4;    // 四路循环展开.
        }
        // 合并.
        midSum = _mm_add_pi32(midSum, midSum1);    // 两两合并(0~1).
        midSum2 = _mm_add_pi32(midSum2, midSum3);    // 两两合并(2~3).
        midSum = _mm_add_pi32(midSum, midSum2);    // 两两合并(0~3).
        q = (const int32_t*)&midSum;
        s = q[0] + q[1];
    
        // 处理剩下的.
        q = (const int32_t*)p;
        for(i=0; i<cntRem; ++i)
        {
            s += q[i];
        }
    
        // 清理MMX状态.
        _mm_empty();    // [MMX] EMMS.
    
        return s;
    }
    #endif    // #ifdef INTRIN_MMX
    
    
    #ifdef INTRIN_SSE2
    // 32位整数数组求和_SSE版.
    int32_t sumint_sse(const int32_t* pbuf, size_t cntbuf)
    {
        int32_t s = 0;    // 求和变量.
        size_t i;
        size_t nBlockWidth = 4;    // 块宽. SSE寄存器能一次处理4个int32_t.
        size_t cntBlock = cntbuf / nBlockWidth;    // 块数.
        size_t cntRem = cntbuf % nBlockWidth;    // 剩余数量.
        __m128i xidSum = _mm_setzero_si128();    // 求和变量。[SSE2] PXOR. 赋初值0.
        __m128i xidLoad;    // 加载.
        const __m128i* p = (const __m128i*)pbuf;    // SSE批量处理时所用的指针.
        const int32_t* q;    // 单个数据处理时所用指针.
    
        // SSE批量处理.
        for(i=0; i<cntBlock; ++i)
        {
            xidLoad = _mm_load_si128(p);    // [SSE2] MOVDQA. 加载.
            xidSum = _mm_add_epi32(xidSum, xidLoad);    // [SSE2] PADDD. 32位整数紧缩环绕加法.
            p ++;
        }
        // 合并.
        q = (const int32_t*)&xidSum;
        s = q[0] + q[1] + q[2] + q[3];
    
        // 处理剩下的.
        q = (const int32_t*)p;
        for(i=0; i<cntRem; ++i)
        {
            s += q[i];
        }
    
        return s;
    }
    
    // 32位整数数组求和_SSE四路循环展开版.
    int32_t sumint_sse_4loop(const int32_t* pbuf, size_t cntbuf)
    {
        int32_t s = 0;    // 返回值.
        size_t i;
        size_t nBlockWidth = 4*4;    // 块宽. SSE寄存器能一次处理4个int32_t,然后循环展开4次.
        size_t cntBlock = cntbuf / nBlockWidth;    // 块数.
        size_t cntRem = cntbuf % nBlockWidth;    // 剩余数量.
        __m128i xidSum = _mm_setzero_si128();    // 求和变量。[SSE2] PXOR. 赋初值0.
        __m128i xidSum1 = _mm_setzero_si128();
        __m128i xidSum2 = _mm_setzero_si128();
        __m128i xidSum3 = _mm_setzero_si128();
        __m128i xidLoad;    // 加载.
        __m128i xidLoad1;
        __m128i xidLoad2;
        __m128i xidLoad3;
        const __m128i* p = (const __m128i*)pbuf;    // SSE批量处理时所用的指针.
        const int32_t* q;    // 单个数据处理时所用指针.
    
        // SSE批量处理.
        for(i=0; i<cntBlock; ++i)
        {
            xidLoad = _mm_load_si128(p);    // [SSE2] MOVDQA. 加载.
            xidLoad1 = _mm_load_si128(p+1);
            xidLoad2 = _mm_load_si128(p+2);
            xidLoad3 = _mm_load_si128(p+3);
            xidSum = _mm_add_epi32(xidSum, xidLoad);    // [SSE2] PADDD. 32位整数紧缩环绕加法.
            xidSum1 = _mm_add_epi32(xidSum1, xidLoad1);
            xidSum2 = _mm_add_epi32(xidSum2, xidLoad2);
            xidSum3 = _mm_add_epi32(xidSum3, xidLoad3);
            p += 4;    // 四路循环展开.
        }
        // 合并.
        xidSum = _mm_add_epi32(xidSum, xidSum1);    // 两两合并(0~1).
        xidSum2 = _mm_add_epi32(xidSum2, xidSum3);    // 两两合并(2~3).
        xidSum = _mm_add_epi32(xidSum, xidSum2);    // 两两合并(0~3).
        q = (const int32_t*)&xidSum;
        s = q[0] + q[1] + q[2] + q[3];
    
        // 处理剩下的.
        q = (const int32_t*)p;
        for(i=0; i<cntRem; ++i)
        {
            s += q[i];
        }
    
        return s;
    }
    #endif    // #ifdef INTRIN_SSE2
    
    
    
    
    
    //////////////////////////////////////////////////
    // main
    //////////////////////////////////////////////////
    
    // 变量对齐.
    #ifndef ATTR_ALIGN
    #  if defined(__GNUC__)    // GCC
    #    define ATTR_ALIGN(n)    __attribute__((aligned(n)))
    #  else    // 否则使用VC格式.
    #    define ATTR_ALIGN(n)    __declspec(align(n))
    #  endif
    #endif    // #ifndef ATTR_ALIGN
    
    
    #define BUFSIZE    4096    // = 32KB{L1 Cache} / (2 * sizeof(int32_t))
    ATTR_ALIGN(32) int32_t buf[BUFSIZE];
    
    // 测试时的函数类型
    typedef int32_t (*TESTPROC)(const int32_t* pbuf, size_t cntbuf);
    
    // 进行测试
    void runTest(const char* szname, TESTPROC proc)
    {
        const int testloop = 4000;    // 重复运算几次延长时间,避免计时精度问题.
        const clock_t TIMEOUT = CLOCKS_PER_SEC/2;    // 最短测试时间.
        int i,j,k;
        clock_t    tm0, dt;    // 存储时间.
        double mps;    // M/s.
        double mps_good = 0;    // 最佳M/s. 因线程切换会导致的数值波动, 于是选取最佳值.
        volatile int32_t n=0;    // 避免内循环被优化.
        for(i=1; i<=3; ++i)    // 多次测试.
        {
            tm0 = clock();
            // main
            k=0;
            do
            {
                for(j=1; j<=testloop; ++j)    // 重复运算几次延长时间,避免计时开销带来的影响.
                {
                    n = proc(buf, BUFSIZE);    // 避免内循环被编译优化消掉.
                }
                ++k;
                dt = clock() - tm0;
            }while(dt<TIMEOUT);
            // show
            mps = (double)k*testloop*BUFSIZE*CLOCKS_PER_SEC/(1024.0*1024.0*dt);    // k*testloop*BUFSIZE/(1024.0*1024.0) 将数据规模换算为M,然后再乘以 CLOCKS_PER_SEC/dt 换算为M/s .
            if (mps_good<mps)    mps_good=mps;    // 选取最佳值.
            //printf("%s:\t%.0f M/s\t//%f\n", szname, mps, n);
        }
        printf("%s:\t%.0f M/s\t//%d\n", szname, mps_good, n);
    }
    
    int main(int argc, char* argv[])
    {
        char szBuf[64];
        int i;
    
        printf("simdsumint v1.00 (%dbit)\n", INTRIN_WORDSIZE);
        printf("Compiler: %s\n", COMPILER_NAME);
        cpu_getbrand(szBuf);
        printf("CPU:\t%s\n", szBuf);
        printf("\n");
    
        // init buf
        srand( (unsigned)time( NULL ) );
        for (i = 0; i < BUFSIZE; i++) buf[i] = (int32_t)(rand() & 0x7fff);    // 使用&0x7fff是为了使数值在一定范围内,便于观察结果是否正确.
    
        // test
        runTest("sumint_base", sumint_base);    // 32位整数数组求和_基本版.
    #ifdef INTRIN_MMX
        if (simd_mmx(NULL))
        {
            runTest("sumint_mmx", sumint_mmx);    // 32位整数数组求和_MMX版.
            runTest("sumint_mmx_4loop", sumint_mmx_4loop);    // 32位整数数组求和_MMX四路循环展开版.
        }
    #endif    // #ifdef INTRIN_MMX
    #ifdef INTRIN_SSE2
        if (simd_sse_level(NULL) >= SIMD_SSE_2)
        {
            runTest("sumint_sse", sumint_sse);    // 32位整数数组求和_SSE版.
            runTest("sumint_sse_4loop", sumint_sse_4loop);    // 32位整数数组求和_SSE四路循环展开版.
        }
    #endif    // #ifdef INTRIN_SSE2
    
        return 0;
    }


    2.2 makefile

      全部代码——

    # flags
    CC = g++
    CFS = -Wall -msse2
    
    # args
    RELEASE =0
    BITS =
    CFLAGS =
    
    # [args] 生成模式. 0代表debug模式, 1代表release模式. make RELEASE=1.
    ifeq ($(RELEASE),0)
        # debug
        CFS += -g
    else
        # release
        CFS += -O3 -DNDEBUG
        //CFS += -O3 -g -DNDEBUG
    endif
    
    # [args] 程序位数. 32代表32位程序, 64代表64位程序, 其他默认. make BITS=32.
    ifeq ($(BITS),32)
        CFS += -m32
    else
        ifeq ($(BITS),64)
            CFS += -m64
        else
        endif
    endif
    
    # [args] 使用 CFLAGS 添加新的参数. make CFLAGS="-mavx".
    CFS += $(CFLAGS)
    
    
    .PHONY : all clean
    
    # files
    TARGETS = simdsumint
    OBJS = simdsumint.o
    
    all : $(TARGETS)
    
    simdsumint : $(OBJS)
        $(CC) $(CFS) -o $@ $^
    
    
    simdsumint.o : simdsumint.c zintrin.h ccpuid.h
        $(CC) $(CFS) -c $<
    
    
    clean :
        rm -f $(OBJS) $(TARGETS) $(addsuffix .exe,$(TARGETS))


    三、编译测试

    3.1 编译

      在以下编译器中成功编译——
    VC6:x86版。
    VC2003:x86版。
    VC2005:x86版。
    VC2010:x86版、x64版。
    GCC 4.7.0(Fedora 17 x64):x86版、x64版。
    GCC 4.6.2(MinGW(20120426)):x86版。
    GCC 4.7.1(TDM-GCC(MinGW-w64)):x86版、x64版。
    llvm-gcc-4.2(Mac OS X Lion 10.7.4, Xcode 4.4.1):x86版、x64版。


    3.2 测试

      因虚拟机上的有效率损失,于是仅在真实系统上进行测试。

      系统环境——
    CPU:Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz
    操作系统:Windows 7 SP1 x64版

      然后分别运行VC与GCC编译的Release版可执行文件,即以下4个程序——
    exe\simdsumint_vc32.exe:VC2010 SP1 编译的32位程序,/O2 /arch:SSE2。
    exe\simdsumint_vc64.exe:VC2010 SP1 编译的64位程序,/O2 /arch:SSE2。
    exe\simdsumint_gcc32.exe:GCC 4.7.1(TDM-GCC(MinGW-w64)) 编译的32位程序,-O3 -mss2。
    exe\simdsumint_gcc64.exe:GCC 4.7.1(TDM-GCC(MinGW-w64)) 编译的64位程序,-O3 -mss2。

      测试结果(使用cmdarg_ui)——

    参考文献——
    《Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes:1, 2A, 2B, 2C, 3A, 3B, and 3C》044US. August 2012. http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
    《Intel® Architecture Instruction Set Extensions Programming Reference》014. AUGUST 2012. http://software.intel.com/en-us/avx/
    《AMD64 Architecture Programmer’s Manual Volume 4: 128-Bit and 256-Bit Media Instructions》. December 2011. http://developer.amd.com/documentation/guides/Pages/default.aspx#manuals
    《[C] 让VC、BCB支持C99的整数类型(stdint.h、inttypes.h)(兼容GCC)》. http://www.cnblogs.com/zyl910/archive/2012/08/08/c99int.html
    《[C] zintrin.h: 智能引入intrinsic函数 V1.01版。改进对Mac OS X的支持,增加INTRIN_WORDSIZE宏》. http://www.cnblogs.com/zyl910/archive/2012/10/01/zintrin_v101.html
    《[C/C++] ccpuid:CPUID信息模块 V1.03版,改进mmx/sse指令可用性检查(使用signal、setjmp,支持纯C)、修正AVX检查Bug》. http://www.cnblogs.com/zyl910/archive/2012/10/13/ccpuid_v103.html
    《[x86]SIMD指令集发展历程表(MMX、SSE、AVX等)》. http://www.cnblogs.com/zyl910/archive/2012/02/26/x86_simd_table.html
    《SIMD(MMX/SSE/AVX)变量命名规范心得》. http://www.cnblogs.com/zyl910/archive/2012/04/23/simd_var_name.html
    《GCC 64位程序的makefile条件编译心得——32位版与64位版、debug版与release版(兼容MinGW、TDM-GCC)》. http://www.cnblogs.com/zyl910/archive/2012/08/14/gcc64_make.html
    《[C#] cmdarg_ui:“简单参数命令行程序”的通用图形界面》.  http://www.cnblogs.com/zyl910/archive/2012/06/19/cmdarg_ui.html
    《[C] 跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac)》. http://www.cnblogs.com/zyl910/archive/2012/10/22/simdsumfloat.html


    源码下载——
    https://files.cnblogs.com/zyl910/simdsumint.rar


     

    作者:zyl910
    版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0.
  • 相关阅读:
    cross apply / outer apply
    查看锁表
    常用的一些占位符
    如何将法向量转换到世界空间
    Essential Mathematics for Games and Interactive Applications
    下一本要看的书
    第五章第五节 四元数
    向量坐标转换、旋转矩阵以及视图转换
    怪诞行为学摘录
    unity3d发布apk资源打包和访问方式
  • 原文地址:https://www.cnblogs.com/zyl910/p/simdsumint.html
Copyright © 2011-2022 走看看