前面我们用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”)