我的第一个调用Intrinsics函数的程序

TestSSE.cpp

#include "counter.h"
#include <intrin.h>
#include <stdlib.h>
#include <math.h>

/*
标题:我的第一个调用Intrinsics函数的程序
所属项目名称:TestSSE
项目类型:Win32控制台项目
依赖:counter.h文件//提供计时功能
描述:以前需要使用汇编对CPU的指令集进行优化,现在可以直接使用Intrinsic函数达到类似效果,
      可读性和移植性相对于汇编语言有长足进步。现在极少有x86架构cpu不支持SSE/SSE3指令集
	  所以是时候了解Intrinsic函数(SSE、SSE2)的使用。
	  现在让我们通过简单的例子,来了解Intrinsic函数(SSE/SSE2指令)如何使用!
依赖:counter.h
最后更新:kagula 2014-04-22
测试环境:Windows 8.1 64bit、Visual Studio 2013 Update1
测试结果:在Core i5-2500k上,addWithSSE函数相对于add函数大概缩短了一半的运算时间。

如何调用Intrinsics函数还可参考下面资料:
[1]《跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac) 》
http://www.cnblogs.com/zyl910/archive/2012/10/22/simdsumfloat.html
[2]《在C/C++代码中使用SSE等指令集的指令(5)SSE进行加法运算简单的性能测试》
http://blog.csdn.net/gengshenghong/article/details/7011373
[4]《Introduction to SSE Programming》
http://www.codeproject.com/Articles/4522/Introduction-to-SSE-Programming
*/

struct MyData {
	_MM_ALIGN16 float *op1;//后面使用到的Intrinsics函数需要128位对齐
	_MM_ALIGN16 float *op2;
	_MM_ALIGN16 float *result1;
	_MM_ALIGN16 float *result2;

	MyData(const unsigned int count)
	{
		op1 = new float[count];
		op2 = new float[count];
		result1 = new float[count];;
		result2 = new float[count];;
	}

	~MyData()
	{
		delete op1;
		delete op2;
		delete result1;
		delete result2;
	}
};

//初始化样本
void initSample(const unsigned int count, MyData &md)
{
	for (unsigned int i = 0; i < count; i++)
	{
		md.op1[i] = (float)rand() / (float)RAND_MAX;
		md.op2[i] = (float)rand() / (float)RAND_MAX;
	}
}

void add(const unsigned int count, MyData &md)
{
	for (unsigned int i = 0; i < count; i++)
		md.result1[i] = md.op1[i] + md.op2[i];
}

void addWithSSE(const unsigned int count, MyData &md)
{
	__m128  a;
	__m128  b;
	__m128  c;

	for (unsigned int i = 0; i < count; i = i + 4)
	{
		//Intrinsic函数的调用分三个步骤,具体如下

		//[S1/3]装载数据
		a = _mm_load_ps(md.op1 + i);
		b = _mm_load_ps(md.op2 + i);

		//[S2/3]四元组运算.  要求CPU支持SSE指令集
		c = _mm_add_ps(a, b);   // c = a + b  

		//[S3/3]保存数据
		_mm_store_ps(md.result2 + i, c);
	}
}
/*
    Intrinsic函数名的形式为_mm_<intrin_op>_<suffix>
	有三部分组成,其中后缀(suffix)又分为两部分.

    函数名中的mm,代表操作数是128位,把mm替换为mm256就是256位,需CPU支持AVX。

    函数名中的add可替换为sub、mul、div、sqrt、rcp、rsqrt、max、min分别
代表了减法、乘法、除法、平方根、倒数、平方根的倒数、返回较大值、较小值等。

    函数名中的后缀有两部分组成
	第一部分,有三种,决定运算范围
	s  标量(scalar)  只操作元组的第一个因子
	p  包(pack)      操作元组中的全部因子
	ep 扩展包(extend pack) 相对于p,因子的位宽增加一倍。
	   比如原来128位容纳4个因子,现在只能2个因子,低位向高位做了符号扩展

	第二部分,有11种,决定元组中因子的数据类型(部分要求SSE2支持,比如双精)
	s    单精(single-precision floating point)
	d    双精(double-precision floating point)
	i128 128位整数(signed 128-bit integer)
	i64  64位整数(signed 64-bit integer
	u64  无符号64位整数(unsigned 64-bit integer)
	i32  32位整数(signed 32-bit integer)
	u32  无符号32位整数(unsigned 32-bit integer)
	i16  16位整数(signed 16-bit integer
	u16  无符号16位整数(unsigned 16-bit integer)
	i8   8位整数(signed 8-bit integer)
	u8   无符号8位整数(unsigned 8-bit integer	

	所以_mm_add_ps函数名,表示128位,加法,元组,单精度函数
	因为一个单精度占32位,操作数为128位,128/32=4,所以元组含四个因子。

	例如:可以把_mm_add_ps中的ps替换为ss,就只对四元组中的第一个元素操作了。
	有哪些Intrinsic函数可供我们调用,参考下面资料

[1]《VC++ 浮点数学SSE Intrinsics》
http://blog.163.com/[email protected]/blog/static/457384842007814114410576/
[2]《Intel Intrinsics Guide》
https://software.intel.com/sites/landingpage/IntrinsicsGuide/

	通过Intrinsic形式要调用CPU扩展指令集SSE、SSE2,得考虑到还有少部分老机器不支持,
可使用CPUID指令(在Visual Studio 2010以上版本中有对应的Intrinsic函数)判断。
*/

int main(int argc, wchar_t* argv[])
{
	const unsigned int count = 400 * 100000; // 4*32bit=128bit对齐
	MyData md(count);
	initSample(count, md);
	srand((unsigned int)time(NULL));

	wprintf(L"Add a vector array:\n");
	startTiming();
	add(count, md);
	stopWithPrintTiming();

	printf("\n");
	wprintf(L"Add a vector array with SSE instructions:\n");
	startTiming();
	addWithSSE(count, md);
	stopWithPrintTiming();

	//system("pause");

	return 0;
}

counter.h

#ifndef _COUNTER_H_
#define _COUNTER_H_

/*
功能:计数器
调用者:TestSSE.cpp
*/

#include <time.h>
#include <stdio.h>  

#ifdef WIN32  || WIN64

#include <windows.h>
#define timing_t double
_LARGE_INTEGER g_startTime, g_stopTime;
double g_dqFreq;

inline void startTiming()
{
	_LARGE_INTEGER f;
	QueryPerformanceFrequency(&f);
	g_dqFreq = (double)f.QuadPart;

	QueryPerformanceCounter(&g_startTime);
}

// unit: ms
inline timing_t stopTiming()
{
	QueryPerformanceCounter(&g_stopTime);
	return ((double)(g_stopTime.QuadPart - g_startTime.QuadPart) / g_dqFreq * 1000);
}

inline timing_t stopWithPrintTiming()
{
	timing_t timing;
	QueryPerformanceCounter(&g_stopTime);
	timing = ((double)(g_stopTime.QuadPart - g_startTime.QuadPart) / g_dqFreq * 1000);
	printf("Elapsed Timing(ms) : %.3lf\n\n", timing);

	return timing;
}

#else
#include <unistd.h>
typedef unsigned long long int64;
#define timing_t int64
#if defined(__i386__)
inline int64 GetCycleCount() {
	int64 result;
	__asm__ __volatile__("rdtsc" : "=A" (result));
	return result;
}
#elif defined(__x86_64__)
inline int64 GetCycleCount()
{
	int64 hi, lo;
	__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
	return ((int64)lo) | (((int64)hi) << 32);
}
#endif  

int64 ticks_start, ticks_end;

inline void startTiming()
{
	ticks_start = GetCycleCount();
}

// unit: cycles
inline int64 stopTiming()
{
	ticks_end = GetCycleCount();
	return (ticks_end - ticks_start);
}

inline int64 stopWithPrintTiming()
{
	int64 timing;
	ticks_end = GetCycleCount();
	timing = (ticks_end - ticks_start);
	printf("----------Elapsed Timing(Cycles) : %llu\n", timing);
	printf("----------------------------------------\n");

	return timing;
}
#endif  

// unit: ms
inline void wait(int ms)
{
#ifdef WIN32
	Sleep(ms);
#else
	usleep(ms * 1000);
#endif
}

#endif

我的第一个调用Intrinsics函数的程序

时间: 2024-10-27 21:17:51

我的第一个调用Intrinsics函数的程序的相关文章

bind()适配器(Adapter) -- 调用全局函数及成员函数

调用全局函数 调用全局函数程序实例: #include <iostream> #include <algorithm> #include <functional> #include <locale> #include <string> using namespace std; using namespace std::placeholders; char my_toupper(char c) { locale loc; return std::u

C#实现百度地图附近搜索&amp;调用JavaScript函数

前一篇文章"C#调用百度地图API入门&解决BMap未定义问题"讲述了如何通过C#调用百度API显示地图,并且如何解决BMap未定义的问题.这篇文章主要更加详细的介绍百度地图的一些功能,包括附近搜索.城市搜索.路线规划.添加覆盖物等等. 希望文章对你有所帮助!如果文章中有不足之处,还请海涵~ 百度官方文档:http://developer.baidu.com/map/jsmobile.htm 官方DEMO例:http://developer.baidu.com/map/jsde

Android使用JNI(从java调用本地函数)

当编写一个混合有本地C代码和Java的应用程序时,需要使用Java本地接口(JNI)作为连接桥梁.JNI作为一个软件层和API,允许使用本地代码调用Java对象的方法,同时也允许在Java方法中调用本地函数. 在Java端,开发者所需要做的仅仅是在连接本地函数的方法之前加上native关键字.这样VM就会去寻找这个本地函数. 1.从Java调用本地函数 从Java调用本地函数时,需要在类中定义一个带有native关键字的特有方法,作为连接本地代码的桥梁.通过这个定义,尝试调用本地方法时JVM会找

linux平台学x86汇编(十九):C语言中调用汇编函数

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 除了内联汇编以外,还有一种途径可以把汇编代码整合到C/C++语言中,C/C++语言可以直接调用汇编函数,把输入值传递给函数,然后从函数获得输出值. 如果希望汇编语言函数和C/C++程序一起工作,就必须显示地遵守C样式的函数格式,也就是说所有输入变量都必须从堆栈读取,并且大多数输入值都返回到EAX嫁寄存器中.在汇编函数代码中,C样式函数对于可以修改哪些寄存器和函数必须保留哪些寄

Lua调用C函数

在上一篇文章(C调用lua函数)中,讲述了如何用c语言调用lua函数,通常,A语言能调用B语言,反过来也是成立的.正如Java 与c语言之间使用JNI来互调,Lua与C也可以互调. 当lua调用c函数时,使用了和c调用lua中的同一种栈,c函数从栈中得到函数,然后将结果压入栈中.为了区分返回结果和栈中的其他值,每一个函数返回结果的个数. 这里有个重要的概念:这个栈不是全局的结构,每个函数都有自己的私有局部栈.哪怕c函数调用了lua代码,lua代码再次调用该c函数,他们有各自独立的局部栈.第一个参

C语言调用Lua函数

记得上学时,初中英文课本中,上网叫做surfing the internet,中文叫网上冲浪.那个时期,人们经常称互联网为赛博空间.如今工作了,大量的零碎时间用于上微博,知乎,QQ,这些碎片化的阅读让人读起来轻松,也能获取些粗浅的信息.然而它们是消耗时间的黑洞,时间就这样一分一秒地飞逝,年末的时候,知乎会告诉你回答了多少问题,阅读了相当于一部<红楼梦>那么多的文字.只是当你静下来一想,这些浅阅读并没给你带来有深度,系统的知识.在你的时间线上,两条相邻信息往往是八竿子也打不着的.而且你还时不时去

在lldb调试中调用c++函数

在lldb调试时,调用oc对象的方法不足为奇,因为msgSend是有原型导出的,oc对象的方法都运行期绑定的,绑定信息都在objc_class中.只要在调试中[receiver sel]之类,lldb就自动完成的整个由SEL通过msgSend路由到receiver的IMP方法并执行的整个过程.但是要调用c++函数则没有这么方便,虽然c++函数(包括成员函数和非成员函数)的链接符号有着函数原型的详细信息,但却不包括类的定义和名字空间的定义,即使lldb翻译出这样一个符号(symbol)Quartz

#python#子类调用父类函数的方法

Python中的子类中的__init__()函数会覆盖父类的函数,一些情况往往需要在子类里调用父类函数. 如下例程里,???处是需要调用父类函数的地方,接下来结合例程具体介绍. 1 1 # -*- coding:utf-8 -*- 2 2 class Student: 3 3 def __init__(self,name): 4 4 self.name=name 5 5 def ps(self): 6 6 print('I am %s'%self.name) 7 7 8 8 class Scor

动态调用DLL函数有时正常,有时报Access violation的异常

动态调用DLL函数有时正常,有时报Access violation的异常 typedef int (add *)(int a,int b); void test() { hInst=LoadLibraryA("aimdtl.dll"); (FARPROC &)add=GetProcAddress(hInst,"add"); add(1,2); } 按这个代码执行,add函数有时OK,有时报Access violation的异常.看到提示,第一反应就是内存异常