现在的位置: 首页 > 综合 > 正文

深入探讨用位掩码代替分支(4):VC2010速度测试

2013年08月17日 ⁄ 综合 ⁄ 共 5672字 ⁄ 字号 评论关闭

  前面我们用VC6测试了位掩码代替分支的速度。VC6是1998年发售的,离现在有14年了。在14年里,Intel与AMD的CPU都换了好几套微架构了,VC6编译器很可能无法充分发挥它们的性能。而且,从2003年AMD推出了64位处理器开始,现在64位系统越来越普及,我们希望测试64位下的性能。
  于是选择最新的VC系列编译器——Visual C++ 2010,它是2010年发售的,支持x64平台。

一、代码改进

1.1 通用字符处理——tchar.h

  传统的Windows程序一般使用ANSI+DBCS字符集。而从Windows NT开始,Windows内核采用Unicode字符集。但那时基于ANSI+DBCS的Win9X系统还很流行,所以VC6默认使用ANSI+DBCS字符集。
  虽然VC6中也有用于通用字符处理的“tchar.h”。但考虑到传统的C语言教学,一般很少用到它。

  如今WinXP替代Win9X已经有很多年了,甚至都快被Win7取代了,ANSI+DBCS字符集已经退出历史舞台。于是VC2010默认使用Unicode字符集。我们应使用“tchar.h”做通用字符处理。使用要点有——
1.字符类型。用“_TCHAR”替代“char”。
2.字符串常量。用“_T”这个宏来定义字符串常量。如“_T("\n")”。
3.字符串函数。相对于传统的C语言标准函数,tchar函数的特点是函数名前面多了“_t”。如“_tprintf(_T("\n"));”
4.程序入口。传统C语言程序的入口是“main”函数,而现在是“_tmain”函数。如“int _tmain(int argc, _TCHAR* argv[])”。

1.2 检测64位环境

  因考虑到程序会在64位环境下运行,所以需要检测64位环境。检测方法——
http://www.cnblogs.com/zyl910/archive/2012/02/19/vcis64.html
VC 64位程序开发心的——获得程序位数和操作系统位数

二、全部代码

  全部代码——

// 用位掩码做饱和处理.用求负生成掩码
#define LIMITSU_FAST(n, bits) ( (n) & -((n) >= 0) | -((n) >= (1<<(bits))) )
#define LIMITSU_SAFE(n, bits) ( (LIMITSU_FAST(n, bits)) & ((1<<(bits)) - 1) )
#define LIMITSU_BYTE(n) ((BYTE)(LIMITSU_FAST(n, 8)))

// 用位掩码做饱和处理.用带符号右移生成掩码
#define LIMITSW_FAST(n, bits) ( ( (n) | ((signed short)((1<<(bits)) - 1 - (n)) >> 15) ) & ~((signed short)(n) >> 15) )
#define LIMITSW_SAFE(n, bits) ( (LIMITSW_FAST(n, bits)) & ((1<<(bits)) - 1) )
#define LIMITSW_BYTE(n) ((BYTE)(LIMITSW_FAST(n, 8)))

// 数据规模
#define DATASIZE	16384	// 128KB / (sizeof(signed short) * 4)

// 缓冲区
signed short	bufS[DATASIZE*4];	// 源缓冲区。64位的颜色(4通道,每通道16位)
BYTE	bufD[DATASIZE*4];	// 目标缓冲区。32位的颜色(4通道,每通道8位)

// 测试时的函数类型
typedef void (*TESTPROC)(BYTE* pbufD, const signed short* pbufS, int cnt);

// 用if分支做饱和处理
void f0_if(BYTE* pbufD, const signed short* pbufS, int cnt)
{
	const signed short* pS = pbufS;
	BYTE* pD = pbufD;
	int i;
	for(i=0; i<cnt; ++i)
	{
		// 分别对4个通道做饱和处理
		pD[0] = (pS[0]<0) ? 0 : ( (pS[0]>255) ? 255 : (BYTE)pS[0] );
		pD[1] = (pS[1]<0) ? 0 : ( (pS[1]>255) ? 255 : (BYTE)pS[1] );
		pD[2] = (pS[2]<0) ? 0 : ( (pS[2]>255) ? 255 : (BYTE)pS[2] );
		pD[3] = (pS[3]<0) ? 0 : ( (pS[3]>255) ? 255 : (BYTE)pS[3] );
		// next
		pS += 4;
		pD += 4;
	}
}

// 用min、max饱和处理
void f1_min(BYTE* pbufD, const signed short* pbufS, int cnt)
{
	const signed short* pS = pbufS;
	BYTE* pD = pbufD;
	int i;
	for(i=0; i<cnt; ++i)
	{
		// 分别对4个通道做饱和处理
		pD[0] = min(max(0, pS[0]), 255);
		pD[1] = min(max(0, pS[1]), 255);
		pD[2] = min(max(0, pS[2]), 255);
		pD[3] = min(max(0, pS[3]), 255);
		// next
		pS += 4;
		pD += 4;
	}
}

// 用位掩码做饱和处理.用求负生成掩码
void f2_neg(BYTE* pbufD, const signed short* pbufS, int cnt)
{
	const signed short* pS = pbufS;
	BYTE* pD = pbufD;
	int i;
	for(i=0; i<cnt; ++i)
	{
		// 分别对4个通道做饱和处理
		pD[0] = LIMITSU_BYTE(pS[0]);
		pD[1] = LIMITSU_BYTE(pS[1]);
		pD[2] = LIMITSU_BYTE(pS[2]);
		pD[3] = LIMITSU_BYTE(pS[3]);
		// next
		pS += 4;
		pD += 4;
	}
}

// 用位掩码做饱和处理.用带符号右移生成掩码
void f3_sar(BYTE* pbufD, const signed short* pbufS, int cnt)
{
	const signed short* pS = pbufS;
	BYTE* pD = pbufD;
	int i;
	for(i=0; i<cnt; ++i)
	{
		// 分别对4个通道做饱和处理
		pD[0] = LIMITSW_BYTE(pS[0]);
		pD[1] = LIMITSW_BYTE(pS[1]);
		pD[2] = LIMITSW_BYTE(pS[2]);
		pD[3] = LIMITSW_BYTE(pS[3]);
		// next
		pS += 4;
		pD += 4;
	}
}

// 进行测试
void runTest(_TCHAR* szname, TESTPROC proc)
{
	int i,j;
	DWORD	tm0, tm1;	// 存储时间
	for(i=1; i<=3; ++i)	// 多次测试
	{
		//tm0 = GetTickCount();
		tm0 = timeGetTime();
		// main
		for(j=1; j<=4000; ++j)	// 重复运算几次延长时间,避免计时精度问题
		{
			proc(bufD, bufS, DATASIZE);
		}
		// show
		//tm1 = GetTickCount() - tm0;
		tm1 = timeGetTime() - tm0;
		_tprintf(_T("%s[%d]:\t%u\n"), szname, i, tm1);

	}
}

// 获取程序位数(被编译为多少位的代码)
int GetProgramBits()
{
	return sizeof(int*) * 8;
}

// 安全的取得真实系统信息
VOID SafeGetNativeSystemInfo(__out LPSYSTEM_INFO lpSystemInfo)
{
	if (NULL==lpSystemInfo)	return;
	typedef VOID (WINAPI *LPFN_GetNativeSystemInfo)(LPSYSTEM_INFO lpSystemInfo);
	LPFN_GetNativeSystemInfo fnGetNativeSystemInfo = (LPFN_GetNativeSystemInfo)GetProcAddress( GetModuleHandle(_T("kernel32")), "GetNativeSystemInfo");;
	if (NULL != fnGetNativeSystemInfo)
	{
		fnGetNativeSystemInfo(lpSystemInfo);
	}
	else
	{
		GetSystemInfo(lpSystemInfo);
	}
}

// 获取操作系统位数
int GetSystemBits()
{
	SYSTEM_INFO si;
	SafeGetNativeSystemInfo(&si);
 	if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ||
		si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 )
	{
		return 64;
	}
	return 32;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int i;	// 循环变量

	//printf("Hello World!\n");
	const int nBitCode = GetProgramBits();
	const int nBitSys = GetSystemBits();
	_tprintf(_T("== noif:VC2010(%d) on %dbit =="), nBitCode, nBitSys);

	// 初始化
	srand( (unsigned)time( NULL ) );
	for(i=0; i<DATASIZE*4; ++i)
	{
		bufS[i] = (signed short)((rand()&0x1FF) - 128);	// 使数值在 [-128, 383] 区间
	}

	// 准备开始。可以将将进程优先级设为实时
	if (argc<=1)
	{
		_tprintf(_T("<Press any key to continue>"));
		_getch();
	}
	_tprintf(_T("\n"));

	// 进行测试
	runTest(_T("f0_if"), f0_if);
	runTest(_T("f1_min"), f1_min);
	runTest(_T("f2_neg"), f2_neg);
	runTest(_T("f3_sar"), f3_sar);

	// 结束前提示
	if (argc<=1)
	{
		_tprintf(_T("<Press any key to exit>"));
		_getch();
	}
	_tprintf(_T("\n"));

	return 0;
}

三、配置与编译

3.1 输出汇编代码

  配置方法如下——
1.点击菜单栏的 项目->属性,打开项目的属性页。
2.在左边的功能树中选择 配置属性->C/C++->输出文件。
3.在右边的“汇编程序输出”中选择“带源代码的程序集”。

3.2 配置winmm.lib库

  配置方法如下——
1.点击菜单栏的 项目->属性,打开项目的属性页。
2.在左边的功能树中选择 配置属性->链接器->输入。
3.在右边的“附加依赖项”中添加“Winmm.lib”。

3.3 配置x64平台

  点击菜单栏的 生成->配置管理器,打开“配置管理器”对话框,新建平台“x64”。

  具体细节可参考MSDN——
http://msdn.microsoft.com/zh-cn/library/9yb4317s.aspx
如何:针对 64 位平台配置 Visual C++ 项目

3.4 编译生成

  点击菜单栏的 生成->批生成。生成Release版。

四、测试结果

  运行“Release\noifVC2010.exe”,在32位winXP上的测试结果——

== noif:VC2010(32) on 32bit ==<Press any key to continue>
f0_if[1]:       1793
f0_if[2]:       1812
f0_if[3]:       1733
f1_min[1]:      2112
f1_min[2]:      2111
f1_min[3]:      2132
f2_neg[1]:      511
f2_neg[2]:      519
f2_neg[3]:      512
f3_sar[1]:      442
f3_sar[2]:      436
f3_sar[3]:      437

  运行“Release\noifVC2010.exe”,在64位win7上的测试结果——

== noif:VC2010(32) on 64bit ==<Press any key to continue>
f0_if[1]:       1731
f0_if[2]:       1716
f0_if[3]:       1685
f1_min[1]:      2106
f1_min[2]:      2106
f1_min[3]:      2090
f2_neg[1]:      500
f2_neg[2]:      514
f2_neg[3]:      515
f3_sar[1]:      437
f3_sar[2]:      421
f3_sar[3]:      437

  运行“x64\Release\noifVC2010.exe”,在64位win7上的测试结果——

== noif:VC2010(64) on 64bit ==<Press any key to continue>
f0_if[1]:       1638
f0_if[2]:       1623
f0_if[3]:       1622
f1_min[1]:      1997
f1_min[2]:      1997
f1_min[3]:      2012
f2_neg[1]:      421
f2_neg[2]:      437
f2_neg[3]:      421
f3_sar[1]:      328
f3_sar[2]:      327
f3_sar[3]:      328

  硬件环境——
CPU:Intel Core i3-2310M, 2100 MHz
内存:DDR3-1066

源码下载——
http://files.cnblogs.com/zyl910/noifVC2010.rar
(建议阅读编译器生成的汇编代码,位于“Release\noifVC2010.asm”、“x64\Release\noifVC2010\noifVC2010.asm”)

抱歉!评论已关闭.