我了解如何在 AVX2 中進行一般算術運算。但是,我想將標量代碼中的條件操作轉換為 AVX2。我該怎么做?例如,我想矢量化
double arr[4] = {1.0,2.0,3.0,4.0};
double condition = 3.0;
for (int i = 0; i < 4; i ) {
if (arr[i] < condition) {
arr[i] *= 1.75;
}
else {
arr[i] *= 6.5;
}
}
for (auto i : arr) {
std::cout << i << '\t';
}
預期輸出:
1.75 3.5 19.5 26
如何在 AVX2 中執行上述條件操作?
uj5u.com熱心網友回復:
使用 AVX2 條件操作。計算整個向量的兩種可能輸出。之后保存滿足您條件的那些特定結果(掩碼)。對于你的情況:
double arr[4] = { 1.0,2.0,3.0,4.0 };
double condition = 3.0;
__m256d vArr = _mm256_loadu_pd(&arr[0]);
__m256d vMultiplier1 = _mm256_set1_pd(1.75);
__m256d vMultiplier2 = _mm256_set1_pd(6.5);
__m256d vFirstResult = _mm256_mul_pd(vArr, vMultiplier1); //if-branch
__m256d vSecondResult = _mm256_mul_pd(vArr, vMultiplier2); //else-branch
__m256d vCondition = _mm256_set1_pd(condition);
vCondition= _mm256_cmp_pd(vArr, vCondition, _CMP_LT_OQ); //a < b ordered (non-signalling)
// Use mask to choose between _firstResult and _secondResult for each element
vFirstResult = _mm256_blendv_pd(vSecondResult, vFirstResult, vCondition);
double res[4];
_mm256_storeu_pd(&res[0], vFirstResult);
for (auto i : res) {
std::cout << i << '\t';
}
替代 BLENDV 的可能替代方法是 AND、ANDNOT 和 OR 的組合。然而,BLENDV 在簡單性和性能方面都要好得多。只要您至少擁有 SSE4.1 并且還沒有 AVX512,就可以使用 BLENDV。
有關_CMP_LT_OQ含義和含義的資訊,請參閱Dave Dopson 的表格。你可以做任何你想做的比較,相應地改變它。
Peter Cordes對 AVX2 和 AVX512 中的條件操作有詳細的說明。在Agner Fog 的“優化 C ”第 12.4 章第 121-124 頁中有更多關于條件向量化的示例(帶有 SSE 和 AVX512 示例) 。
也許您不想在 else-branch 中進行一些計算,或者不想明確地將其歸零。這樣您的預期輸出看起來像
1.75 3.5 0.0 0.0
在這種情況下,您可以更快地執行指令序列,因為您不必考慮else -branch。至少有兩種方法可以實作加速:
- 洗掉第二個乘法,但保留 blendv。而不是 _secondResult 只使用歸零向量(它可以是全域常量)。
- 洗掉二次乘法和 blendv,并用 AND 掩碼替換 blendv。此變體也使用歸零向量。
第二種方式會更好。例如,根據uops表,Skylake 微架構上的 VBLENDVB 需要 2 uops,2 個時鐘延遲,并且每個時鐘只能完成一次。同時,VANDPD 具有 1 微指令、1 個時鐘延遲,并且可以在一個時鐘內完成 3 次。
更糟糕的方式,只是與零混合
double arr[4] = { 1.0,2.0,3.0,4.0 };
double condition = 3.0;
__m256d vArr = _mm256_loadu_pd(&arr[0]);
__m256d vMultiplier1 = _mm256_set1_pd(1.75);
__m256d vFirstResult = _mm256_mul_pd(vArr, vMultiplier1); //if-branch
__m256d vZeroes = _mm256_setzero_pd();
__m256d vCondition = _mm256_set1_pd(condition);
vCondition = _mm256_cmp_pd(vArr, vCondition, _CMP_LT_OQ); //a < b ordered (non-signalling)
//Conditionally blenv _firstResult when IF statement satisfied, zeroes otherwise
vFirstResult = _mm256_blendv_pd(vZeroes, vFirstResult, vCondition);
double res[4];
_mm256_storeu_pd(&res[0], vFirstResult);
for (auto i : res) {
std::cout << i << '\t';
}
更好的方法,按位與比較結果是有條件地歸零的更便宜的方法。
double arr[4] = { 1.0,2.0,3.0,4.0 };
double condition = 3.0;
__m256d vArr = _mm256_loadu_pd(&arr[0]);
__m256d vMultiplier1 = _mm256_set1_pd(1.75);
__m256d vFirstResult = _mm256_mul_pd(vArr, vMultiplier1); //if-branch
__m256d vZeroes = _mm256_setzero_pd();
__m256d vCondition = _mm256_set1_pd(condition);
vCondition = _mm256_cmp_pd(vArr, vCondition, _CMP_LT_OQ); //a < b ordered (non-signalling)
// If result not satisfied condition, after bitwise AND it becomes zero
vFirstResult = _mm256_and_pd(vFirstResult, vCondition);
double res[4] = {0.0,0.0,0.0,0.0};
_mm256_storeu_pd(&res[0], vFirstResult);
for (auto i : res) {
std::cout << i << '\t';
這利用了比較結果向量的真正含義,并且 IEEE 的位模式0.0是所有位都歸零。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/537936.html
下一篇:檢查if陳述句中的空值
