Я попытался перевести следующий код в AVX встроенные функции для того, чтобы улучшить производительность:Как оптимизировать AVX код
for (int alpha = 0; alpha < 4; alpha++) {
for (int k = 0; k < 3; k++) {
for (int beta = 0; beta < 4; beta++) {
for (int l = 0; l < 4 ; l++) {
d2_phi[(alpha*3+k)*16 + beta*4+l] =
- (d2_phi[(alpha*3+k)*16 + beta*dim+l]
+ b[k] * ( lam_12[ beta][alpha] * a[l]
+ lam_22[alpha][ beta] * b[l]
+ lam_23[alpha][ beta] * rjk[l] )
+ rjk[k] * ( lam_13[ beta][alpha] * a[l]
+ lam_23[ beta][alpha] * b[l]
+ lam_33[alpha][ beta] * rjk[l] )
)/sqrt_gamma;
}
}
}
}
и попытался это следующим образом:
// load sqrt_gamma, because it is constant
__m256d ymm7 = _mm256_broadcast_sd(&sqrt_gamma);
for (int alpha=0; alpha < 4; alpha++) {
for (int k=0; k < 3; k++) {
// Load values that are only dependent on k
__m256d ymm9 = _mm256_broadcast_sd(b+k); // all b[k]
__m256d ymm8 = _mm256_broadcast_sd(rjk+k); // all rjk[k]
for (int beta=0; beta < 4; beta++) {
// Load the lambdas, because they will stay the same for nine iterations
__m256d ymm15 = _mm256_broadcast_sd(lam_12_p + 4*beta + alpha); // all lam_12[ beta][alpha]
__m256d ymm14 = _mm256_broadcast_sd(lam_22_p + 4*alpha + beta); // all lam_22[alpha][ beta]
__m256d ymm13 = _mm256_broadcast_sd(lam_23_p + 4*alpha + beta); // all lam_23[alpha][ beta]
__m256d ymm12 = _mm256_broadcast_sd(lam_13_p + 4*beta + alpha); // all lam_13[ beta][alpha]
__m256d ymm11 = _mm256_broadcast_sd(lam_23_p + 4*beta + alpha); // all lam_23[ beta][alpha]
__m256d ymm10 = _mm256_broadcast_sd(lam_33_p + 4*alpha + beta); // lam_33[alpha][ beta]
// Load the values that depend on the innermost loop, which is removed do to AVX
__m256d ymm6 =_mm256_load_pd(a); // a[i] until a[l+3]
__m256d ymm5 =_mm256_load_pd(b); // b[i] until b[l+3]
__m256d ymm4 =_mm256_load_pd(rjk); // rjk[i] until rjk[l+3]
//__m256d ymm3 =_mm256_load_pd(d2_phi_p + (alpha*3+k)*16 + beta*dim); // d2_phi[(alpha*3+k)*12 + beta*dim] until d2_phi[(alpha*3+k)*12 + beta*dim +3]
__m256d ymm3 =_mm256_load_pd(d2_phi_p + 4*s);
// Block that is later on multiplied with b[k]
__m256d ymm2 = _mm256_mul_pd(ymm15, ymm6); // lam_12[ beta][alpha] * a[l]
__m256d ymm1 = _mm256_mul_pd(ymm14, ymm5); // lam_22[alpha][ beta] * b[l];
__m256d ymm0 = _mm256_add_pd(ymm2, ymm1); // lam_12[ beta][alpha] * a[l] + lam_22[alpha][ beta]*b[l];
ymm2 = _mm256_mul_pd(ymm13, ymm4); // lam_23[alpha][ beta] * rjk[l]
ymm0 = _mm256_add_pd(ymm2, ymm0); // lam_12[ beta][alpha] * a[l] + lam_22[alpha][ beta]*b[l] + lam_23[alpha][ beta] * b[i];
ymm0 = _mm256_mul_pd(ymm9, ymm0); // b[k] * (first sum of three)
// Block that is later on multiplied with rjk[k]
ymm2 = _mm256_mul_pd(ymm12, ymm6); // lam_13[ beta][alpha] * a[l]
ymm1 = _mm256_mul_pd(ymm11, ymm5); // lam_23[ beta][alpha] * b[l]
ymm2 = _mm256_add_pd(ymm2, ymm1); // lam_13[ beta][alpha] * a[l] + lam_22[alpha][ beta]*b[l];
ymm1 = _mm256_mul_pd(ymm10, ymm4); // lam_33[alpha][ beta] * rjk[l]
ymm2 = _mm256_add_pd(ymm2, ymm1); // lam_13[ beta][alpha] * a[l] + lam_22[alpha][ beta]*b[l] + lam_33[alpha][ beta] *rjk[l]
ymm2 = _mm256_mul_pd(ymm2, ymm8); // rjk[k] * (second sum of three)
ymm0 = _mm256_add_pd(ymm0, ymm2); // add to temporal result in ymm0
ymm0 = _mm256_add_pd(ymm3, ymm0); // Old value of d2 Phi;
ymm0 = _mm256_div_pd(ymm0, ymm7); // all divided by sqrt_gamma
_mm256_store_pd(d2_phi_p + (alpha*3+k)*16 + beta*dim, ymm0);
}
}
}
Но себе свободу плохо. Он еще медленнее, чем авто-векторизованный код, созданный компилятором Intel. Я попытался следующие вещи:
- Все массивы данных 64 байт выровнены
__declspec(align(64))
- Магазин в конце был заменен на потоковом хранилище
_mm256_stream_pd
Когда я смотрю в созданную сборку код, я вижу, что автокод извлекает все параметры на каждой итерации (а не так, как я, только в петлях, к которым они принадлежат). Он также содержит больше арифметических операций. В последней точке магазину в конце нужно только половину времени моего (я повторяю фрагмент кода 1000000 раз), и я не вижу причины для этого. (Я использовал Intel VTune Amplifier, чтобы посмотреть на сборку и потраченное время.)
Спасибо за помощь!
Почему бы не генерировать ассемблерный листинг из авто-векторизации коды и использовать его в качестве отправной точки и посмотреть, если вы можете улучшить его? –
У меня уже есть сборка.Но, как я сказал, я смущен, потому что он извлекает данные больше, он не использует инструкции загрузки/хранения для выровненных данных, хотя он выровнен и в качестве основного пункта: я не понимаю, почему хранилище происходит быстрее , хотя в обоих случаях выполняется инструкция типа vmovupd ymmword ptr [ecx + 0x408fc0], ymm6 . – user3572032
Избавьтесь от деления, очевидно. Умножьте на ответное. – harold