使用 SIMD 管理累积(单个)值的清理代码循环的方法是什么
What the method to manage Cleanup Code loop for a cumulative (single) value packed into two values using SIMD?
假设我管理一个名为 v_phase
的__m128d
变量,其计算公式为
index 0 : load prev phase
index 1 : phase += newValue
index 2 : phase += newValue
index 3 : phase += newValue
index 4 : phase += newValue
...
这是基本代码:
__m128d v_phase;
// load prev cumulated mPhase to v_phase (as mPhase, mPhase + nextValue)
for (int sampleIndex = 0; sampleIndex < blockSize; sampleIndex += 2, pValue += 2) {
// function with phase
// update pValue increment (its not linear)
// phase increment: v_phase += newValue
}
// cleanup code
if (blockSize % 2 == 0) {
mPhase = v_phase.m128d_f64[0];
}
事实是:如果blockSize
是偶数,它工作正常:它将在最后一次循环迭代中将另外两个相位值相加,并取v_phase.m128d_f64[0]
(即新添加的两个相位值中的第一个)。
但是,如果blockSize
很奇怪呢?我只需要上次迭代的v_phase.m128d_f64[1]
,而无需再对两个相位值求和。
我可以使用sampleIndex < blockSize - 1
,但这会将逻辑// function with phase
移动到// cleanup code
(我不太喜欢它)。
循环中放置一个if是我会避免的事情(branc预测;因为我使用的是SIMD,所以我正在优化代码,这会变慢)。
有什么提示吗?
下面是一个更"完整"的示例:
double phase = mPhase;
__m128d v_pB = _mm_setr_pd(0.0, pB[0]);
v_pB = _mm_mul_pd(v_pB, v_radiansPerSampleBp0);
__m128d v_pC = _mm_setr_pd(0.0, pC[0]);
v_pC = _mm_mul_pd(v_pC, v_radiansPerSample);
__m128d v_pB_prev = _mm_setr_pd(0.0, 0.0);
v_pB_prev = _mm_mul_pd(v_pB_prev, v_radiansPerSampleBp0);
__m128d v_pC_prev = _mm_setr_pd(0.0, 0.0);
v_pC_prev = _mm_mul_pd(v_pC_prev, v_radiansPerSample);
__m128d v_phaseAcc1;
__m128d v_phaseAcc2;
__m128d v_phase = _mm_set1_pd(phase);
// phase
v_phaseAcc1 = _mm_add_pd(v_pB, v_pC);
v_phaseAcc1 = _mm_max_pd(v_phaseAcc1, v_boundLower);
v_phaseAcc1 = _mm_min_pd(v_phaseAcc1, v_boundUpper);
v_phaseAcc2 = _mm_add_pd(v_pB_prev, v_pC_prev);
v_phaseAcc2 = _mm_max_pd(v_phaseAcc2, v_boundLower);
v_phaseAcc2 = _mm_min_pd(v_phaseAcc2, v_boundUpper);
v_phase = _mm_add_pd(v_phase, v_phaseAcc1);
v_phase = _mm_add_pd(v_phase, v_phaseAcc2);
for (int sampleIndex = 0; sampleIndex < blockSize; sampleIndex += 2, pB += 2, pC += 2) {
// code that will use v_phase
// phase increment
v_pB = _mm_loadu_pd(pB + 1);
v_pB = _mm_mul_pd(v_pB, v_radiansPerSampleBp0);
v_pC = _mm_loadu_pd(pC + 1);
v_pC = _mm_mul_pd(v_pC, v_radiansPerSample);
v_pB_prev = _mm_load_pd(pB);
v_pB_prev = _mm_mul_pd(v_pB_prev, v_radiansPerSampleBp0);
v_pC_prev = _mm_load_pd(pC);
v_pC_prev = _mm_mul_pd(v_pC_prev, v_radiansPerSample);
v_phaseAcc1 = _mm_add_pd(v_pB, v_pC);
v_phaseAcc1 = _mm_max_pd(v_phaseAcc1, v_boundLower);
v_phaseAcc1 = _mm_min_pd(v_phaseAcc1, v_boundUpper);
v_phaseAcc2 = _mm_add_pd(v_pB_prev, v_pC_prev);
v_phaseAcc2 = _mm_max_pd(v_phaseAcc2, v_boundLower);
v_phaseAcc2 = _mm_min_pd(v_phaseAcc2, v_boundUpper);
v_phase = _mm_add_pd(v_phase, v_phaseAcc1);
v_phase = _mm_add_pd(v_phase, v_phaseAcc2);
}
// cleanup code
if (blockSize % 2 == 0) {
mPhase = v_phase.m128d_f64[0];
}
else {
??? if odd?
}
除了
最后一个之外,您还可以从循环中输出前一个v_phase
。也就是说,在更新您的v_phase
之前,存储上一个:
__m128d prev_v_phase;
for (...) {
...
prev_v_phase = v_phase;
v_phase = _mm_add_pd(v_phase, v_phaseAcc1);
v_phase = _mm_add_pd(v_phase, v_phaseAcc2);
}
// cleanup code
if (blockSize % 2 == 0) {
mPhase = v_phase.m128d_f64[0];
}
else {
mPhase = prev_v_phase.m128d_f64[1];
}
如果循环根本不执行任何迭代(然后prev_v_phase
将未初始化),这将失败,但在这种情况下,性能并不重要,因此易于处理。
相关文章:
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 为什么在这个代码结束循环中没有得到结束
- 下面是我为检测链接列表中的循环而制作的代码
- 为什么在C的循环中使用printf的Rust代码不显示输出,而在C++的循环中显示std::cout
- 在java中解决这段代码时面临循环中的问题
- 如何通过替换顺序代码的while循环来添加OpenMP for循环
- 我的代码运行良好,但在游戏循环中中断
- 如何使用递归循环我的代码(当用户输入无效输入时,它会再次提示他们)?
- 如何编译使用循环引用的代码?
- 我正在尝试创建一个菜单,但我的代码一直在循环
- 使用基于数组和范围的 For 循环替换一些基本代码行
- 在C/C 中使用检查功能的Windows NT命令行代码循环
- 我想不出一种方法来使我的代码循环
- 使用 SIMD 管理累积(单个)值的清理代码循环的方法是什么
- 我如何让我的部分代码循环并重复一个问题,直到答案是有效的输入C++
- C 代码循环问题
- 如何仅使用if-else语句(no-for、do/while等)使此代码循环一定次数?(c++)
- 如何运行代码循环 if (a==b) 并且不循环如果 a != b 没有"else"
- 用相同的代码循环遍历对象和指针
- 如何使我的代码循环(与arduino)