前面我们用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
源码下载——
https://files.cnblogs.com/zyl910/noifVC2010.rar
(建议阅读编译器生成的汇编代码,位于“Release\noifVC2010.asm”、“x64\Release\noifVC2010\noifVC2010.asm”)