c/c++ 代码中使用sse指令集加速

使用SSE指令,首先要了解这一类用于进行初始化加载数据以及将暂存器的数据保存到内存相关的指令,

我们知道,大多数SSE指令是使用的xmm0到xmm8的暂存器,那么使用之前,就需要将数据从内存加载到这些暂存器。

1. load系列,用于加载数据,从内存到暂存器

__m128 _mm_load_ss (float *p)
__m128 _mm_load_ps (float *p)
__m128 _mm_load1_ps (float *p)
__m128 _mm_loadh_pi (__m128 a, __m64 *p)
__m128 _mm_loadl_pi (__m128 a, __m64 *p)
__m128 _mm_loadr_ps (float *p)
__m128 _mm_loadu_ps (float *p)  

上面是从手册查询到的load系列的函数。其中,

_mm_load_ss用于scalar的加载,所以,加载一个单精度浮点数到暂存器的低字节,其它三个字节清0,(r0 := *p, r1 := r2 := r3 := 0.0)。

_mm_load_ps用于packed的加载(下面的都是用于packed的),要求p的地址是16字节对齐,否则读取的结果会出错,(r0 := p[0], r1 := p[1], r2 := p[2], r3 := p[3])。

_mm_load1_ps表示将p地址的值,加载到暂存器的四个字节,需要多条指令完成,所以,从性能考虑,在内层循环不要使用这类指令。(r0 := r1 := r2 := r3 := *p)。

_mm_loadh_pi和_mm_loadl_pi分别用于从两个参数高底字节等组合加载。具体参考手册。

_mm_loadr_ps表示以_mm_load_ps反向的顺序加载,需要多条指令完成,当然,也要求地址是16字节对齐。(r0 := p[3], r1 := p[2], r2 := p[1], r3 := p[0])。

_mm_loadu_ps和_mm_load_ps一样的加载,但是不要求地址是16字节对齐,对应指令为movups。

2. set系列,用于加载数据,大部分需要多条指令完成,但是可能不需要16字节对齐。

__m128 _mm_set_ss (float w)
__m128 _mm_set_ps (float z, float y, float x, float w)
__m128 _mm_set1_ps (float w)
__m128 _mm_setr_ps (float z, float y, float x, float w)
__m128 _mm_setzero_ps ()  

这一系列函数主要是类似于load的操作,但是可能会调用多条指令去完成,方便的是可能不需要考虑对齐的问题。

_mm_set_ss对应于_mm_load_ss的功能,不需要字节对齐,需要多条指令。(r0 = w, r1 = r2 = r3 = 0.0)

_mm_set_ps对应于_mm_load_ps的功能,参数是四个单独的单精度浮点数,所以也不需要字节对齐,需要多条指令。(r0=w, r1 = x, r2 = y, r3 = z,注意顺序)

_mm_set1_ps对应于_mm_load1_ps的功能,不需要字节对齐,需要多条指令。(r0 = r1 = r2 = r3 = w)

_mm_setzero_ps是清0操作,只需要一条指令。(r0 = r1 = r2 = r3 = 0.0)

3. store系列,用于将计算结果等SSE暂存器的数据保存到内存中。

void _mm_store_ss (float *p, __m128 a)
void _mm_store_ps (float *p, __m128 a)
void _mm_store1_ps (float *p, __m128 a)
void _mm_storeh_pi (__m64 *p, __m128 a)
void _mm_storel_pi (__m64 *p, __m128 a)
void _mm_storer_ps (float *p, __m128 a)
void _mm_storeu_ps (float *p, __m128 a)
void _mm_stream_ps (float *p, __m128 a)  

这一系列函数和load系列函数的功能对应,基本上都是一个反向的过程。

_mm_store_ss:一条指令,*p = a0

_mm_store_ps:一条指令,p[i] = a[i]。

_mm_store1_ps:多条指令,p[i] = a0。

_mm_storeh_pi,_mm_storel_pi:值保存其高位或低位。

_mm_storer_ps:反向,多条指令。

_mm_storeu_ps:一条指令,p[i] = a[i],不要求16字节对齐。

_mm_stream_ps:直接写入内存,不改变cache的数据。

(2)算术指令

SSE提供了大量的浮点运算指令,包括加法、减法、乘法、除法、开方、最大值、最小值、近似求倒数、求开方的倒数等等,可见SSE指令的强大之处。那么在了解了上面的数据加载和数据保存的指令之后,使用这些算术指令就很容易了,下面以加法为例。

SSE中浮点加法的指令有:

__m128 _mm_add_ss (__m128 a, __m128 b)
__m128 _mm_add_ps (__m128 a, __m128 b) 

其中,_mm_add_ss表示scalar执行模式,_mm_add_ps表示packed执行模式。

一般而言,使用SSE指令写代码,步骤为:使用load/set函数将数据从内存加载到SSE暂存器;使用相关SSE指令完成计算等;使用store系列函数将结果从暂存器保存到内存,供后面使用。

下面是一个完成加法的例子:

#include <intrin.h>  

int main(int argc, char* argv[])
{
    float op1[4] = {1.0, 2.0, 3.0, 4.0};
    float op2[4] = {1.0, 2.0, 3.0, 4.0};
    float result[4];  

    __m128  a;
    __m128  b;
    __m128  c;  

    // Load
    a = _mm_loadu_ps(op1);
    b = _mm_loadu_ps(op2);  

    // Calculate
    c = _mm_add_ps(a, b);   // c = a + b  

    // Store
    _mm_storeu_ps(result, c);  

    /*      // Using the __m128 union to get the result.
    printf("0: %lf\n", c.m128_f32[0]);
    printf("1: %lf\n", c.m128_f32[1]);
    printf("2: %lf\n", c.m128_f32[2]);
    printf("3: %lf\n", c.m128_f32[3]);
    */
    printf("0: %lf\n", result[0]);
    printf("1: %lf\n", result[1]);
    printf("2: %lf\n", result[2]);
    printf("3: %lf\n", result[3]);  

    return 0;
}  

这个例子,前面已经写过类似的加法例子,注释中的printf部分是利用__m128这个数据类型来获取相关的值,

这个类型是一个union类型,具体定义可以参考相关头文件,但是,对于实际使用,有时候这个值是一个中间值,需要后面计算使用,就得使用store了,效率更高。

上面使用的是_mm_loadu_ps和_mm_storeu_ps,不要求字节对齐,如果使用_mm_load_ps和_mm_store_ps,会发现程序会崩溃或得不到正确结果。下面是指定字节对齐后的一种实现方法:

#include <intrin.h>  

int main(int argc, char* argv[])
{
    __declspec(align(16)) float op1[4] = {1.0, 2.0, 3.0, 4.0};
    __declspec(align(16)) float op2[4] = {1.0, 2.0, 3.0, 4.0};
    _MM_ALIGN16 float result[4];        // A macro, same as "__declspec(align(16))"   

    __m128  a;
    __m128  b;
    __m128  c;  

    // Load
    a = _mm_load_ps(op1);
    b = _mm_load_ps(op2);  

    // Calculate
    c = _mm_add_ps(a, b);   // c = a + b  

    // Store
    _mm_store_ps(result, c);  

    /*      // Using the __m128 union to get the result.
    printf("0: %lf\n", c.m128_f32[0]);
    printf("1: %lf\n", c.m128_f32[1]);
    printf("2: %lf\n", c.m128_f32[2]);
    printf("3: %lf\n", c.m128_f32[3]);
    */
    printf("0: %lf\n", result[0]);
    printf("1: %lf\n", result[1]);
    printf("2: %lf\n", result[2]);
    printf("3: %lf\n", result[3]);  

    return 0;
}  

原文地址:http://blog.csdn.net/gengshenghong/article/details/7011373

时间: 2024-09-30 11:02:53

c/c++ 代码中使用sse指令集加速的相关文章

【转帖】超能课堂(186) CPU中的那些指令集都有什么用?

超能课堂(186)CPU中的那些指令集都有什么用? https://www.expreview.com/68615.html 不明觉厉 开始的地方 第一大类:基础运算类x86.x86-64及EM64T等 第二大类:SIMD指令集,有SSE系列,AVX系列 多媒体应用 加解密运算 数据序列化 游戏 科学计算&人工智能 检测处理性能,跑分 第三大类:虚拟化指令集Intel,虚拟机应用 第四大类:安全类指令集,如加解密AES-IN指令集 第五大类:多线程应用,TSX事务同步扩展指令集 总结 本文约51

【转】【SEE】基于SSE指令集的程序设计简介

SSE技术简介 Intel公司的单指令多数据流式扩展(SSE,Streaming SIMD Extensions)技术能够有效增强CPU浮点运算的能力.Visual Studio .NET 2003提供了对SSE指令集的编程支持,从而允许用户在C++代码中不用编写汇编代码就可直接使用SSE指令的功能.MSDN中有关SSE技术的主题 [1]有可能会使不熟悉使用SSE汇编指令编程的初学者感到困惑,但是在阅读MSDN有关文档的同时,参考一下Intel软件说明书(Intel Software manua

【转】【SSE】基于SSE指令集的程序设计简介

基于SSE指令集的程序设计简介 作者:Alex Farber 出处:http://www.codeproject.com/cpp/sseintro.asp SSE技术简介 Intel公司的单指令多数据流式扩展(SSE,Streaming SIMD Extensions)技术能够有效增强CPU浮点运算的能力.Visual Studio .NET 2003提供了对SSE指令集的编程支持,从而允许用户在C++代码中不用编写汇编代码就可直接使用SSE指令的功能.MSDN中有关SSE技术的主题[1]有可能

SSE指令集学习:Compiler Intrinsic

大多数的函数是在库中,Intrinsic Function却内嵌在编译器中(built in to the compiler). 1. Intrinsic Function Intrinsic Function作为内联函数,直接在调用的地方插入代码,即避免了函数调用的额外开销,又能够使用比较高效的机器指令对该函数进行优化.优化器(Optimizer)内置的一些Intrinsic Function行为信息,可以对Intrinsic进行一些不适用于内联汇编的优化,所以通常来说Intrinsic Fu

关于 html5 代码中 &quot;xxx&quot;(双引号) 和 (单引号)&#39;xxx&#39; 的区别,谈谈我的经验!

最近在学习的时候看到有人问 "find" 与 'find' 的区别,发现了很多人都说没有区别,纯属个人爱好! 于是我好奇地试验了一下,发现了不一样的结果,通过对比分析发现 '' 和 "" (单引号,双引号),还是有很大的区别地! 建议:<!--************为了防止 引号匹配错误: 单引号与双引号交叉使用*******************--> 在使用html onclick 事件属性时候乱用 单引号 /双引号 会出错的,比如: <!

从Android代码中来记忆23种设计模式

我的简书同步发布:从Android代码中来记忆23种设计模式 相信大家都曾经下定决心把23种设计模式牢记于心,每次看完之后过一段时间又忘记了~,又得回去看,脑子里唯一依稀记得的是少数设计模式的大致的定义.其实,网上很多文章讲得都非常好,我也曾经去看过各种文章.也曾一直苦恼这些难以永久记下的设计模式,直到我接触到了<Android源码设计模式解析与实战>--何红辉与关爱明著,发现原来其实我们在Android中都接触过这些设计模式,只是我们不知道而已.既然我们都接触过,我们只需一一对号入座,对设计

提取代码中的部分代码字段

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-

使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 选择调用的进程为 24 i386 getuid sys_getuid1647 i386 getgid sys_getgid16 使用库函数API方式 使用C代码中嵌入汇编代码方式

如何设置可以避免php代码中的中文在浏览器中成为乱码?

其实很简单,只需要在代码开始的前面加上一条这样的语句就行: header("content-Type: text/html; charset=Utf-8");   //设置字符的编码是utp-8mysql_query('set names utf8'); 如果是在html代码中,只需要加上 <Meta http-equiv="Content-Type" Content="text/html; Charset=utf-8">