मैं एक स्पैर मैट्रिक्स - वेक्टर गुणा को तेज करने के लिए नए AVX2 गेटर निर्देशों का लाभ उठाने का प्रयास कर रहा हूं। मैट्रिक्स एक पंक्ति सूचक के साथ सीएसआर (या येल) प्रारूप में है जो कॉलम इंडेक्स सरणी को इंगित करता है जो बदले में कॉलम रखता है। इस तरह के एक चटाई-vec mul के लिए सी कोड इस तरह दिखता है:AVX2 स्पैर मैट्रिक्स गुणा
for (int row = 0; row < n_rows - 1; row++) {
double rowsum = 0;
for (int col = row_ptr[row]; col < row_ptr[row + 1]; col++) {
rowsum += values[col] * x[col_indices[col]];
}
result[row] = rowsum;
}
अब मेरा लक्ष्य AVX2 intrinsics के साथ इस तेजी लाने के लिए है। निम्नलिखित कोड https://blog.fox-toolkit.org/?p=174 पर आधारित नवीनतम इंटेल या जीसीसी के साथ काम करता है। मैंने शेष को यहां हटा दिया क्योंकि मेरी पंक्तियां 4 युगल (कॉलम% 4 == 0) पर संरेखित हैं (वैसे भी भाग्यशाली)। यदि कोई दिलचस्पी लेता है तो मेरे पास कोड शेष भी है, लेकिन बिंदु यह है कि कोड वास्तव में थोड़ा धीमा है। मैंने डिस्सेप्लोर की जांच की और उपर्युक्त संस्करण के लिए केवल एफपी निर्देश उत्पन्न किए गए और मेरे AVX2 कोड के लिए सभी AVX2 ऑप्स अपेक्षित दिखाई देते हैं। कैश में फिट होने वाली छोटी मैट्रिक्स के साथ भी AVX2 संस्करण अच्छा नहीं है। मैं यहां परेशान हूं ...
double* value_base = &values[0];
double* x_base = &x[0];
int* index_base = &col_indices[0];
for (int row = 0; row < n_rows - 1; row++) {
int col_length = row_ptr[row + 1] - row_ptr[row];
__m256d rowsum = _mm256_set1_pd(0.);
for (int col4 = 0; col4 < col_length; col4 += 4) {
// Load indices for x vector(const __m128i*)
__m128i idxreg = _mm_load_si128((const __m128i*)index_base);
// Load 4 doubles from x indexed by idxreg (AVX2)
__m256d x_ = _mm256_i32gather_pd(x_base, idxreg, 8);
// Load 4 doubles linear from memory (value array)
__m256d v_ = _mm256_load_pd(value_base);
// FMA: rowsum += x_ * v_
rowsum = _mm256_fmadd_pd(x_, v_, rowsum);
index_base += 4;
value_base += 4;
}
__m256d s = _mm256_hadd_pd(rowsum, rowsum);
result[row] = ((double*)&s)[0] + ((double*)&s)[2];
// Alternative (not faster):
// Now we split the upper and lower AVX register, and do a number of horizontal adds
//__m256d hsum = _mm256_add_pd(rowsum, _mm256_permute2f128_pd(rowsum, rowsum, 0x1));
//_mm_store_sd(&result[row], _mm_hadd_pd(_mm256_castpd256_pd128(hsum), _mm256_castpd256_pd128(hsum)));
}
कोई सुझाव स्वागत है।
धन्यवाद एक बहुत, क्रिस
मूल रूप से, बेकार इकट्ठा होता है। – harold
क्या @harold ने कहा: एकत्रित भार बहुत अक्षम हैं और प्रदर्शन को मार देंगे जब तक कि आप भार की प्रारंभिक लागत को कम करने के लिए पर्याप्त गणना नहीं कर रहे हैं। आप इसके लिए स्केलर कोड के साथ ही चिपक सकते हैं। –
नकल '_mm_move_sd' +' _mm_loadh_pd' के साथ एकत्रित होता है। हैसवेल पर यह हार्डवेयर इकट्ठा करने से तेज है। –