日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

[C] 跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac)...

發布時間:2025/1/21 linux 102 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [C] 跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac)... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:zyl910。

  本文面對對SSE等SIMD指令集有一定基礎的讀者,以單精度浮點數組求和為例演示了如何跨平臺使用SSE、AVX指令集。因使用了stdint、zintrin、ccpuid這三個模塊,可以完全避免手工編寫匯編代碼,具有很高可移植性。支持vc、gcc編譯器,在Windows、Linux、Mac這三大平臺上成功運行。


一、問題背景

  最初,我們只能使用匯編語言來編寫SIMD代碼。不僅寫起來很麻煩,而且易讀性、可維護性、移植性都較差。
  不久,VC、GCC等編譯器相繼支持了Intrinsic函數,使我們可以擺脫匯編,利用C語言來調用SIMD指令集,大大提高了易讀性和可維護。而且移植性也有提高,能在同一編譯器上實現32位與64位的平滑過渡。
  但當代碼在另一種編譯器編譯時,會遇到一些問題而無法編譯。甚至在使用同一種編譯器的不同版本時,也會遇到無法編譯問題。

  首先是整數類型問題——
  傳統C語言的short、int、long等整數類型是與平臺相關的,不同平臺上的位長是不同的(例如Windows是LLP64模型,Linux、Mac等Unix系統多采用LP64模型)。而使用SSE等SIMD指令集時需要精確計算數據的位數,不同位長的數據必須使用不同的指令來處理。
  有一個解決辦法,就是使用C99標準中stdint.h所提供的指定位長的整數類型。GCC對C99標準支持性較好,而VC的步驟很慢,貌似直到VC2010才支持stdint.h。而很多時候我們為了兼容舊代碼,不得不使用VC6等老版本的VC編譯器。

  其次是Intrinsic函數的頭文件問題,不同編譯器所使用的頭文件不同——
  對于早期版本VC,需要根據具體的指令集需求,手動引入mmintrin.h、xmmintrin.h等頭文件。對于VC2005或更高版本,引入intrin.h就行了,它會自動引入當前編譯器所支持的所有Intrinsic頭文件。
  對于早期版本GCC,也是手動引入mmintrin.h、xmmintrin.h等頭文件。而對于高版本的GCC,引入x86intrin.h就行了,它會自動引入當前編譯環境所允許的Intrinsic頭文件。

  再次是當前編譯環境下的Intrinsic函數集支持性問題——
  對于VC來說,VC6支持MMX、3DNow!、SSE、SSE2,然后更高版本的VC支持更多的指令集。但是,VC沒有提供檢測Intrinsic函數集支持性的辦法。例如你在VC2010上編寫了一段使用了AVX Intrinsic函數的代碼,但拿到VC2005上就不能通過編譯了。其次,VC不支持64位下的MMX,這讓一些老程序遷徙到64位版時遭來了一些麻煩。
  而對于GCC來說,它使用-mmmx、-msse等編譯器開關來啟用各種指令集,同時定義了對應的 __MMX__、__SSE__等宏,然后x86intrin.h會根據這些宏來聲明相應的Intrinsic函數集。__MMX__、__SSE__等宏可以幫助我們判斷Intrinsic函數集是否支持,但這只是GCC的專用功能。
  此外還有一些細節問題,例如某些Intrinsic函數僅在64下才能使用、有些老版本編譯器的頭文件缺少某個Intrinsic函數。所以我們希望有一種統一的方式來判斷Intrinsic函數集的支持性。

  除了編譯期間的問題外,還有運行期間的問題——
  在運行時,怎么檢測當前處理器支持哪些指令集?
  雖然X86體系提供了用來檢測處理器的CPUID指令,但它沒有規范的Intrinsic函數,在不同的編譯器上的用法不同。
  而且X86體系有很多種指令集,每種指令集具體的檢測方法是略有區別的。尤其是SSE、AVX這樣的SIMD指令集是需要操作系統配合才能正常使用的,所以在CPUID檢查通過后,還需要進一步驗證。


二、范例講解

2.1 事先準備

  為了解決上面提到的問題,我編寫了三個模塊——
stdint:智能支持C99的stdint.h,解決整數類型問題。最新版的地址是 http://www.cnblogs.com/zyl910/archive/2012/08/08/c99int.html 。
zintrin:在編譯時檢測Intrinsic函數集支持性,并自動引入相關頭文件、修正細節問題。最新版的地址是 http://www.cnblogs.com/zyl910/archive/2012/10/01/zintrin_v101.html 。
ccpuid:在編譯時檢測指令集的支持性。最新版的地址是 http://www.cnblogs.com/zyl910/archive/2012/10/13/ccpuid_v103.html 。

  這三個模塊的純C版就是一個頭文件,用起來很方便,將它們放在項目中,直接#include就行了。例如——

#define __STDC_LIMIT_MACROS 1 // C99整數范圍常量. [純C程序可以不用, 而C++程序必須定義該宏.]#include "zintrin.h" #include "ccpuid.h"

?

  因為stdint.h會被zintrin.h或ccpuid.h引用,所以不需要手動引入它。
  因為它們用到了C99整數范圍常量,所以應該在程序的最前面定義__STDC_LIMIT_MACROS宏(或者可以在項目配置、編譯器命令行等位置進行配置)。根據C99規范,純C程序可以不用, 而C++程序必須定義該宏。本文為了演示,定義了該宏。


2.2 C語言版

  我們先用C語言編寫一個基本的單精度浮點數組求和函數——

// 單精度浮點數組求和_基本版. // // result: 返回數組求和結果. // pbuf: 數組的首地址. // cntbuf: 數組長度. float sumfloat_base(const float* pbuf, size_t cntbuf) {float s = 0; // 求和變量. size_t i;for(i=0; i<cntbuf; ++i){s += pbuf[i];}return s; }

?

  該函數很容易理解——先將返回值賦初值0,然后循環加上數組中每一項的值。


2.3 SSE版

2.3.1 SSE普通版

  SSE寄存器是128位的,對應__m128類型,它能一次能處理4個單精度浮點數。
  很多SSE指令要求內存地址按16字節對齊。本文為了簡化,假定浮點數組的首地址是總是16字節對齊的,僅需要考慮數組長度不是4的整數倍問題。
  因使用了SSE Intrinsic函數,我們可以根據zintrin.h所提供的INTRIN_SSE宏進行條件編譯。
  代碼如下——

#ifdef INTRIN_SSE // 單精度浮點數組求和_SSE版. float sumfloat_sse(const float* pbuf, size_t cntbuf) {float s = 0; // 求和變量. size_t i;size_t nBlockWidth = 4; // 塊寬. SSE寄存器能一次處理4個float.size_t cntBlock = cntbuf / nBlockWidth; // 塊數.size_t cntRem = cntbuf % nBlockWidth; // 剩余數量.__m128 xfsSum = _mm_setzero_ps(); // 求和變量。[SSE] 賦初值0__m128 xfsLoad; // 加載.const float* p = pbuf; // SSE批量處理時所用的指針.const float* q; // 將SSE變量上的多個數值合并時所用指針.// SSE批量處理.for(i=0; i<cntBlock; ++i){xfsLoad = _mm_load_ps(p); // [SSE] 加載xfsSum = _mm_add_ps(xfsSum, xfsLoad); // [SSE] 單精浮點緊縮加法p += nBlockWidth;}// 合并.q = (const float*)&xfsSum;s = q[0] + q[1] + q[2] + q[3];// 處理剩下的.for(i=0; i<cntRem; ++i){s += p[i];}return s; }#endif // #ifdef INTRIN_SSE

?

  上述代碼大致可分為四個部分——
1. 變量定義與初始化。
2. SSE批量處理。即對前面能湊成4個一組的數據,利用SSE的128位寬度同時對4個數累加。
3. 合并。將__m128上的多個數值合并到求和變量。因考慮某些編譯器不能直接使用“.”來訪問__m128變量中的數據,于是利用指針q來訪問xfsSum中的數據。
4. 處理剩下的。即對尾部不能湊成4個一組的數據,采用基本的逐項相加算法。

  上述代碼總共用到了3個SSE Intrinsic函數——
_mm_setzero_ps:對應XORPS指令。將__m128上的每一個單精度浮點數均賦0值,偽代碼:for(i=0;i<4;++i) C[i]=0.0f。
_mm_load_ps:對應MOVPS指令。從內存中對齊加載4個單精度浮點數到__m128變量,偽代碼:for(i=0;i<4;++i) C[i]=_A[i]。
_mm_add_ps:對應ADDPS指令。相加,即對2個__m128變量的4個單精度浮點數進行垂直相加,偽代碼:for(i=0;i<4;++i) C[i]=A[i]+B[i]。


2.3.2 SSE四路循環展開版

  循環展開可以降低循環開銷,提高指令級并行性能。
  一般來說,四路循環展開就差不多夠了。我們可以很方便的將上一節的代碼改造為四路循環展開版——

// 單精度浮點數組求和_SSE四路循環展開版. float sumfloat_sse_4loop(const float* pbuf, size_t cntbuf) {float s = 0; // 返回值. size_t i;size_t nBlockWidth = 4*4; // 塊寬. SSE寄存器能一次處理4個float,然后循環展開4次.size_t cntBlock = cntbuf / nBlockWidth; // 塊數.size_t cntRem = cntbuf % nBlockWidth; // 剩余數量.__m128 xfsSum = _mm_setzero_ps(); // 求和變量。[SSE] 賦初值0__m128 xfsSum1 = _mm_setzero_ps();__m128 xfsSum2 = _mm_setzero_ps();__m128 xfsSum3 = _mm_setzero_ps();__m128 xfsLoad; // 加載. __m128 xfsLoad1;__m128 xfsLoad2;__m128 xfsLoad3;const float* p = pbuf; // SSE批量處理時所用的指針.const float* q; // 將SSE變量上的多個數值合并時所用指針.// SSE批量處理.for(i=0; i<cntBlock; ++i){xfsLoad = _mm_load_ps(p); // [SSE] 加載.xfsLoad1 = _mm_load_ps(p+4);xfsLoad2 = _mm_load_ps(p+8);xfsLoad3 = _mm_load_ps(p+12);xfsSum = _mm_add_ps(xfsSum, xfsLoad); // [SSE] 單精浮點緊縮加法xfsSum1 = _mm_add_ps(xfsSum1, xfsLoad1);xfsSum2 = _mm_add_ps(xfsSum2, xfsLoad2);xfsSum3 = _mm_add_ps(xfsSum3, xfsLoad3);p += nBlockWidth;}// 合并.xfsSum = _mm_add_ps(xfsSum, xfsSum1); // 兩兩合并(0~1).xfsSum2 = _mm_add_ps(xfsSum2, xfsSum3); // 兩兩合并(2~3).xfsSum = _mm_add_ps(xfsSum, xfsSum2); // 兩兩合并(0~3).q = (const float*)&xfsSum;s = q[0] + q[1] + q[2] + q[3];// 處理剩下的.for(i=0; i<cntRem; ++i){s += p[i];}return s; }

?


2.4 AVX版

2.4.1 AVX普通版

  AVX寄存器是256位的,對應__m256類型,它能一次能處理8個單精度浮點數。
  很多AVX指令要求內存地址按32字節對齊。本文為了簡化,假定浮點數組的首地址是總是32字節對齊的,僅需要考慮數組長度不是8的整數倍問題。
  因使用了AVX Intrinsic函數,我們可以根據zintrin.h所提供的INTRIN_AVX宏進行條件編譯。

  代碼如下——

#ifdef INTRIN_AVX // 單精度浮點數組求和_AVX版. float sumfloat_avx(const float* pbuf, size_t cntbuf) {float s = 0; // 求和變量. size_t i;size_t nBlockWidth = 8; // 塊寬. AVX寄存器能一次處理8個float.size_t cntBlock = cntbuf / nBlockWidth; // 塊數.size_t cntRem = cntbuf % nBlockWidth; // 剩余數量.__m256 yfsSum = _mm256_setzero_ps(); // 求和變量。[AVX] 賦初值0__m256 yfsLoad; // 加載.const float* p = pbuf; // AVX批量處理時所用的指針.const float* q; // 將AVX變量上的多個數值合并時所用指針.// AVX批量處理.for(i=0; i<cntBlock; ++i){yfsLoad = _mm256_load_ps(p); // [AVX] 加載yfsSum = _mm256_add_ps(yfsSum, yfsLoad); // [AVX] 單精浮點緊縮加法p += nBlockWidth;}// 合并.q = (const float*)&yfsSum;s = q[0] + q[1] + q[2] + q[3] + q[4] + q[5] + q[6] + q[7];// 處理剩下的.for(i=0; i<cntRem; ++i){s += p[i];}return s; }#endif // #ifdef INTRIN_AVX

?

  由上可見,將SSE Intrinsic代碼(sumfloat_sse)升級為 AVX Intrinsic代碼(sumfloat_avx)是很容易的——
1. 升級數據類型,將__m128升級成了__m256。
2. 升級Intrinsic函數,在函數名中加入255。例如_mm_setzero_ps、_mm_load_ps、_mm_add_ps,對應的AVX版函數是 _mm256_setzero_ps、_mm256_load_ps、_mm256_add_ps。
3. 因位寬翻倍,地址計算與數據合并的代碼需稍加改動。

  當使用VC2010編譯含有AVX的代碼時,VC會提醒你——
warning C4752: 發現 Intel(R) 高級矢量擴展;請考慮使用 /arch:AVX

  目前“/arch:AVX”尚未整合到項目屬性的“C++\代碼生成\啟用增強指令集”中,需要手動在項目屬性的“C++\命令行”的附加選項中加上“/arch:AVX”——

詳見MSDN——
http://msdn.microsoft.com/zh-cn/library/7t5yh4fd(v=vs.100).aspx
在 Visual Studio 中設置 /arch:AVX 編譯器選項
1.打開項目的“屬性頁”對話框。 有關更多信息,請參見 如何:打開項目屬性頁。
2.單擊“C/C++”文件夾。
3.單擊“命令行”屬性頁。
4.在“附加選項”框中添加 /arch:AVX。


2.4.2 AVX四路循環展開版

  同樣的,我們可以編寫AVX四路循環展開版——

// 單精度浮點數組求和_AVX四路循環展開版. float sumfloat_avx_4loop(const float* pbuf, size_t cntbuf) {float s = 0; // 求和變量. size_t i;size_t nBlockWidth = 8*4; // 塊寬. AVX寄存器能一次處理8個float,然后循環展開4次.size_t cntBlock = cntbuf / nBlockWidth; // 塊數.size_t cntRem = cntbuf % nBlockWidth; // 剩余數量.__m256 yfsSum = _mm256_setzero_ps(); // 求和變量。[AVX] 賦初值0__m256 yfsSum1 = _mm256_setzero_ps();__m256 yfsSum2 = _mm256_setzero_ps();__m256 yfsSum3 = _mm256_setzero_ps();__m256 yfsLoad; // 加載. __m256 yfsLoad1;__m256 yfsLoad2;__m256 yfsLoad3;const float* p = pbuf; // AVX批量處理時所用的指針.const float* q; // 將AVX變量上的多個數值合并時所用指針.// AVX批量處理.for(i=0; i<cntBlock; ++i){yfsLoad = _mm256_load_ps(p); // [AVX] 加載.yfsLoad1 = _mm256_load_ps(p+8);yfsLoad2 = _mm256_load_ps(p+16);yfsLoad3 = _mm256_load_ps(p+24);yfsSum = _mm256_add_ps(yfsSum, yfsLoad); // [AVX] 單精浮點緊縮加法yfsSum1 = _mm256_add_ps(yfsSum1, yfsLoad1);yfsSum2 = _mm256_add_ps(yfsSum2, yfsLoad2);yfsSum3 = _mm256_add_ps(yfsSum3, yfsLoad3);p += nBlockWidth;}// 合并.yfsSum = _mm256_add_ps(yfsSum, yfsSum1); // 兩兩合并(0~1).yfsSum2 = _mm256_add_ps(yfsSum2, yfsSum3); // 兩兩合并(2~3).yfsSum = _mm256_add_ps(yfsSum, yfsSum2); // 兩兩合并(0~3).q = (const float*)&yfsSum;s = q[0] + q[1] + q[2] + q[3] + q[4] + q[5] + q[6] + q[7];// 處理剩下的.for(i=0; i<cntRem; ++i){s += p[i];}return s; }

?

2.5 測試框架

2.5.1 測試所用的數組

  首先考慮一下測試所用的數組的長度應該是多少比較好。
  為了避免內存帶寬問題,這個數組最好能放在L1 Data Cache中。現在的處理器的L1 Data Cache一般是32KB,為了保險最好再除以2,那么數組的長度應該是 32KB/(2*sizeof(float))=4096。
  其次考慮內存對齊問題,avx要求32字節對齊。我們可以定義一個ATTR_ALIGN宏來統一處理變量的內存對齊問題。
  該數組定義如下——

// 變量對齊. #ifndef ATTR_ALIGN # if defined(__GNUC__) // GCC # define ATTR_ALIGN(n) __attribute__((aligned(n))) # else // 否則使用VC格式. # define ATTR_ALIGN(n) __declspec(align(n)) # endif #endif // #ifndef ATTR_ALIGN#define BUFSIZE 4096 // = 32KB{L1 Cache} / (2 * sizeof(float)) ATTR_ALIGN(32) float buf[BUFSIZE];

?


2.5.2 測試函數

  如果為每一個函數都編寫一套測試代碼,那不僅代碼量大,而且不易維護。
  可以考慮利用函數指針來實現一套測試框架。
  因sumfloat_base等函數的簽名是一致的,于是可以定義這樣的一種函數指針——
// 測試時的函數類型
typedef float (*TESTPROC)(const float* pbuf, size_t cntbuf);

  然后再編寫一個對TESTPROC函數指針進行測試的函數——

// 進行測試 void runTest(const char* szname, TESTPROC proc) {const int testloop = 4000; // 重復運算幾次延長時間,避免計時精度問題.const clock_t TIMEOUT = CLOCKS_PER_SEC/2; // 最短測試時間.int i,j,k;clock_t tm0, dt; // 存儲時間.double mps; // M/s.double mps_good = 0; // 最佳M/s. 因線程切換會導致的數值波動, 于是選取最佳值.volatile float n=0; // 避免內循環被優化.for(i=1; i<=3; ++i) // 多次測試. {tm0 = clock();// maink=0;do{for(j=1; j<=testloop; ++j) // 重復運算幾次延長時間,避免計時開銷帶來的影響. {n = proc(buf, BUFSIZE); // 避免內循環被編譯優化消掉. }++k;dt = clock() - tm0;}while(dt<TIMEOUT);// showmps = (double)k*testloop*BUFSIZE*CLOCKS_PER_SEC/(1024.0*1024.0*dt); // k*testloop*BUFSIZE/(1024.0*1024.0) 將數據規模換算為M,然后再乘以 CLOCKS_PER_SEC/dt 換算為M/s .if (mps_good<mps) mps_good=mps; // 選取最佳值.//printf("%s:\t%.0f M/s\t//%f\n", szname, mps, n); }printf("%s:\t%.0f M/s\t//%f\n", szname, mps_good, n); }

?

  j是最內層的循環,負責多次調用TESTPROC函數指針。如果每調用一次TESTPROC函數指針后又調用clock函數,那會帶來較大的計時開銷,影響評測成績。
  k循環負責檢測超時。當發現超過預定時限,便計算mps,即每秒鐘處理了多少百萬個單精度浮點數。然后存儲最佳的mps。
  i是最外層循環的循環變量,循環3次然后報告最佳值。


2.5.3 進行測試

  在進行測試之前,需要對buf數組進行初始化,將數組元素賦隨機值——

// init buf srand( (unsigned)time( NULL ) );for (i = 0; i < BUFSIZE; i++) buf[i] = (float)(rand() & 0x3f); // 使用&0x3f是為了讓求和后的數值不會超過float類型的有效位數,便于觀察結果是否正確.

?

  然后可以開始測試了——

// testrunTest("sumfloat_base", sumfloat_base); // 單精度浮點數組求和_基本版. #ifdef INTRIN_SSEif (simd_sse_level(NULL) >= SIMD_SSE_1){runTest("sumfloat_sse", sumfloat_sse); // 單精度浮點數組求和_SSE版.runTest("sumfloat_sse_4loop", sumfloat_sse_4loop); // 單精度浮點數組求和_SSE四路循環展開版. } #endif // #ifdef INTRIN_SSE #ifdef INTRIN_AVXif (simd_avx_level(NULL) >= SIMD_AVX_1){runTest("sumfloat_avx", sumfloat_avx); // 單精度浮點數組求和_AVX版.runTest("sumfloat_avx_4loop", sumfloat_avx_4loop); // 單精度浮點數組求和_AVX四路循環展開版. } #endif // #ifdef INTRIN_AVX

?

  INTRIN_SSE、INTRIN_AVX 宏是 zintrin.h 提供的,用于在編譯時檢測編譯器是否支持SSE、AVX指令集。
  simd_sse_level、simd_avx_level函數是 ccpuid.h 提供的,用于在運行時檢測當前系統環境是否支持SSE、AVX指令集。


2.6 雜項

  為了方便對比測試,可以在程序啟動時顯示程序版本、編譯器名稱、CPU型號信息。即在main函數中加上——

char szBuf[64];int i;printf("simdsumfloat v1.00 (%dbit)\n", INTRIN_WORDSIZE);printf("Compiler: %s\n", COMPILER_NAME);cpu_getbrand(szBuf);printf("CPU:\t%s\n", szBuf);printf("\n");

?

  INTRIN_WORDSIZE 宏是 zintrin.h 提供的,為當前機器的字長。
  cpu_getbrand是 ccpuid.h 提供的,用于獲得CPU型號字符串。
  COMPILER_NAME 是一個用來獲得編譯器名稱的宏,它的詳細定義是——

// Compiler name #define MACTOSTR(x) #x #define MACROVALUESTR(x) MACTOSTR(x) #if defined(__ICL) // Intel C++ # if defined(__VERSION__) # define COMPILER_NAME "Intel C++ " __VERSION__ # elif defined(__INTEL_COMPILER_BUILD_DATE) # define COMPILER_NAME "Intel C++ (" MACROVALUESTR(__INTEL_COMPILER_BUILD_DATE) ")" # else # define COMPILER_NAME "Intel C++" # endif // # if defined(__VERSION__) #elif defined(_MSC_VER) // Microsoft VC++ # if defined(_MSC_FULL_VER) # define COMPILER_NAME "Microsoft VC++ (" MACROVALUESTR(_MSC_FULL_VER) ")" # elif defined(_MSC_VER) # define COMPILER_NAME "Microsoft VC++ (" MACROVALUESTR(_MSC_VER) ")" # else # define COMPILER_NAME "Microsoft VC++" # endif // # if defined(_MSC_FULL_VER) #elif defined(__GNUC__) // GCC # if defined(__CYGWIN__) # define COMPILER_NAME "GCC(Cygmin) " __VERSION__ # elif defined(__MINGW32__) # define COMPILER_NAME "GCC(MinGW) " __VERSION__ # else # define COMPILER_NAME "GCC " __VERSION__ # endif // # if defined(_MSC_FULL_VER) #else # define COMPILER_NAME "Unknown Compiler" #endif // #if defined(__ICL) // Intel C++

?

?

三、全部代碼

3.1 simdsumfloat.c

  全部代碼——

simdsumfloat.c #define __STDC_LIMIT_MACROS 1 // C99整數范圍常量. [純C程序可以不用, 而C++程序必須定義該宏.]#include <stdlib.h> #include <stdio.h> #include <time.h>#include "zintrin.h" #include "ccpuid.h"// Compiler name #define MACTOSTR(x) #x #define MACROVALUESTR(x) MACTOSTR(x) #if defined(__ICL) // Intel C++ # if defined(__VERSION__) # define COMPILER_NAME "Intel C++ " __VERSION__ # elif defined(__INTEL_COMPILER_BUILD_DATE) # define COMPILER_NAME "Intel C++ (" MACROVALUESTR(__INTEL_COMPILER_BUILD_DATE) ")" # else # define COMPILER_NAME "Intel C++" # endif // # if defined(__VERSION__) #elif defined(_MSC_VER) // Microsoft VC++ # if defined(_MSC_FULL_VER) # define COMPILER_NAME "Microsoft VC++ (" MACROVALUESTR(_MSC_FULL_VER) ")" # elif defined(_MSC_VER) # define COMPILER_NAME "Microsoft VC++ (" MACROVALUESTR(_MSC_VER) ")" # else # define COMPILER_NAME "Microsoft VC++" # endif // # if defined(_MSC_FULL_VER) #elif defined(__GNUC__) // GCC # if defined(__CYGWIN__) # define COMPILER_NAME "GCC(Cygmin) " __VERSION__ # elif defined(__MINGW32__) # define COMPILER_NAME "GCC(MinGW) " __VERSION__ # else # define COMPILER_NAME "GCC " __VERSION__ # endif // # if defined(_MSC_FULL_VER) #else # define COMPILER_NAME "Unknown Compiler" #endif // #if defined(__ICL) // Intel C++// // sumfloat: 單精度浮點數組求和的函數 //// 單精度浮點數組求和_基本版. // // result: 返回數組求和結果. // pbuf: 數組的首地址. // cntbuf: 數組長度. float sumfloat_base(const float* pbuf, size_t cntbuf) {float s = 0; // 求和變量. size_t i;for(i=0; i<cntbuf; ++i){s += pbuf[i];}return s; }#ifdef INTRIN_SSE // 單精度浮點數組求和_SSE版. float sumfloat_sse(const float* pbuf, size_t cntbuf) {float s = 0; // 求和變量. size_t i;size_t nBlockWidth = 4; // 塊寬. SSE寄存器能一次處理4個float.size_t cntBlock = cntbuf / nBlockWidth; // 塊數.size_t cntRem = cntbuf % nBlockWidth; // 剩余數量.__m128 xfsSum = _mm_setzero_ps(); // 求和變量。[SSE] 賦初值0__m128 xfsLoad; // 加載.const float* p = pbuf; // SSE批量處理時所用的指針.const float* q; // 將SSE變量上的多個數值合并時所用指針.// SSE批量處理.for(i=0; i<cntBlock; ++i){xfsLoad = _mm_load_ps(p); // [SSE] 加載xfsSum = _mm_add_ps(xfsSum, xfsLoad); // [SSE] 單精浮點緊縮加法p += nBlockWidth;}// 合并.q = (const float*)&xfsSum;s = q[0] + q[1] + q[2] + q[3];// 處理剩下的.for(i=0; i<cntRem; ++i){s += p[i];}return s; }// 單精度浮點數組求和_SSE四路循環展開版. float sumfloat_sse_4loop(const float* pbuf, size_t cntbuf) {float s = 0; // 返回值. size_t i;size_t nBlockWidth = 4*4; // 塊寬. SSE寄存器能一次處理4個float,然后循環展開4次.size_t cntBlock = cntbuf / nBlockWidth; // 塊數.size_t cntRem = cntbuf % nBlockWidth; // 剩余數量.__m128 xfsSum = _mm_setzero_ps(); // 求和變量。[SSE] 賦初值0__m128 xfsSum1 = _mm_setzero_ps();__m128 xfsSum2 = _mm_setzero_ps();__m128 xfsSum3 = _mm_setzero_ps();__m128 xfsLoad; // 加載. __m128 xfsLoad1;__m128 xfsLoad2;__m128 xfsLoad3;const float* p = pbuf; // SSE批量處理時所用的指針.const float* q; // 將SSE變量上的多個數值合并時所用指針.// SSE批量處理.for(i=0; i<cntBlock; ++i){xfsLoad = _mm_load_ps(p); // [SSE] 加載.xfsLoad1 = _mm_load_ps(p+4);xfsLoad2 = _mm_load_ps(p+8);xfsLoad3 = _mm_load_ps(p+12);xfsSum = _mm_add_ps(xfsSum, xfsLoad); // [SSE] 單精浮點緊縮加法xfsSum1 = _mm_add_ps(xfsSum1, xfsLoad1);xfsSum2 = _mm_add_ps(xfsSum2, xfsLoad2);xfsSum3 = _mm_add_ps(xfsSum3, xfsLoad3);p += nBlockWidth;}// 合并.xfsSum = _mm_add_ps(xfsSum, xfsSum1); // 兩兩合并(0~1).xfsSum2 = _mm_add_ps(xfsSum2, xfsSum3); // 兩兩合并(2~3).xfsSum = _mm_add_ps(xfsSum, xfsSum2); // 兩兩合并(0~3).q = (const float*)&xfsSum;s = q[0] + q[1] + q[2] + q[3];// 處理剩下的.for(i=0; i<cntRem; ++i){s += p[i];}return s; } #endif // #ifdef INTRIN_SSE#ifdef INTRIN_AVX // 單精度浮點數組求和_AVX版. float sumfloat_avx(const float* pbuf, size_t cntbuf) {float s = 0; // 求和變量. size_t i;size_t nBlockWidth = 8; // 塊寬. AVX寄存器能一次處理8個float.size_t cntBlock = cntbuf / nBlockWidth; // 塊數.size_t cntRem = cntbuf % nBlockWidth; // 剩余數量.__m256 yfsSum = _mm256_setzero_ps(); // 求和變量。[AVX] 賦初值0__m256 yfsLoad; // 加載.const float* p = pbuf; // AVX批量處理時所用的指針.const float* q; // 將AVX變量上的多個數值合并時所用指針.// AVX批量處理.for(i=0; i<cntBlock; ++i){yfsLoad = _mm256_load_ps(p); // [AVX] 加載yfsSum = _mm256_add_ps(yfsSum, yfsLoad); // [AVX] 單精浮點緊縮加法p += nBlockWidth;}// 合并.q = (const float*)&yfsSum;s = q[0] + q[1] + q[2] + q[3] + q[4] + q[5] + q[6] + q[7];// 處理剩下的.for(i=0; i<cntRem; ++i){s += p[i];}return s; }// 單精度浮點數組求和_AVX四路循環展開版. float sumfloat_avx_4loop(const float* pbuf, size_t cntbuf) {float s = 0; // 求和變量. size_t i;size_t nBlockWidth = 8*4; // 塊寬. AVX寄存器能一次處理8個float,然后循環展開4次.size_t cntBlock = cntbuf / nBlockWidth; // 塊數.size_t cntRem = cntbuf % nBlockWidth; // 剩余數量.__m256 yfsSum = _mm256_setzero_ps(); // 求和變量。[AVX] 賦初值0__m256 yfsSum1 = _mm256_setzero_ps();__m256 yfsSum2 = _mm256_setzero_ps();__m256 yfsSum3 = _mm256_setzero_ps();__m256 yfsLoad; // 加載. __m256 yfsLoad1;__m256 yfsLoad2;__m256 yfsLoad3;const float* p = pbuf; // AVX批量處理時所用的指針.const float* q; // 將AVX變量上的多個數值合并時所用指針.// AVX批量處理.for(i=0; i<cntBlock; ++i){yfsLoad = _mm256_load_ps(p); // [AVX] 加載.yfsLoad1 = _mm256_load_ps(p+8);yfsLoad2 = _mm256_load_ps(p+16);yfsLoad3 = _mm256_load_ps(p+24);yfsSum = _mm256_add_ps(yfsSum, yfsLoad); // [AVX] 單精浮點緊縮加法yfsSum1 = _mm256_add_ps(yfsSum1, yfsLoad1);yfsSum2 = _mm256_add_ps(yfsSum2, yfsLoad2);yfsSum3 = _mm256_add_ps(yfsSum3, yfsLoad3);p += nBlockWidth;}// 合并.yfsSum = _mm256_add_ps(yfsSum, yfsSum1); // 兩兩合并(0~1).yfsSum2 = _mm256_add_ps(yfsSum2, yfsSum3); // 兩兩合并(2~3).yfsSum = _mm256_add_ps(yfsSum, yfsSum2); // 兩兩合并(0~3).q = (const float*)&yfsSum;s = q[0] + q[1] + q[2] + q[3] + q[4] + q[5] + q[6] + q[7];// 處理剩下的.for(i=0; i<cntRem; ++i){s += p[i];}return s; }#endif // #ifdef INTRIN_AVX// // main //// 變量對齊. #ifndef ATTR_ALIGN # if defined(__GNUC__) // GCC # define ATTR_ALIGN(n) __attribute__((aligned(n))) # else // 否則使用VC格式. # define ATTR_ALIGN(n) __declspec(align(n)) # endif #endif // #ifndef ATTR_ALIGN#define BUFSIZE 4096 // = 32KB{L1 Cache} / (2 * sizeof(float)) ATTR_ALIGN(32) float buf[BUFSIZE];// 測試時的函數類型 typedef float (*TESTPROC)(const float* pbuf, size_t cntbuf);// 進行測試 void runTest(const char* szname, TESTPROC proc) {const int testloop = 4000; // 重復運算幾次延長時間,避免計時精度問題.const clock_t TIMEOUT = CLOCKS_PER_SEC/2; // 最短測試時間.int i,j,k;clock_t tm0, dt; // 存儲時間.double mps; // M/s.double mps_good = 0; // 最佳M/s. 因線程切換會導致的數值波動, 于是選取最佳值.volatile float n=0; // 避免內循環被優化.for(i=1; i<=3; ++i) // 多次測試. {tm0 = clock();// maink=0;do{for(j=1; j<=testloop; ++j) // 重復運算幾次延長時間,避免計時開銷帶來的影響. {n = proc(buf, BUFSIZE); // 避免內循環被編譯優化消掉. }++k;dt = clock() - tm0;}while(dt<TIMEOUT);// showmps = (double)k*testloop*BUFSIZE*CLOCKS_PER_SEC/(1024.0*1024.0*dt); // k*testloop*BUFSIZE/(1024.0*1024.0) 將數據規模換算為M,然后再乘以 CLOCKS_PER_SEC/dt 換算為M/s .if (mps_good<mps) mps_good=mps; // 選取最佳值.//printf("%s:\t%.0f M/s\t//%f\n", szname, mps, n); }printf("%s:\t%.0f M/s\t//%f\n", szname, mps_good, n); }int main(int argc, char* argv[]) {char szBuf[64];int i;printf("simdsumfloat v1.00 (%dbit)\n", INTRIN_WORDSIZE);printf("Compiler: %s\n", COMPILER_NAME);cpu_getbrand(szBuf);printf("CPU:\t%s\n", szBuf);printf("\n");// init buf srand( (unsigned)time( NULL ) );for (i = 0; i < BUFSIZE; i++) buf[i] = (float)(rand() & 0x3f); // 使用&0x3f是為了讓求和后的數值不會超過float類型的有效位數,便于觀察結果是否正確.// testrunTest("sumfloat_base", sumfloat_base); // 單精度浮點數組求和_基本版. #ifdef INTRIN_SSEif (simd_sse_level(NULL) >= SIMD_SSE_1){runTest("sumfloat_sse", sumfloat_sse); // 單精度浮點數組求和_SSE版.runTest("sumfloat_sse_4loop", sumfloat_sse_4loop); // 單精度浮點數組求和_SSE四路循環展開版. } #endif // #ifdef INTRIN_SSE #ifdef INTRIN_AVXif (simd_avx_level(NULL) >= SIMD_AVX_1){runTest("sumfloat_avx", sumfloat_avx); // 單精度浮點數組求和_AVX版.runTest("sumfloat_avx_4loop", sumfloat_avx_4loop); // 單精度浮點數組求和_AVX四路循環展開版. } #endif // #ifdef INTRIN_AVXreturn 0; }

?


3.2 makefile

  全部代碼——

makefile # flags CC = g++ CFS = -Wall -msse# args RELEASE =0 BITS = CFLAGS =# [args] 生成模式. 0代表debug模式, 1代表release模式. make RELEASE=1. ifeq ($(RELEASE),0)# debugCFS += -g else# releaseCFS += -O3 -DNDEBUG//CFS += -O3 -g -DNDEBUG endif# [args] 程序位數. 32代表32位程序, 64代表64位程序, 其他默認. make BITS=32. ifeq ($(BITS),32)CFS += -m32 elseifeq ($(BITS),64)CFS += -m64elseendif endif# [args] 使用 CFLAGS 添加新的參數. make CFLAGS="-mavx". CFS += $(CFLAGS).PHONY : all clean# files TARGETS = simdsumfloat OBJS = simdsumfloat.oall : $(TARGETS)simdsumfloat : $(OBJS)$(CC) $(CFS) -o $@ $^simdsumfloat.o : simdsumfloat.c zintrin.h ccpuid.h$(CC) $(CFS) -c $<clean :rm -f $(OBJS) $(TARGETS) $(addsuffix .exe,$(TARGETS))

?


四、編譯測試

4.1 編譯

  在以下編譯器中成功編譯——
VC6:x86版。
VC2003:x86版。
VC2005:x86版。
VC2010:x86版、x64版。
GCC 4.7.0(Fedora 17 x64):x86版、x64版。
GCC 4.6.2(MinGW(20120426)):x86版。
GCC 4.7.1(TDM-GCC(MinGW-w64)):x86版、x64版。
llvm-gcc-4.2(Mac OS X Lion 10.7.4, Xcode 4.4.1):x86版、x64版。


4.2 測試

  因虛擬機上的有效率損失,于是僅在真實系統上進行測試。

  系統環境——
CPU:Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz
操作系統:Windows 7 SP1 x64版

  然后分別運行VC與GCC編譯的Release版可執行文件,即以下4個程序——
exe\simdsumfloat_vc32.exe:VC2010 SP1 編譯的32位程序,/O2 /arch:SSE2。
exe\simdsumfloat_vc64.exe:VC2010 SP1 編譯的64位程序,/O2 /arch:AVX。
exe\simdsumfloat_gcc32.exe:GCC 4.7.1(TDM-GCC(MinGW-w64)) 編譯的32位程序,-O3 -mavx。
exe\simdsumfloat_gcc64.exe:GCC 4.7.1(TDM-GCC(MinGW-w64)) 編譯的64位程序,-O3 -mavx。

  測試結果(使用cmdarg_ui)——

?

參考文獻——
《Intel? 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes:1, 2A, 2B, 2C, 3A, 3B, and 3C》044US. August 2012. http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
《Intel? Architecture Instruction Set Extensions Programming Reference》014. AUGUST 2012. http://software.intel.com/en-us/avx/
《AMD64 Architecture Programmer’s Manual Volume 4: 128-Bit and 256-Bit Media Instructions》. December 2011. http://developer.amd.com/documentation/guides/Pages/default.aspx#manuals
《[C] 讓VC、BCB支持C99的整數類型(stdint.h、inttypes.h)(兼容GCC)》. http://www.cnblogs.com/zyl910/archive/2012/08/08/c99int.html
《[C] zintrin.h: 智能引入intrinsic函數 V1.01版。改進對Mac OS X的支持,增加INTRIN_WORDSIZE宏》. http://www.cnblogs.com/zyl910/archive/2012/10/01/zintrin_v101.html
《[C/C++] ccpuid:CPUID信息模塊 V1.03版,改進mmx/sse指令可用性檢查(使用signal、setjmp,支持純C)、修正AVX檢查Bug》. http://www.cnblogs.com/zyl910/archive/2012/10/13/ccpuid_v103.html
《[x86]SIMD指令集發展歷程表(MMX、SSE、AVX等)》. http://www.cnblogs.com/zyl910/archive/2012/02/26/x86_simd_table.html
《SIMD(MMX/SSE/AVX)變量命名規范心得》. http://www.cnblogs.com/zyl910/archive/2012/04/23/simd_var_name.html
《GCC 64位程序的makefile條件編譯心得——32位版與64位版、debug版與release版(兼容MinGW、TDM-GCC)》. http://www.cnblogs.com/zyl910/archive/2012/08/14/gcc64_make.html
《[C#] cmdarg_ui:“簡單參數命令行程序”的通用圖形界面》.? http://www.cnblogs.com/zyl910/archive/2012/06/19/cmdarg_ui.html


源碼下載——
http://files.cnblogs.com/zyl910/simdsumfloat.rar

總結

以上是生活随笔為你收集整理的[C] 跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac)...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

九九精品视频在线观看 | www.香蕉视频在线观看 | 欧美精品乱码久久久久久按摩 | 中文字幕一区在线 | 免费成人黄色av | 色www免费视频 | 日韩影视精品 | 伊色综合久久之综合久久 | 婷婷在线观看视频 | 日本一区二区不卡高清 | 国产一区二区三区久久久 | 日韩电影在线一区 | 精品视频在线视频 | 狠狠色伊人亚洲综合网站野外 | 国产另类av | 五月婷婷丁香激情 | 视频在线亚洲 | 日韩成人黄色av | 国产精品免费久久 | av三级在线免费观看 | 国产九九九精品视频 | 久久 亚洲视频 | 亚洲精品综合一区二区 | 欧美成人亚洲 | 成人免费在线网 | 99精品小视频| 日本久久久久久久久久久 | 中文字幕欧美日韩va免费视频 | 精品美女视频 | 美女视频永久黄网站免费观看国产 | 国产伦理久久 | 婷婷在线免费视频 | 国产精品久久免费看 | av丁香 | 国语精品免费视频 | 精品国产片 | 国产精品久久久久久久久搜平片 | 免费日韩一级片 | 国产最新视频在线观看 | 精品久久久久久久久久 | 日本三级久久久 | 久久av一区二区三区亚洲 | 成人97视频一区二区 | 超碰在线官网 | 日韩精品一区二区在线观看 | 九九99视频 | 国产在线不卡精品 | 亚洲五月婷婷 | 国产精品毛片一区二区三区 | 国产精品专区在线 | japanese黑人亚洲人4k | 一区二区三区免费 | 99视频免费| 亚洲精色| 在线黄色国产 | 亚洲日韩中文字幕 | 色婷婷狠狠五月综合天色拍 | 91在线视频| 免费观看www视频 | 园产精品久久久久久久7电影 | 久草视频在线免费播放 | 波多野结衣视频一区 | 波多野结衣久久精品 | 九九热久久免费视频 | 狠狠干网| 国偷自产视频一区二区久 | 在线观看一级视频 | 一区二区三区影院 | 亚洲女裸体| av7777777| 日韩免费视频观看 | 中文字幕在线一二 | 亚洲女人天堂成人av在线 | 国产剧情一区二区在线观看 | 伊人婷婷网 | 国产成人av福利 | 色吊丝在线永久观看最新版本 | 国产精品第十页 | 韩日在线一区 | 国产精品久久一卡二卡 | 精品在线观看一区二区 | 五月婷婷电影网 | 91av视频| 免费a视频在线观看 | 在线国产视频 | 在线观看亚洲精品 | 久久久久久久久国产 | 涩涩爱夜夜爱 | 三级黄色网络 | 一本大道久久精品懂色aⅴ 五月婷社区 | 久久国产精品第一页 | 91福利视频网站 | 手机在线小视频 | 久久精品国产第一区二区三区 | 免费日韩一区二区三区 | 97精品超碰一区二区三区 | 国产涩图 | 在线免费黄色av | 97在线观看免费高清完整版在线观看 | 欧美日韩免费视频 | 中国一区二区视频 | 国产精品日韩在线 | 免费在线观看的av网站 | 色七七亚洲影院 | 精品在线播放视频 | 日韩av在线小说 | 久久国产免费视频 | 天天干天天摸 | 91在线精品视频 | 激情综合六月 | 91福利在线观看 | 最新日韩视频在线观看 | 粉嫩高清一区二区三区 | 亚洲另类在线视频 | 在线观看免费中文字幕 | 亚洲精品国产精品乱码在线观看 | 亚洲国产视频在线 | 日韩在线观看第一页 | 久久久久在线观看 | 久久色在线观看 | www.天天干.com| 亚洲国产精品日韩 | 国产精品剧情 | 99久久网站 | 亚洲国产精品成人女人久久 | 久久久香蕉视频 | 久久国产精品99久久人人澡 | 国产一级免费视频 | 国产.精品.日韩.另类.中文.在线.播放 | 天天躁天天躁天天躁婷 | 人人爽人人爽人人爽学生一级 | 国产馆在线播放 | 亚洲国产网站 | 99精品国产99久久久久久福利 | 日本久久高清视频 | 久久婷综合| 国产精品视频在线看 | 久久久香蕉视频 | 久久综合色播五月 | 色综合中文字幕 | 91九色视频在线播放 | 亚洲一二三久久 | 超碰在97 | 手机av电影在线观看 | 一区二区欧美在线观看 | 国产黄色片免费 | 中文字幕日韩精品有码视频 | 亚洲欧美视频在线 | 色视频在线看 | 五月天六月婷 | 日本精品一区二区在线观看 | 高清免费在线视频 | 中文字幕亚洲综合久久五月天色无吗'' | 国产91精品久久久久 | 国产色妞影院wwwxxx | 欧美va日韩va| 亚洲成 人精品 | 久久久99精品免费观看app | a级国产乱理伦片在线观看 亚洲3级 | 日韩av资源站 | 深爱激情综合网 | a级黄色片视频 | 日韩av福利在线 | 黄色网址国产 | 一级黄视频| 亚洲午夜精品久久久久久久久 | 国产免费视频在线 | 国产青春久久久国产毛片 | 午夜久久久精品 | 久草免费福利在线观看 | 91亚洲国产成人久久精品网站 | 91视频啪| 丰满少妇对白在线偷拍 | 久久成人精品电影 | 中文字幕av有码 | 91精品视频观看 | 欧美久久久久久久久久久久 | 亚洲毛片视频 | 日韩a级黄色 | 日韩午夜三级 | 日韩激情在线 | 欧美激情精品久久久久久变态 | 韩日视频在线 | 久久精品91视频 | 中文字幕精品www乱入免费视频 | 国产精品久久久久久久午夜 | 天堂av中文字幕 | 国产一区二区不卡视频 | 中文在线8新资源库 | 亚洲成人网在线 | 日本激情中文字幕 | 免费视频一二三区 | 欧美日韩不卡一区 | 欧美国产三区 | 欧美日韩高清国产 | 国产精品午夜8888 | 99久久精品免费看国产免费软件 | 在线视频中文字幕一区 | 国产人成免费视频 | 中文字幕在线一区观看 | 美女视频黄是免费的 | 色综合天天综合在线视频 | 国产精品美女久久久久久久 | 91漂亮少妇露脸在线播放 | 91精品福利在线 | 一本一本久久a久久精品综合妖精 | 精品一区二区在线免费观看 | 欧美日韩一二三四区 | 蜜臀aⅴ国产精品久久久国产 | 91重口视频 | 日本久久精| 国产高清视频免费在线观看 | 182午夜在线观看 | 操操操日日日干干干 | 青青河边草免费 | 激情xxxx | 亚洲午夜精品电影 | 久保带人| 国产精品久久视频 | 999久久久久久久久 69av视频在线观看 | 国产精品美女久久久久久久久 | 91久久精品一区二区三区 | 五月开心激情 | 国产精品视频线看 | 精品久久网站 | 亚洲作爱视频 | 欧美午夜性生活 | 欧美精品三级在线观看 | 色资源网在线观看 | 久久综合久久八八 | 亚洲激情校园春色 | 日韩在线免费观看视频 | 97超碰精品 | 国产精品一区二区av日韩在线 | 91福利在线观看 | 国产精品美女久久久久久久 | 久久国内精品99久久6app | 成人片在线播放 | 成人黄色短片 | 久久久久草 | 五月婷婷在线视频观看 | 久久久国产一区二区三区 | 国产精品入口传媒 | 亚洲午夜精品在线观看 | 96亚洲精品久久久蜜桃 | 麻豆91在线观看 | 日本久久成人中文字幕电影 | 色综合天天色综合 | 一区精品久久 | 9i看片成人免费看片 | 日韩专区中文字幕 | 青青河边草免费 | 免费在线黄色av | 四川妇女搡bbbb搡bbbb搡 | 日韩中文字幕亚洲一区二区va在线 | 国产分类视频 | 91中文在线观看 | 亚州视频在线 | 久久久久9999亚洲精品 | 97精品超碰一区二区三区 | 日韩精品视频久久 | 制服丝袜在线91 | 欧美日韩精品在线视频 | 中文字幕文字幕一区二区 | 欧美精品二区 | 亚洲国产精品久久 | 香蕉视频在线观看免费 | 国产精品大片免费观看 | av在观看| 亚洲精品综合一二三区在线观看 | 综合色狠狠 | 黄色国产大片 | 最新av在线网站 | 亚洲黄色av网址 | 日韩精品久久久久久 | 久久国产影视 | 天天干天天射天天爽 | 中文在线中文a | 天天干天天操天天爱 | 99精品视频在线播放免费 | 欧美日韩一区三区 | 精产嫩模国品一二三区 | 国内视频1区 | 欧美一级在线 | 99精品在这里 | 综合激情网... | 99c视频高清免费观看 | 亚洲v欧美v国产v在线观看 | 亚洲成人黄色在线观看 | 国产一区二区久久久 | www.午夜视频 | 中文字幕在线观看日本 | 日韩在线无 | 国产成人精品一区二区三区在线观看 | 97电影网站| 欧美视频日韩 | 日韩在线色 | 久久久69| 美女视频一区 | 国产精品丝袜久久久久久久不卡 | 四虎永久免费网站 | 免费三级黄色片 | 天天射日| 精品夜夜嗨av一区二区三区 | 亚洲精品视频在 | 中文字幕色综合网 | 国产精品久久久久久久久久 | 美女久久久久久久久久久 | 天堂av在线网 | 婷婷伊人五月 | 国产一区 在线播放 | 日韩精品久久一区二区 | 欧美a性| 国产精品国产三级国产aⅴ9色 | 中文字幕亚洲综合久久五月天色无吗'' | 国产精品久久久区三区天天噜 | 在线亚洲人成电影网站色www | 国产最新视频在线观看 | 日韩免费电影一区二区 | 青春草视频在线播放 | 99视频在线 | 丰满少妇在线观看网站 | 国产精品免费久久 | 亚洲三级国产 | 免费亚洲精品视频 | 人人插人人射 | 欧美激情综合色综合啪啪五月 | 99精品偷拍视频一区二区三区 | 在线观看视频在线观看 | 亚洲最新av网址 | 午夜视频在线观看一区 | 人人爽人人舔 | 四虎在线观看网址 | 999久久| 久久无码av一区二区三区电影网 | 日韩免费在线视频观看 | 免费av影视 | 国产99re | 久草色在线观看 | 国产中出在线观看 | 中文字幕在线观看完整版 | 97偷拍视频| 中文字幕在线看视频国产中文版 | 综合久久影院 | 91一区二区三区在线观看 | 四虎影视国产精品免费久久 | 国产91免费观看 | 精品久久网 | 国产在线一区二区 | 精品免费久久久久久 | 欧美在线视频免费 | 色婷婷狠狠18 | 人人干狠狠操 | 色综合五月天 | 精品一区二区三区久久久 | 久草在线免费在线观看 | 96看片| 日韩在线一二三区 | 亚洲区另类春色综合小说校园片 | 日韩免费播放 | va视频在线 | 日韩女同一区二区三区在线观看 | 久久五月婷婷丁香社区 | 久久99精品久久久久久 | 精品亚洲男同gayvideo网站 | 99re视频在线观看 | 亚洲高清国产视频 | 最近日本字幕mv免费观看在线 | 激情丁香综合五月 | 久草在线一免费新视频 | 天天操天天射天天操 | 久久国产精品免费视频 | 日本在线观看一区二区 | 亚洲九九| 999视频在线观看 | 六月婷婷色 | 最近中文字幕mv免费高清在线 | 日韩免费在线观看视频 | 欧美性生活久久 | 97精品国产一二三产区 | 九九热免费视频在线观看 | 天天摸日日摸人人看 | 国产成人久久久77777 | 欧美大片大全 | 色综合久久88色综合天天人守婷 | 99视频国产精品免费观看 | 午夜影视一区 | 国产精品久久伊人 | 国产无套精品久久久久久 | 亚洲草视频 | 日韩欧美在线一区二区 | 奇米7777狠狠狠琪琪视频 | 欧美另类69 | 一区二区三区高清在线观看 | av免费在线播放 | 97超碰免费在线观看 | 一级黄色片在线播放 | 久久综合色婷婷 | 精品久久久免费 | 国产夫妻性生活自拍 | 久久情侣偷拍 | 亚洲成a人片在线观看网站口工 | 18pao国产成视频永久免费 | 丁香亚洲| 国产大陆亚洲精品国产 | 日韩极品在线 | 国产精品18久久久 | 一区二区三区在线观看免费 | 国产黄影院色大全免费 | 91在线看免费 | 18性欧美xxxⅹ性满足 | 亚洲国产三级 | 国产又粗又猛又色又黄视频 | 久久手机精品视频 | 国产不卡精品 | 精品一区二区久久久久久久网站 | 久久丁香网 | 青春草视频 | 精品视频成人 | 天天干中文字幕 | 午夜精品视频在线 | 丝袜美女视频网站 | 日韩精品一区二区在线观看视频 | 欧美最猛性xxxxx免费 | 成人av片免费看 | 免费热情视频 | 日韩高清av | 国产女做a爱免费视频 | 国产精品久久人 | 在线观看国产亚洲 | 手机成人av在线 | 亚洲欧美视频在线观看 | 最近免费中文字幕大全高清10 | 中文字幕一区二区在线播放 | 精品一区精品二区高清 | 视频一区二区免费 | 91九色视频在线 | 国产资源在线播放 | 国产精品久久久久影院 | 在线观看日韩免费视频 | 亚洲免费在线视频 | 欧美a级一区二区 | 午夜国产一区二区三区四区 | 天天操狠狠干 | 久久免费福利 | 高清色免费| 国内视频在线观看 | 亚洲综合爱 | 久久精品免费电影 | 国产高清综合 | 亚洲精品成人av在线 | 天天天操天天天干 | 在线观看网站黄 | 国产精品视频最多的网站 | 亚洲精品网站在线 | 91av视屏| 色天天综合久久久久综合片 | 久久伊人操 | 亚洲色图美腿丝袜 | 亚洲精品免费在线观看视频 | 天天干,天天射,天天操,天天摸 | 人人看97 | 中日韩在线 | 国产一线二线三线性视频 | 日韩欧美国产成人 | 91你懂的 | 成人国产网站 | 国产精品一区二区三区免费看 | 狠狠网 | 在线久久 | 国产精品区一区 | av不卡在线看 | 国产精品久久久777 成人手机在线视频 | 国产一二区视频 | 免费观看性生活大片3 | 黄色片网站av | 青草视频在线免费 | 五月婷婷一区二区三区 | 一区二区三区国产精品 | 在线有码中文字幕 | a电影在线观看 | 婷婷草| 国产在线视频导航 | av一级久久 | 99亚洲精品在线 | 免费观看国产精品视频 | 日本夜夜草视频网站 | 精品国模一区二区三区 | 精品v亚洲v欧美v高清v | 9999在线 | 中文字幕91 | 国产精品久久久久久久久久久久 | 91在线porny国产在线看 | 久久官网 | 在线观看视频三级 | 亚洲免费不卡 | 精品产品国产在线不卡 | 美女久久久久久久久久 | 四虎影视成人精品 | 五月婷婷黄色 | 成人影片在线免费观看 | 麻豆影视在线观看 | 国产色婷婷 | 在线日本v二区不卡 | 色婷婷国产精品一区在线观看 | 91麻豆精品国产91久久久无限制版 | 婷婷日韩| www.天天干 | 探花视频在线观看 | 九草在线观看 | 国产精品麻豆果冻传媒在线播放 | 国产精品黄色 | 岛国一区在线 | 99免费视频 | 国产精品久久中文字幕 | www色婷婷com | 麻豆视频一区 | 国内成人精品2018免费看 | 色天堂在线视频 | 亚洲精品在线观看av | 久久激五月天综合精品 | av网站手机在线观看 | 99草在线视频 | 不卡av在线 | 国产精品久久久999 国产91九色视频 | 日日夜夜精品 | 成人av片免费看 | 在线观看免费av网站 | 天天干天天插伊人网 | 在线国产一区 | 毛片网站免费 | 久久综合狠狠综合久久狠狠色综合 | 久久综合五月天婷婷伊人 | 92国产精品久久久久首页 | 欧美ⅹxxxxxx | 丁香婷婷网| 91在线91 | 在线看v片成人 | 欧美一区二区免费在线观看 | 免费观看一区二区三区视频 | 国产亚洲精品免费 | 亚洲天堂色婷婷 | 三级黄色大片在线观看 | 国产资源在线播放 | 亚洲aⅴ乱码精品成人区 | 天天干天天干天天射 | 免费av 在线 | 欧美日韩视频免费看 | www.亚洲黄色 | 成人一区在线观看 | 欧美日韩国产在线精品 | 国产精品视频观看 | 久久精彩 | 天天狠狠干 | 极品美女被弄高潮视频网站 | 久久电影中文字幕视频 | 天天在线视频色 | 日韩欧美视频免费看 | 成人av一区二区在线观看 | 久久69av| 日本深夜福利视频 | 香蕉网站在线观看 | 国产视频欧美视频 | 永久免费精品视频网站 | 在线看黄色的网站 | 白丝av免费观看 | av网站免费看 | 香蕉视频在线网站 | 精品国产三级a∨在线欧美 免费一级片在线观看 | 久久r精品 | 国产99久久精品 | 久久99国产精品自在自在app | 日本一区二区免费在线观看 | www.com久久 | 免费视频97 | 99视频网站| 成人免费av电影 | 一级欧美一级日韩 | www蜜桃视频 | 91大神精品视频在线观看 | 久久午夜色播影院免费高清 | 一区二区三区在线播放 | 久久这里只有精品视频99 | 欧美日韩性视频在线 | 国产精品免费观看网站 | 国产精品手机视频 | 在线观看av网 | 亚洲蜜桃在线 | 日韩在线视频播放 | 婷婷丁香激情网 | 最新久久久 | 五月婷婷操| 亚洲国产精品第一区二区 | av中文字幕不卡 | 日日干日日操 | 91精品国自产在线观看 | 久久免费视频在线观看30 | 亚洲精区二区三区四区麻豆 | 亚洲精品美女久久久久 | 亚洲人人射 | 99热播精品| 99久久精品国产一区二区成人 | 中文字幕4 | 国产在线a免费观看 | 九九免费在线观看视频 | 久久久99国产精品免费 | 人人澡人人模 | 97香蕉超级碰碰久久免费软件 | 久久97精品 | 久久久在线视频 | 一级免费观看 | 国产91对白在线 | 精品国产伦一区二区三区免费 | 成人在线观看影院 | 亚洲欧美在线观看视频 | 欧美色图30p| 久久黄色免费视频 | 手机在线观看国产精品 | 福利视频入口 | 99久久久国产精品美女 | 缴情综合网五月天 | 激情影院在线 | 国产无吗一区二区三区在线欢 | 日日爱影视 | 免费高清在线观看成人 | 国产亚州av| 97成人资源| 麻豆传媒精品 | 日韩精品一区二区三区免费视频观看 | 久久国产精品一区二区三区四区 | 久草在线综合网 | 亚洲狠狠丁香婷婷综合久久久 | 黄色录像av | 国产精品欧美久久久久三级 | 91久久国产自产拍夜夜嗨 | 久久看看| 国产精品久久99精品毛片三a | 91一区啪爱嗯打偷拍欧美 | 国产亚洲精品电影 | 亚洲欧美日本A∨在线观看 青青河边草观看完整版高清 | 久久国内视频 | 黄色亚洲大片免费在线观看 | 亚洲视频在线观看 | 欧美精品亚洲精品日韩精品 | 国产精品美女在线 | 欧美国产精品一区二区 | 丝袜美女在线 | 在线视频麻豆 | 久草在线视频网站 | 精品色999| 天天综合网久久综合网 | 亚洲aⅴ乱码精品成人区 | 久久免费激情视频 | 91亚洲影院 | 一区二区三区在线免费观看 | 久久久999免费视频 日韩网站在线 | 欧美成人久久 | 成人aaa毛片 | 久久国产精品久久精品 | 欧美日本三级 | av丝袜天堂 | 成人午夜电影在线观看 | 三级黄色大片在线观看 | 青青草华人在线视频 | 亚洲成人网在线 | 99爱国产精品| 天天插日日插 | 日韩欧美xx | 97精品免费视频 | 日韩最新av在线 | 超级碰碰免费视频 | 综合激情伊人 | 欧美二区在线播放 | 亚洲欧美视频在线播放 | 91亚洲精品在线 | 久久亚洲免费视频 | 亚洲精品在线观 | 亚洲国产精品999 | 国产高清免费在线播放 | 美女网站免费福利视频 | 日韩www在线 | 亚洲砖区区免费 | 一区二区精品在线观看 | 日韩在线高清 | 日韩一区二区三区免费电影 | 99久久日韩精品免费热麻豆美女 | 福利网址在线观看 | 国产视频一区二区三区在线 | 在线看成人 | 久草视频中文 | 日韩乱理| 亚洲人成影院在线 | 欧美日韩1区2区 | 成人在线免费观看视视频 | 狠狠狠狠狠狠狠狠 | 日韩成人精品在线观看 | 涩涩网站在线播放 | 成人av影院在线观看 | 永久免费精品视频 | 在线观看爱爱视频 | 欧美日本一区 | 天天干天天想 | 久久综合偷偷噜噜噜色 | av网站在线观看播放 | 99精品久久精品一区二区 | 亚洲精品视频一 | 在线韩国电影免费观影完整版 | 国产专区精品 | 激情欧美一区二区三区 | 国产精品久久久久久久久久久免费看 | 天天性天天草 | 91高清完整版在线观看 | 国产精品久久免费看 | 精品成人免费 | 国产3p视频| 欧美日韩国产精品一区 | 成人中文字幕在线 | 中文字幕国产精品一区二区 | 日韩在线观看你懂的 | 西西大胆免费视频 | 精品久久福利 | 亚洲最大色| 黄色片免费看 | 久久不卡国产精品一区二区 | 波多野结衣网址 | 人人网人人爽 | 超碰人人91| 日韩在线 一区二区 | 在线观看免费av网 | 岛国av在线不卡 | 韩国av免费在线 | 99高清视频有精品视频 | 黄色小视频在线观看免费 | 久久久久久久久久影视 | 人人干人人上 | 久久超级碰视频 | 99视频国产精品免费观看 | 在线观看黄网站 | 国产成人精品免高潮在线观看 | 一区二区不卡高清 | 国产特级毛片aaaaaa高清 | 日本中文在线 | 久久视频在线视频 | 玖玖视频免费在线 | 亚洲区视频在线观看 | 7777精品伊人久久久大香线蕉 | 五月天亚洲精品 | 久久黄色网页 | 麻豆成人在线观看 | 亚洲春色奇米影视 | 7799av| 99精品视频精品精品视频 | 午夜男人影院 | 国产一二三在线视频 | 97超碰人人爱 | 中文字幕二区 | 久久毛片网站 | 日韩精品一区电影 | 免费99精品国产自在在线 | 最近字幕在线观看第一季 | 日韩欧美视频一区二区三区 | 激情丁香在线 | 最新av在线网址 | 一区二区精品视频 | 亚洲日本色| 免费在线观看av网址 | 91大神dom调教在线观看 | 国产黄色成人av | 日韩精品一区二区三区在线视频 | 在线观看日韩av | 狠狠色伊人亚洲综合网站色 | 国产在线观 | 91av在线看 | 97超碰人人澡 | 亚洲国产成人av网 | a'aaa级片在线观看 | 日韩在线观看第一页 | 亚洲高清不卡av | 天堂av网站| 一级黄色av | 日韩剧 | 一区在线观看 | 手机成人av在线 | 深爱激情站 | 国产在线a免费观看 | 国产精品大全 | 97福利在线| 欧美性大战久久久久 | 四虎在线观看视频 | 中文字幕精品一区二区精品 | 黄色在线视频网址 | 一区二区视频在线免费观看 | 国产精品va在线观看入 | 亚洲经典视频在线观看 | 曰韩在线 | 一区二区中文字幕在线播放 | 精品视频久久久 | 国产视频一级 | 在线视频一区二区 | 久久国产精品一区二区 | 97av精品 | 在线免费色视频 | 国产精品一区二区三区久久 | 色香网 | 天天射天天射 | 一区二区精品在线 | 丁香婷婷激情 | 国产精品久久久久免费 | 特级西西人体444是什么意思 | 少妇精品久久久一区二区免费 | 这里只有精彩视频 | 国产99中文字幕 | 亚洲视频六区 | 深夜免费小视频 | 国产在线观看免 | 久久精品亚洲国产 | 国产美女精品久久久 | 日韩一区视频在线 | 免费观看av | 国产成人黄色在线 | 在线观看国产日韩欧美 | 精品中文字幕在线播放 | 欧洲色吧 | 91麻豆文化传媒在线观看 | 日韩中文字幕免费视频 | 99视频精品在线 | 91成人精品一区在线播放69 | 日韩免费电影在线观看 | 又黄又刺激 | 91人人网 | 在线观看亚洲电影 | 亚洲最大av网 | 婷婷丁香激情 | 欧美少妇bbwhd | 97超碰总站| 欧美成人一区二区 | 免费观看十分钟 | 国产精品18久久久久久vr | 99re视频在线观看 | 日日干夜夜骑 | 国产视频在线观看一区二区 | 色a资源在线 | 日韩三级不卡 | 亚洲三级黄色 | 久久久久北条麻妃免费看 | 亚洲欧洲av在线 | 久久视频精品在线观看 | 午夜久久久久 | 操操操com| 91精品入口 | 超碰97在线资源站 | 91九色蝌蚪视频在线 | 精品国产a| 日韩专区在线观看 | 国产免费观看久久 | 国产精品网红直播 | 亚洲综合丁香 | 97精品一区| 成人全视频免费观看在线看 | 国产精品在线看 | 一区二区三区高清不卡 | 午夜在线观看 | 激情视频免费在线 | 国产精品国产三级国产 | av网址aaa| 国产精品女人网站 | 激情欧美国产 | av在线免费在线观看 | 中文av网 | 97超碰在线人人 | 综合久久网 | 狠狠色丁香婷婷综合久久片 | 国产精品毛片一区视频播 | 看av免费网站 | 尤物一区二区三区 | 国产精品久久久久久久久搜平片 | 日日操夜| 深夜福利视频一区二区 | 天天综合天天做 | 国产精品第10页 | 国产精品免费久久久久久久久久中文 | 国内精品久久天天躁人人爽 | 国产精品自产拍在线观看蜜 | 91av综合| 天天翘av| 日韩精品中文字幕在线 | 久久99热国产 | 日韩欧美在线高清 | 美女视频免费一区二区 | 国产视| 欧美成人猛片 | 日本韩国在线不卡 | 99在线热播 | 国产资源在线观看 | 麻豆91网站 | 国产色视频网站2 | 天天综合网国产 | 黄色毛片在线 | 狠狠综合久久 | 在线电影 一区 | 久久久久久网站 | 精品天堂av | 亚洲国产免费看 | 一级黄色免费网站 | 色婷婷综合久久久中文字幕 | 精品久久一区二区三区 | 免费看的黄色网 | 午夜国产影院 | 免费视频a | 二区三区中文字幕 | 美女福利视频 | 国产成人一区二区三区久久精品 | 99热精品免费观看 | 韩国一区二区三区在线观看 | 成年人免费在线播放 | 日日操夜夜操狠狠操 | 91中文字幕 | 国产生活一级片 | 国产精品伦一区二区三区视频 | 亚洲成人免费在线 | 超碰在线98| 日本美女xx | 久热久草在线 | 免费国产亚洲视频 | 精品国产乱码久久久久久1区二区 | 国产不卡av在线 | 99精品国产视频 | 亚洲黄色一级视频 | 中文国产字幕 | 亚洲欧美视频在线观看 | 看片一区二区三区 | 综合成人在线 | 亚洲精品久久激情国产片 | 狠狠干狠狠久久 | 国产一级一片免费播放放a 一区二区三区国产欧美 | 草莓视频在线观看免费观看 | 青青草国产成人99久久 | 天天操网 | 日日夜夜天天久久 | 亚洲va在线va天堂 | 成人在线视频网 | 黄色成人在线 | 中文字幕乱偷在线 | av黄色免费在线观看 | 色婷婷综合久久久 | 国产无吗一区二区三区在线欢 | 国产视频在线观看一区 | 国产精品久久久久aaaa | 久久久精品网 | 成人h视频 | 操少妇视频 | 黄网站污 | 国内精品久久久久久久久 | 中文字幕五区 | 深爱婷婷| 99精品视频在线播放观看 | 亚洲精品乱码白浆高清久久久久久 | 麻豆国产视频下载 | 在线视频亚洲 | 99久久精品费精品 | 人人舔人人爽 | 青春草视频在线播放 | 人人狠狠综合久久亚洲 | 久久精品99久久 | 国产一区91 | 正在播放一区 | 国产成人一区二区三区在线观看 | 夜夜夜夜夜夜操 | 99久久精品久久久久久动态片 | 天堂在线成人 | 日韩欧美在线一区二区 | 成人免费视频免费观看 | 四虎成人免费观看 | 精品国产一区二区三区男人吃奶 | 久久久www成人免费毛片 | 免费观看久久久 | 国产99久久久国产精品免费二区 | 在线你懂 | 欧美日韩国产一区 | 午夜视频一区二区三区 | 久久午夜精品影院一区 | 国产高清一| 成人免费观看av | 久久在线精品 | 蜜臀av.com | 91av欧美| 日韩在线不卡视频 | 黄色aa久久 | 开心激情综合网 | 97在线精品视频 | 日韩一区二区免费视频 | 最近高清中文在线字幕在线观看 | 人人干人人搞 | 亚洲国产精品激情在线观看 | 9久久精品| 天天躁日日 | 免费大片黄在线 | 五月天久久婷婷 |