SSE指令学习笔记

       学习的动力来自哪里?学习一样新东西是需要动力的,这个动力有的来自兴趣,即对这个新东西感兴趣,也就特别愿意去学习,比如学车;有的动力来自对职业发展的恐惧,怕过早出现职场瓶颈,于是通过学习新东西、新技能来修炼自己、提升自己,比如工作后又去考研;有的动力来自公司项目需求,因为项目需求进而去学习一样新东西,比如vc开发转android开发。最近在做图像算法优化,如果依次对照上面三条,发现好像都符合,这个优化工作动力来源即有个人兴趣、也有想提升自己、还有公司项目需求,不过优化来优化去,发现算法效率提升并不是很明显,当然这也与我平常处理的都是小图片(一般320*240的视频帧图片)有关。下面是在算法优化过程中,有关sse cpu指令集的学习摘录与整理,希望对这方面不太了解的同学有帮助。不过,我在看这方面资料过程中,始终有一个疑问,就是通用性问题,非常怕使用指令集的算法在某些pc上运行不了,因为sse cpu指令集是intel设计的,我一直在想amd的cpu支持情况怎么样,谷歌了半天也没有找到准确答案,在相关群里问也没有得到靠谱的回答,哪位大牛路过这里,还望指点一下。
       SSE指令介绍:
       SSE是英特尔提出的CPU指令集,最早应用在PIII系列CPU上。学习SSE,先从SSE的全称开始。SSE,英文全称为:Stream SIMD Extentions。而SIMD的英文全称为Single Instruction Multiple Data,这样SSE连起来就是“流数据单指令多数据扩展”。SSE新增了8个128位寄存器,xmm0 ~ xmm7,这些128位寄存器,可以用来存放4个32位单精度浮点数,SSE的浮点数运算指令就是使用这些寄存器。也就是说,SSE中的所有计算都是一次性针对4个浮点数来完成的,这种批处理会带来显著的效率提升。
                                   SSE指令学习笔记_第1张图片
       SSE指令分类:
       p(packed:包裹指令) :该指令对寄存器中的每个元素进行运算,即一次对xmm寄存器中的四个浮点数(data0~data3)均进行计算;
       s(scalar:标量指令):该指令对寄存器中的第一个元素进行运算,即一次只对xmm寄存器中的data0进行计算。
                                         SSE指令学习笔记_第2张图片
       SSE指令格式:
       第一部分表示指令的作用,比如加法add;
       第二部分是p或者s,分别表示为packed或者scalar;
       第三部分为s,表示单精度浮点数。
                                     SSE指令学习笔记_第3张图片
       SSE指令使用:
       使用SSE指令有两种方式:一是直接在C/C++中嵌入(汇编)指令;二是使用Intel C++ Compiler或是Microsoft Visual C++中提供的支持SSE指令集的intrinsics内联函数。从代码可读和维护角度讲,推荐使用intrinsics内联函数的形式。intrinsics是对MMX、SSE等指令集的一种封装,以函数的形式提供,使得程序员更容易编写和使用这些高级指令,在编译的时候,这些函数会被内联为汇编,不会产生函数调用的开销。SSE指令中intrinsics函数的数据类型为:__m128,如果使用sizeof(__m128)计算该类型大小,结果为16,即等于四个浮点数长度。下面为非常简单的代码示例:
#include "stdafx.h"
#include 
#include 
#include 

void sse_add(float *srcA, float *srcB, float *dest, int n)
{
 	int len = n >> 2;
 	for (int i = 0; i < len; i++)
 	{
 		*(__m128*)(dest + i*4) = _mm_add_ps(*(__m128*)(srcA + i*4), *(__m128*)(srcB + i*4));
 	}
}

void normal_add(float *srcA, float *srcB, float *dest, int n)
{
	for (int i = 0; i < n; i++)
	{
		dest[i] = srcA[i] + srcB[i];
	}
}

int main(int argc, _TCHAR* argv[])
{
	DWORD timeStart = 0, timeEnd = 0;
	const int size = 10000, count = 10000;
	// 分配16字节对齐的内存
	_MM_ALIGN16 float *srcA = (_MM_ALIGN16 float*)_mm_malloc(sizeof(float)*size, 16);
	_MM_ALIGN16 float *srcB = (_MM_ALIGN16 float*)_mm_malloc(sizeof(float)*size, 16);
	_MM_ALIGN16 float *dest = (_MM_ALIGN16 float*)_mm_malloc(sizeof(float)*size, 16);
	
	// 初始化
	for (int i = 0; i < size; i++)
	{
		srcA[i] = (float)i;
	}
	memcpy_s(srcB, sizeof(float) * size, srcA, sizeof(float) * size);

	// 标准加法
	timeStart = GetTickCount();
	for (int i = 0; i < count; i++)
	{
		normal_add(srcA, srcB, dest, size);
	}
	timeEnd  = GetTickCount();
	printf("normal test...time  ---> %f \n", (timeEnd - timeStart) * 0.001);

	// SSE指令加法
	timeStart = GetTickCount();
	for (int i = 0; i < count; i++)
	{
		sse_add(srcA, srcB, dest, size);
	}
	timeEnd  = GetTickCount();
	printf("sse test...time  ---> %f \n", (timeEnd - timeStart) * 0.001);

	// 释放内存
	_mm_free(srcA);
	_mm_free(srcB);
	_mm_free(dest);

    system("pause");
	return 0;
}
上述程序使用vs 2005采用release模式编译后,运行结果如下,通过时间对比可知,采用sse指令的加法运算,效率明显得到了提高。
                                               SSE指令学习笔记_第4张图片


参考链接:
http://dev.gameres.com/Program/Other/SSEjianjie.htm
http://www.cnblogs.com/zyl910/archive/2012/10/22/simdsumfloat.html


你可能感兴趣的:(摘录,/,整理,/,随笔)