वेक्टरिंग कोड के दौरान कैश की संख्या में वृद्धि हुई है, मैंने एसएसई 4.2 और एवीएक्स 2 के साथ 2 वैक्टरों के बीच डॉट उत्पाद को सदिशित किया है, जैसा कि आप नीचे देख सकते हैं। कोड को ओओसी अनुकूलन ध्वज के साथ जीसीसी 4.8.4 के साथ संकलित किया गया था। जैसा कि उम्मीद है कि प्रदर्शन दोनों (और एसईएस 4.2 की तुलना में एवीएक्स 2 तेज) के साथ बेहतर हो गया था, लेकिन जब मैंने पीएपीआई के साथ कोड का प्रोफाइल किया, तो मुझे पता चला कि मिस की कुल संख्या (मुख्य रूप से एल 1 और एल 2) बहुत बढ़ी है:कोड
vectorization के बिना:
PAPI_L1_TCM: 784,112,091
PAPI_L2_TCM: 195,315,365
PAPI_L3_TCM: 79,362
SSE 4.2 के साथ
:
PAPI_L1_TCM: 1,024,234,171
PAPI_L2_TCM: 311,541,918
PAPI_L3_TCM: 68,842
avx 2 के साथ
:
PAPI_L1_TCM: 2,719,959,741
PAPI_L2_TCM: 1,459,375,105
PAPI_L3_TCM: 108,140
क्या मेरे कोड में कुछ गड़बड़ हो सकती है या इस प्रकार का व्यवहार सामान्य है?
avx 2 कोड:
double vec_dotProduct(const vec& vecs, const unsigned int& start_a, const unsigned int& start_b, const int& n) {
double dot = 0;
register int i = 0;
const int loopBound = n-3;
__m256d vsum, vecPi, vecCi, vecQCi;
vsum = _mm256_set1_pd(0);
double * const pA = vecs.x+start_a ;
double * const pB = vecs.x+start_b ;
for(; i<loopBound ;i+=4){
vecPi = _mm256_loadu_pd(&(pA)[i]);
vecCi = _mm256_loadu_pd(&(pB)[i]);
vecQCi = _mm256_mul_pd(vecPi,vecCi);
vsum = _mm256_add_pd(vsum,vecQCi);
}
vsum = _mm256_hadd_pd(vsum, vsum);
dot = ((double*)&vsum)[0] + ((double*)&vsum)[2];
for(; i<n; i++)
dot += pA[i] * pB[i];
return dot;
}
SSE 4.2 कोड:
double vec_dotProduct(const vec& vecs, const unsigned int& start_a, const unsigned int& start_b, const int& n) {
double dot = 0;
register int i = 0;
const int loopBound = n-1;
__m128d vsum, vecPi, vecCi, vecQCi;
vsum = _mm_set1_pd(0);
double * const pA = vecs.x+start_a ;
double * const pB = vecs.x+start_b ;
for(; i<loopBound ;i+=2){
vecPi = _mm_load_pd(&(pA)[i]);
vecCi = _mm_load_pd(&(pB)[i]);
vecQCi = _mm_mul_pd(vecPi,vecCi);
vsum = _mm_add_pd(vsum,vecQCi);
}
vsum = _mm_hadd_pd(vsum, vsum);
_mm_storeh_pd(&dot, vsum);
for(; i<n; i++)
dot += pA[i] * pB[i];
return dot;
}
गैर vectorized कोड:
double dotProduct(const vec& vecs, const unsigned int& start_a, const unsigned int& start_b, const int& n) {
double dot = 0;
register int i = 0;
for (i = 0; i < n; ++i)
{
dot += vecs.x[start_a+i] * vecs.x[start_b+i];
}
return dot;
}
संपादित करें: गैर vectorized कोड की सभा:
0x000000000040f9e0 <+0>: mov (%rcx),%r8d
0x000000000040f9e3 <+3>: test %r8d,%r8d
0x000000000040f9e6 <+6>: jle 0x40fa1d <dotProduct(vec const&, unsigned int const&, unsigned int const&, int const&)+61>
0x000000000040f9e8 <+8>: mov (%rsi),%eax
0x000000000040f9ea <+10>: mov (%rdi),%rcx
0x000000000040f9ed <+13>: mov (%rdx),%edi
0x000000000040f9ef <+15>: vxorpd %xmm0,%xmm0,%xmm0
0x000000000040f9f3 <+19>: add %eax,%r8d
0x000000000040f9f6 <+22>: sub %eax,%edi
0x000000000040f9f8 <+24>: nopl 0x0(%rax,%rax,1)
0x000000000040fa00 <+32>: mov %eax,%esi
0x000000000040fa02 <+34>: lea (%rdi,%rax,1),%edx
0x000000000040fa05 <+37>: add $0x1,%eax
0x000000000040fa08 <+40>: vmovsd (%rcx,%rsi,8),%xmm1
0x000000000040fa0d <+45>: cmp %r8d,%eax
0x000000000040fa10 <+48>: vmulsd (%rcx,%rdx,8),%xmm1,%xmm1
0x000000000040fa15 <+53>: vaddsd %xmm1,%xmm0,%xmm0
0x000000000040fa19 <+57>: jne 0x40fa00 <dotProduct(vec const&, unsigned int const&, unsigned int const&, int const&)+32>
0x000000000040fa1b <+59>: repz retq
0x000000000040fa1d <+61>: vxorpd %xmm0,%xmm0,%xmm0
0x000000000040fa21 <+65>: retq
संपादित 2: नीचे आप बड़े एन के लिए वेक्टरकृत और गैर-वेक्टरीकृत कोड के बीच एल 1 कैश मिस की तुलना पा सकते हैं (एन-लेबल पर एक्स-लेबल और एल 1 कैश मिस पर एन)। असल में, बड़े एन के लिए गैर-वेक्टरकृत संस्करण की तुलना में वेक्टरकृत संस्करण में अभी भी अधिक यादें हैं।
आप विधानसभा को देखा है कि आपके संकलक उत्पन्न (जो संकलक आप उपयोग कर रहे, वैसे?) शायद संकलक भी अपने कोड vectorized है, लेकिन एक बेहतर काम किया है? – Rostislav
@ रोस्टिस्लाव मैं जीएनयू जीसीसी 4.8.4 का उपयोग कर रहा हूं। मैं उल्लेख करना भूल गया लेकिन प्रदर्शन वास्तव में बेहतर था, भले ही मिस की संख्या अधिक थी (मैं इसे पहले पोस्ट में जोड़ दूंगा)। – fc67
हमें पहले (गैर-वेक्टरीकृत) मामले के लिए जेनरेट कोड देखने की ज़रूरत होगी। –