#if 1
// MMX, SSE, SSE2
#include <emmintrin.h>
#include <conio.h>
#include <time.h>
#pragma comment(lib, "winmm.lib ")
// 用位掩碼做飽和處理.用求負生成掩碼
#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)
// 緩衝區。SSE需要按128位對齊
__declspec(align(16)) signed short bufS[DATASIZE*4]; // 源緩衝區。64位的顏色(4通道,每通道16位)
__declspec(align(16)) BYTE bufD[DATASIZE*4]; // 目標緩衝區。32位的顏色(4通道,每通道8位)
// 測試時的函數類型
typedef void (*TESTPROC)(BYTE* pbufD, const signed short* pbufS, int cnt);
// http://www.cnblogs.com/zyl910/archive/2012/03/01/checksimd.html
// SSE系列指令集的支持級別. simd_sse_level 函數的返回值。
#define SIMD_SSE_NONE 0 // 不支持
#define SIMD_SSE_1 1 // SSE
#define SIMD_SSE_2 2 // SSE2
#define SIMD_SSE_3 3 // SSE3
#define SIMD_SSE_3S 4 // SSSE3
#define SIMD_SSE_41 5 // SSE4.1
#define SIMD_SSE_42 6 // SSE4.2
const char* simd_sse_names[] = {
"None",
"SSE",
"SSE2",
"SSE3",
"SSSE3",
"SSE4.1",
"SSE4.2",
};
// 是否支持MMX指令集
BOOL simd_mmx()
{
const DWORD BIT_DX_MMX = 0x00800000; // bit 23
DWORD v_edx;
// check processor support
__try
{
_asm
{
mov eax, 1
cpuid
mov v_edx, edx
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return FALSE;
}
if ( v_edx & BIT_DX_MMX )
{
// check OS support
__try
{
_asm
{
pxor mm0, mm0 // executing any MMX instruction
emms
}
return TRUE;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
}
return FALSE;
}
// 檢測SSE系列指令集的支持級別
int simd_sse_level()
{
const DWORD BIT_D_SSE = 0x02000000; // bit 25
const DWORD BIT_D_SSE2 = 0x04000000; // bit 26
const DWORD BIT_C_SSE3 = 0x00000001; // bit 0
const DWORD BIT_C_SSSE3 = 0x00000100; // bit 9
const DWORD BIT_C_SSE41 = 0x00080000; // bit 19
const DWORD BIT_C_SSE42 = 0x00100000; // bit 20
BYTE rt = SIMD_SSE_NONE; // result
DWORD v_edx;
DWORD v_ecx;
// check processor support
__try
{
_asm
{
mov eax, 1
cpuid
mov v_edx, edx
mov v_ecx, ecx
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return SIMD_SSE_NONE;
}
if ( v_edx & BIT_D_SSE )
{
rt = SIMD_SSE_1;
if ( v_edx & BIT_D_SSE2 )
{
rt = SIMD_SSE_2;
if ( v_ecx & BIT_C_SSE3 )
{
rt = SIMD_SSE_3;
if ( v_ecx & BIT_C_SSSE3 )
{
rt = SIMD_SSE_3S;
if ( v_ecx & BIT_C_SSE41 )
{
rt = SIMD_SSE_41;
if ( v_ecx & BIT_C_SSE42 )
{
rt = SIMD_SSE_42;
}
}
}
}
}
}
// check OS support
__try
{
_asm
{
xorps xmm0, xmm0 // executing any SSE instruction
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return SIMD_SSE_NONE;
}
return rt;
}
// 用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;
}
}
// 飽和處理MMX版
void f4_mmx(BYTE* pbufD, const signed short* pbufS, int cnt)
{
//const signed short* pS = pbufS;
//BYTE* pD = pbufD;
const __m64* pS = (const __m64*)pbufS;
__m64* pD = (__m64*)pbufD;
int i;
for(i=0; i<cnt; i+=2)
{
// 同時對兩個像素做飽和處理。即 將兩個64位像素(4通道,每分量爲帶符號16位) 轉爲 兩個32位像素(每分量爲無符號8位)。
pD[0] = _mm_packs_pu16(pS[0], pS[1]); // 飽和方式數據打包(帶符號16位->無符號8位)。等價於 for(i=0;i<4;++i){ pD[0].uB[i]=SU(pS[0].iW[i]); pD[0].uB[4+i]=SU(pS[1].iW[i]); }
// next
pS += 2;
pD += 1;
}
// MMX狀態置空
_mm_empty();
}
// 飽和處理SSE版
void f5_sse(BYTE* pbufD, const signed short* pbufS, int cnt)
{
//const signed short* pS = pbufS;
//BYTE* pD = pbufD;
const __m128i* pS = (const __m128i*)pbufS;
__m128i* pD = (__m128i*)pbufD;
int i;
for(i=0; i<cnt; i+=4)
{
// 同時對四個像素做飽和處理。即 將四個64位像素(4通道,每分量爲帶符號16位) 轉爲 四個32位像素(每分量爲無符號8位)。
pD[0] = _mm_packus_epi16(pS[0], pS[1]); // 飽和方式數據打包(帶符號16位->無符號8位)。等價於 for(i=0;i<8;++i){ pD[0].uB[i]=SU(pS[0].iW[i]); r.uB[8+i]=SU(pS[1].iW[i]); }
// next
pS += 2;
pD += 1;
}
}
// 進行測試
void runTest(char* szname, TESTPROC proc)
{
const int nLoop = 16; // 使用MMX/SSE指令時速度太快了,只好再多循環幾次
int i,j,k;
DWORD tm0, tm1; // 存儲時間
for(i=1; i<=3; ++i) // 多次測試
{
//tm0 = GetTickCount();
tm0 = timeGetTime();
// main
for(k=1; k<=nLoop; ++k)
{
for(j=1; j<=4000; ++j) // 重複運算幾次延長時間,避免計時精度問題
{
proc(bufD, bufS, DATASIZE);
}
}
// show
//tm1 = GetTickCount() - tm0;
tm1 = timeGetTime() - tm0;
printf("%s[%d]:\t%.1f\n", szname, i, (double)tm1/nLoop);
// check
//if (1==i)
//{
// // 檢查結果
// for(j=0; j<=16; ++j)
// printf("[%d]:\t%d\t%u\n", j, bufS[j], bufD[j]);
//}
}
}
int main(int argc, char* argv[])
{
int i; // 循環變量
//printf("Hello World!\n");
printf("== noif:VC6 SIMD ==");
// 初始化
srand( (unsigned)time( NULL ) );
for(i=0; i<DATASIZE*4; ++i)
{
bufS[i] = (signed short)((rand()&0x1FF) - 128); // 使數值在 [-128, 383] 區間
}
// 準備開始。可以將將進程優先級設爲實時
if (argc<=1)
{
printf("<Press any key to continue>");
_getch();
printf("\n");
}
// 進行測試
//runTest("f0_if", f0_if);
//runTest("f1_min", f1_min);
//runTest("f2_neg", f2_neg);
//runTest("f3_sar", f3_sar);
if (simd_mmx()) runTest("f4_mmx", f4_mmx);
if (simd_sse_level()>=SIMD_SSE_2) runTest("f5_sse", f5_sse);
// 結束前提示
if (argc<=1)
{
printf("<Press any key to exit>");
_getch();
printf("\n");
}
return 0;
}
#endif
SSE掩碼運算測試
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.