zoukankan      html  css  js  c++  java
  • 深入探讨用位掩码代替分支(4):VC2010速度测试

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

  • 相关阅读:
    装配Bean
    百练
    东软小选拔
    俄罗斯乘法
    POJ
    ACdream
    javascript 链式作用域
    ie6/7 bug
    onreadystatechange 和 status
    瀑布流 <<转>>
  • 原文地址:https://www.cnblogs.com/zyl910/p/noifopex4.html
Copyright © 2011-2022 走看看