2013-04-23 6 views
8

पर फास्ट पॉपकाउंट मैं इंटेल ज़ीऑन® फी® पर अल्ट्रा फास्ट पॉपकाउंट लागू कर रहा हूं, क्योंकि यह विभिन्न जैव सूचना विज्ञान सॉफ्टवेयर का प्रदर्शन हॉटस्पॉट है।इंटेल ज़ीऑन फाई

मैं कोड के पांच टुकड़े को क्रियान्वित किया है,

#if defined(__MIC__) 
#include <zmmintrin.h> 
__attribute__((align(64))) static const uint32_t POPCOUNT_4bit[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; 
__attribute__((align(64))) static const uint32_t MASK_4bit[16] = {0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF, 0xF}; 
inline uint64_t vpu_popcount1(uint64_t* buf, size_t n) { 
    register size_t result = 0; 
    size_t i; 
    register const __m512i popcnt = _mm512_load_epi32((void*)POPCOUNT_4bit); 
    register const __m512i mask = _mm512_load_epi32((void*)MASK_4bit); 
    register __m512i total; 
    register __m512i shuf; 

#pragma unroll(8) 
    for (i = 0; i < n; i+=8) { 
     shuf = _mm512_load_epi32(&buf[i]); 
     _mm_prefetch((const char *)&buf[i+256], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+64], _MM_HINT_T0); // vprefetch0 
     total = _mm512_setzero_epi32(); 

     total = _mm512_add_epi32(_mm512_permutevar_epi32(_mm512_and_epi32(shuf, mask), popcnt), total); 
     total = _mm512_add_epi32(_mm512_permutevar_epi32(_mm512_and_epi32(_mm512_srli_epi32(shuf, 4), mask), popcnt), total); 
     total = _mm512_add_epi32(_mm512_permutevar_epi32(_mm512_and_epi32(_mm512_srli_epi32(shuf, 8), mask), popcnt), total); 
     total = _mm512_add_epi32(_mm512_permutevar_epi32(_mm512_and_epi32(_mm512_srli_epi32(shuf, 12), mask), popcnt), total); 
     total = _mm512_add_epi32(_mm512_permutevar_epi32(_mm512_and_epi32(_mm512_srli_epi32(shuf, 16), mask), popcnt), total); 
     total = _mm512_add_epi32(_mm512_permutevar_epi32(_mm512_and_epi32(_mm512_srli_epi32(shuf, 20), mask), popcnt), total); 
     total = _mm512_add_epi32(_mm512_permutevar_epi32(_mm512_and_epi32(_mm512_srli_epi32(shuf, 24), mask), popcnt), total); 
     total = _mm512_add_epi32(_mm512_permutevar_epi32(_mm512_and_epi32(_mm512_srli_epi32(shuf, 28), mask), popcnt), total); 

     /* Reduce add, which is analogous to SSSE3's PSADBW instruction, 
      is not implementated as a single instruction in VPUv1, thus 
      emulated by multiple instructions*/ 
     result += _mm512_reduce_add_epi32(total); 
    } 

    return result; 
} 

__attribute__((align(64))) static const unsigned magic[] = {\ 
     0x55555555, 0x55555555, 0x55555555, 0x55555555,\ 
     0x55555555, 0x55555555, 0x55555555, 0x55555555,\ 
     0x55555555, 0x55555555, 0x55555555, 0x55555555,\ 
     0x55555555, 0x55555555, 0x55555555, 0x55555555,\ 
     0x33333333, 0x33333333, 0x33333333, 0x33333333,\ 
     0x33333333, 0x33333333, 0x33333333, 0x33333333,\ 
     0x33333333, 0x33333333, 0x33333333, 0x33333333,\ 
     0x33333333, 0x33333333, 0x33333333, 0x33333333,\ 
     0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,\ 
     0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,\ 
     0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,\ 
     0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,\ 
     0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF,\ 
     0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF,\ 
     0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF,\ 
     0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF,\ 
     0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF,\ 
     0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF,\ 
     0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF,\ 
     0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF,\ 
      0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF,\ 
      0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF,\ 
      0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF,\ 
      0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF 
    }; 

inline uint64_t vpu_popcount2(uint64_t* buf, size_t n) { 
    register size_t result = 0; 
    size_t i; 

    register const __m512i B0 = _mm512_load_epi32((void*)(magic+0)); 
    register const __m512i B1 = _mm512_load_epi32((void*)(magic+16)); 
    register const __m512i B2 = _mm512_load_epi32((void*)(magic+32)); 
    register const __m512i B3 = _mm512_load_epi32((void*)(magic+48)); 
    register const __m512i B4 = _mm512_load_epi32((void*)(magic+64)); 
    register __m512i total; 
    register __m512i shuf; 

#pragma unroll(8) 
    for (i = 0; i < n; i+=8) { 
     shuf = _mm512_load_epi32(&buf[i]); 
     _mm_prefetch((const char *)&buf[i+512], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+64], _MM_HINT_T0); // vprefetch0 
     total = _mm512_sub_epi32(shuf, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf,1))); 
     total = _mm512_add_epi32(_mm512_and_epi32(B1, total), _mm512_and_epi32(B1,_mm512_srli_epi32(total,2))); 
     total = _mm512_and_epi32(B2, _mm512_add_epi32(total, _mm512_srli_epi32(total,4))); 
     total = _mm512_and_epi32(B3, _mm512_add_epi32(total, _mm512_srli_epi32(total,8))); 
     total = _mm512_and_epi32(B4, _mm512_add_epi32(total, _mm512_srli_epi32(total,16))); 

     /* Reduce add, which is analogous to SSSE3's PSADBW instruction, 
      is not implementated as a single instruction in VPUv1, thus 
      emulated by multiple instructions*/ 
     result += _mm512_reduce_add_epi32(total); 
    } 

    return result; 
} 

inline uint64_t vpu_popcount3(uint64_t* buf, size_t n) { 
    register size_t result = 0; 
    size_t i; 

    register const __m512i B0 = _mm512_load_epi32((void*)(magic+0)); 
    register const __m512i B1 = _mm512_load_epi32((void*)(magic+16)); 
    register const __m512i B2 = _mm512_load_epi32((void*)(magic+32)); 
    register const __m512i B3 = _mm512_load_epi32((void*)(magic+48)); 
    register const __m512i B4 = _mm512_load_epi32((void*)(magic+64)); 
    register __m512i total; 
    register __m512i shuf; 

#pragma unroll(4) 
    for (i = 0; i < n; i+=16) { 
     shuf = _mm512_load_epi32(&buf[i]); 
     result += _mm_countbits_64(buf[i+8]); 
     _mm_prefetch((const char *)&buf[i+512], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+576], _MM_HINT_T1); // vprefetch1 
     result += _mm_countbits_64(buf[i+9]); 
     _mm_prefetch((const char *)&buf[i+64], _MM_HINT_T0); // vprefetch0 
     _mm_prefetch((const char *)&buf[i+128], _MM_HINT_T0); // vprefetch0 
     total = _mm512_sub_epi32(shuf, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf,1))); 
     result += _mm_countbits_64(buf[i+10]); 
     total = _mm512_add_epi32(_mm512_and_epi32(B1, total), _mm512_and_epi32(B1,_mm512_srli_epi32(total,2))); 
     result += _mm_countbits_64(buf[i+11]); 
     total = _mm512_and_epi32(B2, _mm512_add_epi32(total, _mm512_srli_epi32(total,4))); 
     result += _mm_countbits_64(buf[i+12]); 
     total = _mm512_and_epi32(B3, _mm512_add_epi32(total, _mm512_srli_epi32(total,8))); 
     result += _mm_countbits_64(buf[i+13]); 
     total = _mm512_and_epi32(B4, _mm512_add_epi32(total, _mm512_srli_epi32(total,16))); 
     result += _mm_countbits_64(buf[i+14]); 

     /* Reduce add, which is analogous to SSSE3's PSADBW instruction, 
      is not implementated as a single instruction in VPUv1, thus 
      emulated by multiple instructions*/ 
     result += _mm512_reduce_add_epi32(total); 
     result += _mm_countbits_64(buf[i+15]); 
    } 

    return result; 
} 

/* Using VPU or SSE's machine intrinsic, CPUs not supporting SIMD 
* will use compiler's implementation, the speed of which depends */ 
static inline size_t scalar_popcountu(unsigned *buf, size_t n) { 
    register size_t cnt = 0; 
    size_t i; 
#pragma vector always 
#pragma unroll(8) 
    for (i = 0; i < n; i++) { 
    cnt += _mm_countbits_32(buf[i]); 
    _mm_prefetch((const char *)&buf[i+512], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[i+64], _MM_HINT_T0); // vprefetch0 
    } 
    return cnt; 
} 

static inline size_t scalar_popcountlu(uint64_t *buf, size_t n) { 
    register size_t cnt = 0; 
    size_t i; 
#pragma vector always 
#pragma unroll(8) 
    for (i = 0; i < n; i++) { 
    cnt += _mm_countbits_64(buf[i]); 
    _mm_prefetch((const char *)&buf[i+512], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[i+64], _MM_HINT_T0); // vprefetch0 
    } 
    return cnt; 
} 
#endif 

OpenMP समर्थन के साथ कोड के ऊपर एक चादर से https://www.dropbox.com/sh/b3sfqps19wa2oi4/iFQ9wQ1NTg

कोड डाउनलोड किया जा सकता इंटेल C/C++ संकलक XE 13 का उपयोग कर संकलित किया गया था का उपयोग करते हुए आदेश:

icc -debug inline-debug-info -O3 -mmic -fno-alias -ansi-alias -opt-streaming-stores always -ipo popcnt-mmic.cpp -o popcnt-mmic -vec-report=2 -openmp 

कोड के साथ "122 धागे" और "संतुलित" निर्यात का उपयोग कर धागा आत्मीयता सह-प्रोसेसर (61 कोर) पर मूल रूप से चलाता है:

export OMP_NUM_THREADS=122;export KMP_AFFINITY=balanced 

मैं जिऑन फी SE10p, बी 1 स्टेपिंग, CentOS6.4 परीक्षण का उपयोग कर रहा junks के 28 मेगाबाइट पर (रैंड द्वारा भरा()) और 10000 समय के लिए पुनरावृति, प्रदर्शन इस प्रकार हैं:

Buffer allocated at: 0x7f456b000000 
OpenMP scalar_popcountu  4310169 us; cnt = 28439328 
OpenMP scalar_popcountlu  1421139 us; cnt = 28439328 
OpenMP vpu_popcount   1489992 us; cnt = 28439328 
OpenMP vpu_popcount2   1109530 us; cnt = 28439328 
OpenMP vpu_popcount3   951122 us; cnt = 28439328 

"scalar_popcountu" और "scalar_popcountlu" क्रमशः "_mm_countbits_32" और "_mm_countbits_64" इंट्रिनिक्स का उपयोग करते हैं, जो स्केलर "पॉपकंट" निर्देश का उपयोग करते हैं। "#pragma वेक्टर हमेशा" को सेट करने के लिए संकलक को भार और योग को 16 हस्ताक्षरित इनट्स या 8 हस्ताक्षरित लंबे समय तक सदिश करने के लिए कहा जाता है, हालांकि पॉपकाउंट स्वयं भी एक स्केलर निर्देश है।

vpu_popcount1 का कार्यान्वयन एसएसएसई 3 पॉपकाउंट कार्यान्वयन http://wm.ite.pl/articles/sse-popcount.html के समान है। हालांकि, 1) ज़ीऑन फाई पूर्णांक पर पैक बाइट ऑपरेशंस का समर्थन नहीं करता है (न्यूनतम डबल शब्द, उर्फ ​​32-बिट) और 2) यह "पूर्ण अंतर के पैक किए गए योग" निर्देश को लागू नहीं करता है (जैसे एसएसएसई 3 में _mm_sad_epu8) कमी जोड़ने "vpermf32x4", "vpaddd" और "movslq" के चार समूहों के संयोजन द्वारा किया गया था। इस प्रकार कार्यान्वयन ने मूल एसएसएसई 3 संस्करण की तुलना में अधिक निर्देश दिए।

vpu_popcount2 का कार्यान्वयन एसएसई 2 पॉपकाउंट कार्यान्वयन के समान है (कोई "हैकर डिलाइट" का संदर्भ ले सकता है)। कार्यान्वयन vpu_popcount1 से कम निर्देश उत्पन्न करता है और लगभग 30% तेज़ है। हालांकि, कड़ी मेहनत "कम करें" अभी भी टाला नहीं जा सकता है।

vpu_popcount3 का कार्यान्वयन बहुत ज़ीओन फी विशिष्ट है। वेक्टर और स्केलर ऑपरेशंस के मिश्रण के साथ, यह vpu_popcount2 से लगभग 15% तेज है (वेक्टर ऑपरेशंस के बीच स्केलर ऑपरेशंस का अंतर मेरे कार्यान्वयन में अवकाश है, कोई संकलक द्वारा उत्पन्न असेंबली कोड के अनुसार स्केलर परिचालन को पुनर्व्यवस्थित कर सकता है, लेकिन अपेक्षित सुधार जहां तक ​​मेरा संबंध है, सीमित है)। सुधार अवलोकन पर आधारित है कि 1) ज़ीओन फाई इन-ऑर्डर शेड्यूलिंग है, 2) दो स्केलर निर्देश या "1 वेक्टर + 1 स्केलर" निर्देश प्रति घड़ी चक्र जारी किए जा सकते हैं। रजिस्टर फ़ाइल संतृप्ति से बचने के लिए मैंने 8 से 4 तक अनलोल घटा दिया है।

स्मृति से एल 2 8 लूप से स्पष्ट प्रीफेच अग्रिम में और प्रत्येक समारोह में एल 2 से एल 1 1 लूप से अग्रिम में एल 1 हिट अनुपात 0.38 से 0.9 9 4 तक बढ़ गया है।

अनलोल प्रदर्शन में लगभग 15% की वृद्धि करता है। यह काउंटर अंतर्ज्ञानी है क्योंकि ज़ीओन फाई इन-ऑर्डर शेड्यूलिंग है। लेकिन अनलॉक आईसीसी कंपाइलर जितना संभव हो उतना संकलित समय शेड्यूलिंग करने में सक्षम बनाता है।

क्या हमारे पास प्रदर्शन को बढ़ावा देने के लिए और भी तकनीकी हैं?ब्रायन Nickerson से तेजी से कोड

दो टुकड़ा,

OpenMP vpu_popcount2   1110737 us; cnt = 28439328 
OpenMP vpu_popcount3   951459 us; cnt = 28439328 
OpenMP vpu_popcount3_r   815126 us; cnt = 28439328 
OpenMP vpu_popcount5   746852 us; cnt = 28439328 

vpu_popcount3_revised:

inline uint64_t vpu_popcount3_revised(uint64_t* buf, size_t n) { 
    _mm_prefetch((const char *)&buf[0], _MM_HINT_T0); // vprefetch0 
    _mm_prefetch((const char *)&buf[8], _MM_HINT_T0); // vprefetch0 
    _mm_prefetch((const char *)&buf[16], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[24], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[32], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[40], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[48], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[56], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[64], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[72], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[80], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[88], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[96], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[104], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[112], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[120], _MM_HINT_T1); // vprefetch1 
    register size_t result; 
    size_t i; 

    register const __m512i B0 = _mm512_load_epi32((void*)(magic+0)); 
    register const __m512i B1 = _mm512_load_epi32((void*)(magic+16)); 
    register const __m512i B2 = _mm512_load_epi32((void*)(magic+32)); 
    register const __m512i B3 = _mm512_load_epi32((void*)(magic+48)); 
    register const __m512i B4 = _mm512_load_epi32((void*)(magic+64)); 
    register __m512i total0; 
    register __m512i total1; 
    register __m512i shuf0; 
    register __m512i shuf1; 
    register __m512i result0; 
    register __m512i result1; 

    result0 = _mm512_setzero_epi32(); 
    result1 = _mm512_setzero_epi32(); 

    for (i = 0; i < n; i+=16) { 
     shuf0 = _mm512_load_epi32(&buf[i ]); 
     shuf1 = _mm512_load_epi32(&buf[i+8]); 
     _mm_prefetch((const char *)&buf[i+128], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+136], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+16], _MM_HINT_T0); // vprefetch0 
     _mm_prefetch((const char *)&buf[i+24], _MM_HINT_T0); // vprefetch0 
     total0 = _mm512_sub_epi32(shuf0, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf0,1))); 
     total1 = _mm512_sub_epi32(shuf1, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf1,1))); 
     total0 = _mm512_add_epi32(_mm512_and_epi32(B1, total0), _mm512_and_epi32(B1,_mm512_srli_epi32(total0,2))); 
     total1 = _mm512_add_epi32(_mm512_and_epi32(B1, total1), _mm512_and_epi32(B1,_mm512_srli_epi32(total1,2))); 
     total0 = _mm512_and_epi32(B2, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,4))); 
     total1 = _mm512_and_epi32(B2, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,4))); 
     total0 = _mm512_and_epi32(B3, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,8))); 
     total1 = _mm512_and_epi32(B3, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,8))); 
     total0 = _mm512_and_epi32(B4, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,16))); 
     total1 = _mm512_and_epi32(B4, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,16))); 
     result0 = _mm512_add_epi32(result0,total0); 
     result1 = _mm512_add_epi32(result1,total1); 

    } 

    result0 = _mm512_add_epi32(result0,result1); 
    result = _mm512_reduce_add_epi32(result0); 

    return result; 
} 

vpu_popcount5:

inline uint64_t vpu_popcount5(uint64_t* buf, size_t n) { 
    _mm_prefetch((const char *)&buf[0], _MM_HINT_T0); // vprefetch0 
    _mm_prefetch((const char *)&buf[8], _MM_HINT_T0); // vprefetch0 
    _mm_prefetch((const char *)&buf[16], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[24], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[32], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[40], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[48], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[56], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[64], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[72], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[80], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[88], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[96], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[104], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[112], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[120], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[128], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[136], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[144], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[152], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[160], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[168], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[176], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[184], _MM_HINT_T1); // vprefetch1 
    register size_t result; 
    size_t i; 

    register const __m512i B0 = _mm512_load_epi32((void*)(magic+0)); 
    register const __m512i B1 = _mm512_load_epi32((void*)(magic+16)); 
    register const __m512i B2 = _mm512_load_epi32((void*)(magic+32)); 
    register const __m512i B3 = _mm512_load_epi32((void*)(magic+48)); 
    register const __m512i B4 = _mm512_load_epi32((void*)(magic+64)); 
    register const __m512i B6 = _mm512_load_epi32((void*)(magic+80)); 
    register __m512i total0; 
    register __m512i total1; 
    register __m512i total2; 
    register __m512i total3; 
    register __m512i shuf0; 
    register __m512i shuf1; 
    register __m512i shuf2; 
    register __m512i shuf3; 
    register __m512i result0; 
    register __m512i result1; 

    result0 = _mm512_setzero_epi32(); 
    result1 = _mm512_setzero_epi32(); 

    for (i = 0; i < n; i+=32) { 
      shuf0 = _mm512_load_epi32(&buf[i ]); 
      shuf1 = _mm512_load_epi32(&buf[i+ 8]); 
      shuf2 = _mm512_load_epi32(&buf[i+16]); 
      shuf3 = _mm512_load_epi32(&buf[i+24]); 
      _mm_prefetch((const char *)&buf[i+192], _MM_HINT_T1); // vprefetch1 
      _mm_prefetch((const char *)&buf[i+200], _MM_HINT_T1); // vprefetch1 
      _mm_prefetch((const char *)&buf[i+208], _MM_HINT_T1); // vprefetch1 
      _mm_prefetch((const char *)&buf[i+216], _MM_HINT_T1); // vprefetch1 
      _mm_prefetch((const char *)&buf[i+32], _MM_HINT_T0); // vprefetch0 
      _mm_prefetch((const char *)&buf[i+40], _MM_HINT_T0); // vprefetch0 
      _mm_prefetch((const char *)&buf[i+48], _MM_HINT_T0); // vprefetch0 
      _mm_prefetch((const char *)&buf[i+56], _MM_HINT_T0); // vprefetch0 
      total0 = _mm512_sub_epi32(shuf0, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf0,1)));      // max value in nn is 10 
      total1 = _mm512_sub_epi32(shuf1, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf1,1))); 
      total2 = _mm512_sub_epi32(shuf2, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf2,1))); 
      total3 = _mm512_sub_epi32(shuf3, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf3,1))); 
      total0 = _mm512_add_epi32(_mm512_and_epi32(B1, total0), _mm512_and_epi32(B1,_mm512_srli_epi32(total0,2))); // max value in nnnn is 0100 
      total1 = _mm512_add_epi32(_mm512_and_epi32(B1, total1), _mm512_and_epi32(B1,_mm512_srli_epi32(total1,2))); 
      total2 = _mm512_add_epi32(_mm512_and_epi32(B1, total2), _mm512_and_epi32(B1,_mm512_srli_epi32(total2,2))); 
      total3 = _mm512_add_epi32(_mm512_and_epi32(B1, total3), _mm512_and_epi32(B1,_mm512_srli_epi32(total3,2))); 
      total0 = _mm512_and_epi32(B2, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,4)));      // max value in 0000nnnn is 00001000 
      total1 = _mm512_and_epi32(B2, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,4))); 
      total2 = _mm512_and_epi32(B2, _mm512_add_epi32(total2, _mm512_srli_epi32(total2,4))); 
      total3 = _mm512_and_epi32(B2, _mm512_add_epi32(total3, _mm512_srli_epi32(total3,4))); 
      total0 = _mm512_add_epi32(total0, total1);                 // max value in 000nnnnn is 00010000 
      total1 = _mm512_add_epi32(total2, total3); 
      total0 = _mm512_add_epi32(total0, _mm512_srli_epi32(total0,8));           // max value in xxxxxxxx00nnnnnn is 00100000 
      total1 = _mm512_add_epi32(total1, _mm512_srli_epi32(total1,8)); 
      total0 = _mm512_and_epi32(B6, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,16)));      // max value in each element is 01000000, i.e. 64 
      total1 = _mm512_and_epi32(B6, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,16))); 
      result0 = _mm512_add_epi32(result0,total0); 
      result1 = _mm512_add_epi32(result1,total1); 
    } 

    result0 = _mm512_add_epi32(result0,result1); 
    result = _mm512_reduce_add_epi32(result0); 

    return result; 
} 
+2

मुझे लगता है कि इस सवाल का नहीं बल्कि बाद से अपने कोड पहले से ही काम करने के लिए लगता है पर [कोड समीक्षा] (http://codereview.stackexchange.com/) होना चाहिए। – Morwenn

+1

बहुत ही रोचक सवाल - मुझे आशा है कि आप समुदाय में सक्रिय रहेंगे। मैं उस तकनीक पर सोच रहा था जिसका उपयोग आपने कैश हिट रेट को मापने के लिए किया था। चीयर्स! – BlueStrat

+0

@ ब्लूस्ट्रैट, मैं कैश हिट दर प्राप्त करने के लिए इंटेल के vTune का उपयोग करता हूं। – Aquaskyline

उत्तर

1

कल पोस्टिंग के बाद से, मैं अपने खुद के कार्ड पर अपने कोड और मेरे सुझाव को चलाने के लिए सक्षम है। मुझे आपके हार्डवेयर की गति के कारण, और शायद मेरे कंपाइलर के संस्करणों से संबंधित होने के कारण, आप बिल्कुल वही समय नहीं मिलते हैं। लेकिन प्रवृत्ति बढ़ी है, और मेरा सुझाव पंद्रह प्रतिशत प्रदर्शन बढ़ाने के बारे में प्रतीत होता था।

मुझे नीचे दिए गए कोड में दिखाए गए अनुसार, थोड़ा और tweaking के साथ, पांच और दस प्रतिशत के बीच एक अतिरिक्त छोटा प्रदर्शन बढ़ावा मिला। कृपया ध्यान दें कि निम्न कोड स्निपेट में, बी 6 में प्रत्येक तत्व 0x000000FF पर सेट है। इस बिंदु पर, मुझे लगता है कि एल्गोरिदम जीडीडीआर से एल 2 कैश तक पहुंचने योग्य अधिकतम टिकाऊ बैंडविड्थ के बहुत करीब हो सकता है।

(जोड़ा गया नोट: इस दावे का एक प्रमाण यह है कि यदि मैं popcount5 फ़ंक्शन के शरीर को लूप के साथ लपेटता हूं जो इसे दस बार दोहराता है - और ध्यान दें कि यह "chunk_size" की दस तीव्र पुनरावृत्ति है इनपुट डेटा, तो नौ बार यह एल 2 में गर्म हो जाएगा - टेस्ट के लिए कुल समय दस के बजाए लगभग पांच के कारक से बढ़ता है। मैं इसे ऊपर लाता हूं क्योंकि मुझे लगता है कि आपका लक्ष्य गति को ट्यून करना है बिट गिनती तर्क का, लेकिन संभवतः जिस एप्लिकेशन में आप इसे तैनात करने की उम्मीद करते हैं, वास्तव में एक छोटा और/या गर्म कामकाजी सेट होता है। यदि ऐसा है, तो DRAM -> L2 बैंडविड्थ द्वारा प्रस्तुत थ्रॉटलिंग चित्र को धुंधला कर रहा है। लेकिन ध्यान दें कि आपके टेस्ट इनपुट के आकार को पीछे हटाना ताकि एल 2 में गर्म रहने के लिए यह अन्य ओवरहेड का कारण बनता है - शायद ओपनएम ओवरहेड - अपेक्षाकृत अधिक महत्वपूर्ण बनने के लिए।)

inline uint64_t vpu_popcount5(uint64_t* buf, size_t n) { 
    _mm_prefetch((const char *)&buf[0], _MM_HINT_T0); // vprefetch0 
    _mm_prefetch((const char *)&buf[8], _MM_HINT_T0); // vprefetch0 
    _mm_prefetch((const char *)&buf[16], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[24], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[32], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[40], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[48], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[56], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[64], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[72], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[80], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[88], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[96], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[104], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[112], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[120], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[128], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[136], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[144], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[152], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[160], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[168], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[176], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[184], _MM_HINT_T1); // vprefetch1 
    register size_t result; 
    size_t i; 

    register const __m512i B0 = _mm512_load_epi32((void*)(magic+0)); 
    register const __m512i B1 = _mm512_load_epi32((void*)(magic+16)); 
    register const __m512i B2 = _mm512_load_epi32((void*)(magic+32)); 
    register const __m512i B6 = _mm512_load_epi32((void*)(magic+80)); 
    register __m512i total0; 
    register __m512i total1; 
    register __m512i total2; 
    register __m512i total3; 
    register __m512i shuf0; 
    register __m512i shuf1; 
    register __m512i shuf2; 
    register __m512i shuf3; 
    register __m512i result0; 
    register __m512i result1; 

    result0 = _mm512_setzero_epi32(); 
    result1 = _mm512_setzero_epi32(); 

    for (i = 0; i < n; i+=32) { 
     shuf0 = _mm512_load_epi32(&buf[i ]); 
     shuf1 = _mm512_load_epi32(&buf[i+ 8]); 
     shuf2 = _mm512_load_epi32(&buf[i+16]); 
     shuf3 = _mm512_load_epi32(&buf[i+24]); 
     _mm_prefetch((const char *)&buf[i+192], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+200], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+208], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+216], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+32], _MM_HINT_T0); // vprefetch0 
     _mm_prefetch((const char *)&buf[i+40], _MM_HINT_T0); // vprefetch0 
     _mm_prefetch((const char *)&buf[i+48], _MM_HINT_T0); // vprefetch0 
     _mm_prefetch((const char *)&buf[i+56], _MM_HINT_T0); // vprefetch0 
     total0 = _mm512_sub_epi32(shuf0, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf0,1)));      // max value in nn is 10 
     total1 = _mm512_sub_epi32(shuf1, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf1,1))); 
     total2 = _mm512_sub_epi32(shuf2, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf2,1))); 
     total3 = _mm512_sub_epi32(shuf3, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf3,1))); 
     total0 = _mm512_add_epi32(_mm512_and_epi32(B1, total0), _mm512_and_epi32(B1,_mm512_srli_epi32(total0,2))); // max value in nnnn is 0100 
     total1 = _mm512_add_epi32(_mm512_and_epi32(B1, total1), _mm512_and_epi32(B1,_mm512_srli_epi32(total1,2))); 
     total2 = _mm512_add_epi32(_mm512_and_epi32(B1, total2), _mm512_and_epi32(B1,_mm512_srli_epi32(total2,2))); 
     total3 = _mm512_add_epi32(_mm512_and_epi32(B1, total3), _mm512_and_epi32(B1,_mm512_srli_epi32(total3,2))); 
     total0 = _mm512_and_epi32(B2, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,4)));      // max value in 0000nnnn is 00001000 
     total1 = _mm512_and_epi32(B2, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,4))); 
     total2 = _mm512_and_epi32(B2, _mm512_add_epi32(total2, _mm512_srli_epi32(total2,4))); 
     total3 = _mm512_and_epi32(B2, _mm512_add_epi32(total3, _mm512_srli_epi32(total3,4))); 
     total0 = _mm512_add_epi32(total0, total1);                 // max value in 000nnnnn is 00010000 
     total1 = _mm512_add_epi32(total2, total3); 
     total0 = _mm512_add_epi32(total0, _mm512_srli_epi32(total0,8));           // max value in xxxxxxxx00nnnnnn is 00100000 
     total1 = _mm512_add_epi32(total1, _mm512_srli_epi32(total1,8)); 
     total0 = _mm512_and_epi32(B6, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,16)));      // max value in each element is 01000000, i.e. 64 
     total1 = _mm512_and_epi32(B6, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,16))); 
     result0 = _mm512_add_epi32(result0,total0); 
     result1 = _mm512_add_epi32(result1,total1); 

     /* Reduce add, which is analogous to SSSE3's PSADBW instruction, 
      is not implementated as a single instruction in VPUv1, thus 
      emulated by multiple instructions*/ 
    } 

    result0 = _mm512_add_epi32(result0,result1); 
    result = _mm512_reduce_add_epi32(result0); 

    return result; 
} 
+0

कोड प्रेरणादायक है, हालांकि परिणाम गलत है। मैंने बी 6 के उपयोग को बी 3 और बी 4 में बदल दिया है, और मुझे फिर से सही परिणाम मिलते हैं। बी 6: ओपनएमपी vpu_popcount3 918206 हमें; सीएनटी = 28439328 ओपनएमपी vpu_popcount3_r 782216 हमें; सीएनटी = 28439328 ओपनएमपी vpu_popcount5 751597 हमें; सीएनटी = 3582282 बी 3, बी 4: ओपनएमपी vpu_popcount3 951459 हमें; सीएनटी = 28439328 ओपनएमपी vpu_popcount3_r 815126 हमें; सीएनटी = 28439328 ओपनएमपी vpu_popcount5 746852 हमें; सीएनटी = 28439328 – Aquaskyline

+0

मैं बी 1 स्टेपिंग, एसई 10 पी का उपयोग कर रहा हूं। – Aquaskyline

+0

मैंने दोबारा जांच की, और मुझे सही जवाब मिल गया। क्या आपने "जादू" सरणी के अंत में 0x000000FF की 16 प्रतियां जोड़ दीं? (मैंने कोड स्निपेट से पहले गद्य में इसकी ओर इशारा किया था, लेकिन शायद मुझे अधिक प्रत्यक्ष होना चाहिए था। क्षमा करें। ध्यान दें कि अन्य बीएन रजिस्टर स्थिरांक के नामों के अनुरूप होने के लिए, मुझे इसे बी 5 कहा जाना चाहिए था, लेकिन यह ' टी वास्तव में कोई फर्क नहीं पड़ता।) –

1

आप निम्नलिखित संस्करण की कोशिश कृपया, और क्या वापस रिपोर्ट यह आपके लिए प्रदर्शन में सुधार करता है?

  • मुझे नहीं लगता कि अपने प्रीफेचिंग दूरी काफी सही थे: मैं कई अंक मुझे लगता है कि अपने कोडिंग में काफी इष्टतम नहीं कर रहे हैं को संबोधित कर रहा हूँ। यह मेरी तरफ देखा जैसे कि आप बाइट ऑफ़सेट दूरी के बारे में सोच रहे थे जब इंडेक्सिंग वास्तव में uint64 के संदर्भ में थी।
  • मुझे लूप के हर पुनरावृत्ति को कम करने के लिए कोई कारण नहीं दिखता है। आप 16 सिम तत्वों में बिट गणनाओं के आंशिक संचय कर सकते हैं, और फिर लूप के बाहर एक ही कमी कर सकते हैं।
  • मुझे नहीं लगता कि स्केलर-साइड पॉपकैंट निर्देश करना फायदेमंद है क्योंकि वास्तव में वीपीयू शेड्यूलिंग का सर्वोत्तम लाभ प्राप्त होता है। एक उत्कृष्ट वीपीयू अनुसूची पर ध्यान केंद्रित करना सबसे महत्वपूर्ण है। मुझे यह भी नहीं लगता कि स्केलर पॉपकाउंट निर्देश वास्तव में एक वेक्टर ऑपरेशन के साथ जोड़े; यानी मुझे लगता है कि यह केवल यू-पाइप में समर्थित है।

inline uint64_t vpu_popcount3_revised(uint64_t* buf, size_t n) { 
    _mm_prefetch((const char *)&buf[0], _MM_HINT_T0); // vprefetch0 
    _mm_prefetch((const char *)&buf[8], _MM_HINT_T0); // vprefetch0 
    _mm_prefetch((const char *)&buf[16], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[24], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[32], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[40], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[48], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[56], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[64], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[72], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[80], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[88], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[96], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[104], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[112], _MM_HINT_T1); // vprefetch1 
    _mm_prefetch((const char *)&buf[120], _MM_HINT_T1); // vprefetch1 
    register size_t result; 
    size_t i; 

    register const __m512i B0 = _mm512_load_epi32((void*)(magic+0)); 
    register const __m512i B1 = _mm512_load_epi32((void*)(magic+16)); 
    register const __m512i B2 = _mm512_load_epi32((void*)(magic+32)); 
    register const __m512i B3 = _mm512_load_epi32((void*)(magic+48)); 
    register const __m512i B4 = _mm512_load_epi32((void*)(magic+64)); 
    register __m512i total0; 
    register __m512i total1; 
    register __m512i shuf0; 
    register __m512i shuf1; 
    register __m512i result0; 
    register __m512i result1; 

    result0 = _mm512_setzero_epi32(); 
    result1 = _mm512_setzero_epi32(); 

    for (i = 0; i < n; i+=16) { 
     shuf0 = _mm512_load_epi32(&buf[i ]); 
     shuf1 = _mm512_load_epi32(&buf[i+8]); 
     _mm_prefetch((const char *)&buf[i+128], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+136], _MM_HINT_T1); // vprefetch1 
     _mm_prefetch((const char *)&buf[i+16], _MM_HINT_T0); // vprefetch0 
     _mm_prefetch((const char *)&buf[i+24], _MM_HINT_T0); // vprefetch0 
     total0 = _mm512_sub_epi32(shuf0, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf0,1))); 
     total1 = _mm512_sub_epi32(shuf1, _mm512_and_epi32(B0, _mm512_srli_epi32(shuf1,1))); 
     total0 = _mm512_add_epi32(_mm512_and_epi32(B1, total0), _mm512_and_epi32(B1,_mm512_srli_epi32(total0,2))); 
     total1 = _mm512_add_epi32(_mm512_and_epi32(B1, total1), _mm512_and_epi32(B1,_mm512_srli_epi32(total1,2))); 
     total0 = _mm512_and_epi32(B2, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,4))); 
     total1 = _mm512_and_epi32(B2, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,4))); 
     total0 = _mm512_and_epi32(B3, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,8))); 
     total1 = _mm512_and_epi32(B3, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,8))); 
     total0 = _mm512_and_epi32(B4, _mm512_add_epi32(total0, _mm512_srli_epi32(total0,16))); 
     total1 = _mm512_and_epi32(B4, _mm512_add_epi32(total1, _mm512_srli_epi32(total1,16))); 
     result0 = _mm512_add_epi32(result0,total0); 
     result1 = _mm512_add_epi32(result1,total1); 

    } 

    /* Reduce add, which is analogous to SSSE3's PSADBW instruction, 
     is not implementated as a single instruction in VPUv1, thus 
     emulated by multiple instructions*/ 

    result0 = _mm512_add_epi32(result0,result1); 
    result = _mm512_reduce_add_epi32(result0); 

    return result; 
} 
+0

फैब, यह वास्तव में तेज़ है। – Aquaskyline

+0

'ओपनएमपी ci_popcount 3871901 हमें; सीएनटी = 28439328' 'ओपनएमपी ci_popcountll 1336664 हमें; सीएनटी = 28439328' 'ओपनएमपी vpu_popcount1 1364152 हमें; सीएनटी = 28439328' 'ओपनएमपी vpu_popcount2 1052478 हमें; सीएनटी = 28439328' 'ओपनएमपी vpu_popcount3 918206 हमें; सीएनटी = 28439328' 'ओपनएमपी vpu_popcount3_r 782216 हमें; cnt = 28439328' – Aquaskyline

संबंधित मुद्दे