Windows和Linux系统上的矢量运算:指令级并行计算SIMD(SSE/VAX)

news2024/12/22 16:59:40

注:本文的SIMD,指的是CPU指令架构中的相关概念。不涉及GPU端的算力机制。

 

基本概念

SIMD,Single Instruction/Multiple Data, 即单指流令多数据流,例如一个乘法指令,可以并行的计算8个浮点数的乘法。

SIMD(Single Instruction/Multiple Data, 即)是目前通用的CPU端的指令级并行计算机制,也叫做矢量运算,这些SIMD包括SSE和AVX。

通过代码直观的简介SIMD机制

用下面两份代码做个基本说明

非矢量运算的代码

void mul4_scalar( float* ptr )
{
    for( int i = 0; i < 4; i++ )
    {
        const float f = ptr[ i ];
        ptr[ i ] = f * f;
    }
}

矢量运算的代码

void mul4_vectorized( float* ptr )
{
    __m128 f = _mm_loadu_ps( ptr );
    f = _mm_mul_ps( f, f );
    _mm_storeu_ps( ptr, f );
}
// __m128 就是sse simd 对象
// _mm_mul_ps 和 _mm_storeu_ps 就是对应的乘法和赋值矢量运算指令(函数)

上述两份代码源自于: http://const.me/articles/simd/simd.pdf

不难看出,在密集计算中SIMD程序的效能肯定比常规程序高很多。(这里就不去和异构计算架构下的机制做比较了)

相关指令在源代码中的位置

Linux系统GCC

      gcc-master\gcc\config\i386\immintrin.h头文件中包含了各种simd指令的相关头文件

      SSE, __m128, 这类SSE指令位置: gcc-master\gcc\config\i386\xmmintrin.h

/* The Intel API is flexible enough that we must allow aliasing with other
   vector types, and their scalar components.  */
typedef float __m128 __attribute__ ((__vector_size__ (16), __may_alias__));

      SSE2, __v4si, __128d定义在 gcc-master\gcc\config\i386\emmintrin.h中

typedef int __v4si __attribute__ ((__vector_size__ (16)));

typedef double __m128d __attribute__ ((__vector_size__ (16), __may_alias__));

      MMX指令 __m64定义在 gcc-master\gcc\config\i386\mmintrin.h中

typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__));
typedef int __m32 __attribute__ ((__vector_size__ (4), __may_alias__));
typedef short __m16 __attribute__ ((__vector_size__ (2), __may_alias__));

      AVX指令, _v4df, __v8si, __m256定义在gcc-master\gcc\config\i386\avxintrin.h中

typedef double __v4df __attribute__ ((__vector_size__ (32)));

typedef int __v8si __attribute__ ((__vector_size__ (32)));

typedef unsigned char __v32qu __attribute__ ((__vector_size__ (32)));

typedef float __m256 __attribute__ ((__vector_size__ (32),
             __may_alias__));

      linux系统中矢量运算 __attribute__ vector_size 定义的说明:

            typedef int v4si __attribute__ ((vector_size (16)));

            The int type specifies the base type, while the attribute specifies the vector size for the variable, measured in bytes. For example, the declaration above causes the compiler to set the mode for the v4si type to be 16 bytes wide and divided into int sized units. For a 32-bit int this means a vector of 4 units of 4 bytes, and the corresponding mode of foo will be V4SI.

           相关细节请见: Vector Extensions (Using the GNU Compiler Collection (GCC))

Windows系统MSVC

      头文件 Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include\intrin.h,也已经包含了相关指令函数的头文件

      __m128d, __m128i定义在 Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include\emmintrin.h中

typedef union __declspec(intrin_type) __declspec(align(16)) __m128i {
    __int8              m128i_i8[16];
    __int16             m128i_i16[8];
    __int32             m128i_i32[4];
    __int64             m128i_i64[2];
    unsigned __int8     m128i_u8[16];
    unsigned __int16    m128i_u16[8];
    unsigned __int32    m128i_u32[4];
    unsigned __int64    m128i_u64[2];
} __m128i;

typedef struct __declspec(intrin_type) __declspec(align(16)) __m128d {
    double              m128d_f64[2];
} __m128d;

      __m128d, __m128i定义在 Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include\emmintrin.h

typedef union __declspec(intrin_type) __declspec(align(16)) __m128i {
    __int8              m128i_i8[16];
    __int16             m128i_i16[8];
    __int32             m128i_i32[4];
    __int64             m128i_i64[2];
    unsigned __int8     m128i_u8[16];
    unsigned __int16    m128i_u16[8];
    unsigned __int32    m128i_u32[4];
    unsigned __int64    m128i_u64[2];
} __m128i;

typedef struct __declspec(intrin_type) __declspec(align(16)) __m128d {
    double              m128d_f64[2];
} __m128d;

      AVX, __m256d, __m256, __m256i定义在 Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.33.31629\include\immintrin.h中

/*
 * Intel(R) AVX compiler intrinsic functions.
 */
typedef union __declspec(intrin_type) __declspec(align(32)) __m256 {
    float m256_f32[8];
} __m256;

typedef struct __declspec(intrin_type) __declspec(align(32)) __m256d {
    double m256d_f64[4];
} __m256d;

typedef union  __declspec(intrin_type) __declspec(align(32)) __m256i {
    __int8              m256i_i8[32];
    __int16             m256i_i16[16];
    __int32             m256i_i32[8];
    __int64             m256i_i64[4];
    unsigned __int8     m256i_u8[32];
    unsigned __int16    m256i_u16[16];
    unsigned __int32    m256i_u32[8];
    unsigned __int64    m256i_u64[4];
} __m256i;

两个系统的定义有区别,所以跨平台应用这些SIMD功能需要主要一些细节。

检查Linux系统或者Windows系统对SSE和AVX的支持情况

注:下面的代码本人常用在在Linux Centos7和Windows 10上

检测代码如下:

#include <iostream>
#ifdef _MSC_VER
# include <intrin.h>
void __cpuidSpec(int p0[4], int p1)
{
    __cpuid(p0, p1);
}
unsigned __int64 __cdecl _xgetbvSpec(unsigned int p)
{
    return _xgetbv(p);
}
#endif

#ifdef __GNUC__
void __cpuidSpec(int* cpuinfo, int info)
{
    __asm__ __volatile__(
        "xchg %%ebx, %%edi;"
        "cpuid;"
        "xchg %%ebx, %%edi;"
        : "=a"(cpuinfo[0]), "=D"(cpuinfo[1]), "=c"(cpuinfo[2]), "=d"(cpuinfo[3])
        : "0"(info));
}

unsigned long long _xgetbvSpec(unsigned int index)
{
    unsigned int eax, edx;
    __asm__ __volatile__(
        "xgetbv;"
        : "=a"(eax), "=d"(edx)
        : "c"(index));
    return ((unsigned long long)edx << 32) | eax;
}
#include <immintrin.h>
#endif

namespace sseavx
{
void sseavxCheck()
{
    bool sseSupportted    = false;
    bool sse2Supportted   = false;
    bool sse3Supportted   = false;
    bool ssse3Supportted  = false;
    bool sse4_1Supportted = false;
    bool sse4_2Supportted = false;
    bool sse4aSupportted  = false;
    bool sse5Supportted   = false;
    bool avxSupportted    = false;

    int cpuinfo[4];
    __cpuidSpec(cpuinfo, 1);

    // Check SSE, SSE2, SSE3, SSSE3, SSE4.1, and SSE4.2 support
    sseSupportted    = cpuinfo[3] & (1 << 25) || false;
    sse2Supportted   = cpuinfo[3] & (1 << 26) || false;
    sse3Supportted   = cpuinfo[2] & (1 << 0) || false;
    ssse3Supportted  = cpuinfo[2] & (1 << 9) || false;
    sse4_1Supportted = cpuinfo[2] & (1 << 19) || false;
    sse4_2Supportted = cpuinfo[2] & (1 << 20) || false;

    avxSupportted         = cpuinfo[2] & (1 << 28) || false;
    bool osxsaveSupported = cpuinfo[2] & (1 << 27) || false;
    if (osxsaveSupported && avxSupportted)
    {
        // _XCR_XFEATURE_ENABLED_MASK = 0
        unsigned long long xcrFeatureMask = _xgetbvSpec(0);
        avxSupportted                     = (xcrFeatureMask & 0x6) == 0x6;
    }

    // ----------------------------------------------------------------------

    // Check SSE4a and SSE5 support

    // Get the number of valid extended IDs
    __cpuidSpec(cpuinfo, 0x80000000);
    int numExtendedIds = cpuinfo[0];
    if (numExtendedIds >= 0x80000001)
    {
        __cpuidSpec(cpuinfo, 0x80000001);
        sse4aSupportted = cpuinfo[2] & (1 << 6) || false;
        sse5Supportted  = cpuinfo[2] & (1 << 11) || false;
    }

    // ----------------------------------------------------------------------
    std::boolalpha(std::cout);
    std::cout << "   Support SSE:   " << sseSupportted << std::endl;
    std::cout << "  Support SSE2:   " << sse2Supportted << std::endl;
    std::cout << "  Support SSE3:   " << sse3Supportted << std::endl;
    std::cout << "Support SSE4.1:   " << sse4_1Supportted << std::endl;
    std::cout << "Support SSE4.2:   " << sse4_2Supportted << std::endl;
    std::cout << " Support SSE4a:   " << sse4aSupportted << std::endl;
    std::cout << "  Support SSE5:   " << sse5Supportted << std::endl;
    std::cout << "   Support AVX:   " << avxSupportted << std::endl;
}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/856784.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Minecraft】Fabric Mod开发完整流程1 - 环境配置与第一个物品

前言 Fabric 是 Minecraft 一款非官方的模组 API,与 Forge mod 不同。它以轻量级和高性能为设计目标,专注于支持新版本的 Minecraft。 Fabric 和 Forge 在各自的加载编译流程上差别很大&#xff0c;所以你很难看见有同时支持二者的 mod&#xff0c;除非做了兼容性处理 Fabri…

新型高速 JavaScript 运行时 Bun 0.7 发布

导读近日&#xff0c;新型 JavaScript 运行时 Bun 正式发布了 0.7 版本&#xff0c;带来了重大的升级。据悉&#xff0c;Bun 是一个配套齐全的 JavaScript 解决方案&#xff0c;集运行时、打包器、转译器和包管理器于一体&#xff0c;追求极致的运行速度。此次更新主要集中在与…

命令提示符之操作基础(Windows)

打开命令提示符 方法一 打开指定文件的文件夹&#xff0c;在路径栏里输入“cmd”&#xff0c;回车&#xff0c;就进入控制台了。默认路径就是指定文件夹的路径。 方法二 打开指定的文件夹&#xff0c;按住shift键&#xff0c;在空白处右击&#xff0c;在菜单栏中选择“在此处打…

基于nodejs+vue+uniapp微信小程序的短视频分享系统

开发语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 3.1小程序端 用户注册页面&#xff0c;输入用户的个人信息点击注册即可。 注册完成后会返回到登录页面&#xff0c;用户输入自己注…

液态金属——究竟是个美丽的概念还是大有可为

液态金属是一种新型的合金材料&#xff0c;在低温熔炼制备工艺下&#xff0c;将不同的金属材料按照一定的配比&#xff0c;通过温度控制使其充分融合&#xff0c;从而形成新的金属材料&#xff08;也可以理解为表面工艺处理手法&#xff09;。 液态金属可看作由正离子流体和自由…

electron+vue3全家桶+vite项目搭建【13.1】ipc通信的使用,主进程与渲染进程之间的交互

文章目录 引入IPC通信[主/渲染]进程对应渲染进程>主进程代码测试测试效果 主进程>渲染进程代码测试测试效果 双向通信代码测试测试效果 引入 electron项目常常由一个主进程和多个渲染进程构成&#xff0c;渲染进程之间是隔离的&#xff0c;而所有渲染进程都和主进程共享…

优雅的使用 Dockerfile 定制镜像

一、使用 Dockerfile 定制镜像 1.1、Dockerfile 定制镜像 1.2、FROM 指定基础镜像 1.3、RUN 执行命令 1.4、构建镜像 1.5、镜像构建上下文&#xff08;Context&#xff09; 1.6、其他 docker build 的用法 二、Dockerfile 指令 2.1、COPY 2.2、ADD 2.3、CMD 2.4、EN…

IDEWA项目实践——mybatis的一些基本原理以及案例

系列文章目录 IDEA项目实践——创建Java项目以及创建Maven项目案例、使用数据库连接池创建项目简介 IDEA创建项目的操作步骤以及在虚拟机里面创建Scala的项目简单介绍_intellij 创建scala IDEA项目实践——动态SQL、关系映射、注解开发 文章目录 系列文章目录 1.MyBatis …

基于Tars高并发IM系统的设计与实现-实战篇5

基于Tars高并发IM系统的设计与实现-实战篇5 群聊服务 GroupChatServer 群聊服务既可以接受来自BrokerServer的用户请求&#xff0c;也需要接收来自其他服务的RPC请求;所以本服务提供两套RPC接口&#xff1a;通用RPC接口和专用RPC接口。 通用RPC接口 通用RPC接口主要处理如下…

Jenkins自动化打包脚本

一、背景 jenkins可以设置定时任务打包&#xff0c;也已手动点按钮打包&#xff0c;还可以通过执行http请求打包&#xff0c;今天我们就通过shell脚本&#xff0c;通过curl命令进行jenkins打包。 二、步骤 2.1 在jenkins上构建项目 设置触发器 2.2 通过shell脚本触发远程构…

【RabbitMQ上手——单实例安装5种简单模式实现通讯过程】

【RabbitMQ入门-单实例安装&5种简单模式实现通讯过程】 一、环境说明二、安装RabbitMQ三、用户权限及Virtual Host设置四、5种简单模式实现通讯过程的实现五、小结 一、环境说明 安装环境&#xff1a;虚拟机VMWare Centos7.6 Maven3.6.3 JDK1.8RabbitMQ版本&#xff1a;…

并发——线程的生命周期和状态

文章目录 Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态&#xff08;图源《Java 并发编程艺术》4.1.4 节&#xff09;。 线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程状态变迁如下图所示&am…

点对点协议PPP

点对点协议PPP(Point-to-Point Protocol)是目前使用最广泛的点对点数据链路层协议。PPP协议是因特网的正确标准。 基本格式&#xff1a; PPP协议是数据链路格式。格式如下&#xff1a; 标志(Flag)字段: PPP的定界符&#xff0c;取值为0x7E 地址(Address)字段: 取值为0xFF&…

多语言自动翻译海外跨境电商独立站源码开发

要搭建一个多语言自动翻译的海外跨境电商独立站&#xff0c;需要进行以下步骤&#xff1a; 1. 选择合适的开发语言和框架&#xff1a;根据自己的技术实力和需求&#xff0c;选择适合的开发语言和框架。 2. 设计数据库结构&#xff1a;根据电商的业务需求&#xff0c;设计数据…

【CHI】架构介绍

Learn the architecture - Introducing AMBA CHI AMBA CHI协议导论--言身寸 1. AMBA CHI简介 一致性集线器接口&#xff08;CHI&#xff09;是AXI一致性扩展&#xff08;ACE&#xff09;协议的演进。它是Arm提供的高级微控制器总线架构&#xff08;AMBA&#xff09;的一部分。…

电源控制--对数与db分贝

在控制理论中&#xff0c;"db"通常表示分贝&#xff08;decibel&#xff09;的缩写。分贝是一种用于度量信号强度、增益或衰减的单位。 在控制系统中&#xff0c;分贝常用于描述信号的增益或衰减。通常&#xff0c;增益以正数的分贝值表示&#xff0c;而衰减以负数的…

C语言——九九乘法表

//九九乘法表 //用程序做一个九九乘法表 #include<stdio.h> int main() {int i,j,result;printf("\n");for(i1;i<10;i){for(j1;j<i;j){resulti*j;printf(" %d*%d%-d",i,j,result);}printf(" \n");}}

成集云 | 畅捷通采购单同步至钉钉 | 解决方案

源系统成集云目标系统 介绍 畅捷通是一家专业的金融科技公司&#xff0c;致力于为投资者提供便捷、高效的金融服务。通过畅捷通T的交易方式&#xff0c;投资者可以更加灵活地进行买卖交易&#xff0c;并且在交易完成后即可获得结算款项&#xff0c;无需等待T1的结算周期。 钉…

利用multiprocessing实现多线程,并实现多个参数传递函数的多并行

前言 利用多线程一般来说都是有 一定的大数据需求。 比如一个函数可能被不断的调用很多次 一般来说我们会使用for循环&#xff0c;但是为了节省时间&#xff0c;我们采用多线程的方式来解决这个问题 show you code 单参数输入 举了两个例子&#xff0c;一看便知 func为我们的函…

探索MongoDB的奥秘:基本命令使用入门指南

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; 探索MongoDB的奥秘&#xff1a;基本命令使用入门指南 ⏱️ 创作时间&a…