将 % 与 SSE2 一起使用?
Using % with SSE2?
这是我尝试转换为SSE2的代码:
double *pA = a;
double *pB = b[voiceIndex];
double *pC = c[voiceIndex];
double *left = audioLeft;
double *right = audioRight;
double phase = 0.0;
double bp0 = mNoteFrequency * mHostPitch;
for (int sampleIndex = 0; sampleIndex < blockSize; sampleIndex++) {
// some other code (that will use phase)
phase += std::clamp(mRadiansPerSample * (bp0 * pB[sampleIndex] + pC[sampleIndex]), 0.0, PI);
while (phase >= TWOPI) { phase -= TWOPI; }
}
以下是我所取得的成就:
double *pA = a;
double *pB = b[voiceIndex];
double *pC = c[voiceIndex];
double *left = audioLeft;
double *right = audioRight;
double phase = 0.0;
double bp0 = mNoteFrequency * mHostPitch;
__m128d v_boundLower = _mm_set1_pd(0.0);
__m128d v_boundUpper = _mm_set1_pd(PI);
__m128d v_bp0 = _mm_set1_pd(bp0);
__m128d v_radiansPerSample = _mm_set1_pd(mRadiansPerSample);
__m128d v_phase = _mm_set1_pd(phase);
__m128d v_pB = _mm_load_pd(pB);
__m128d v_pC = _mm_load_pd(pC);
__m128d v_result = _mm_mul_pd(v_bp0, v_pB);
v_result = _mm_add_pd(v_result, v_pC);
v_result = _mm_mul_pd(v_result, v_radiansPerSample);
v_result = _mm_max_pd(v_result, v_boundLower);
v_result = _mm_min_pd(v_result, v_boundUpper);
for (int sampleIndex = 0; sampleIndex < roundintup8(blockSize); sampleIndex += 8, pB += 8, pC += 8) {
// some other code (that will use v_phase)
v_phase = _mm_add_pd(v_phase, v_result);
v_pB = _mm_load_pd(pB + 2);
v_pC = _mm_load_pd(pC + 2);
v_result = _mm_mul_pd(v_bp0, v_pB);
v_result = _mm_add_pd(v_result, v_pC);
v_result = _mm_mul_pd(v_result, v_radiansPerSample);
v_result = _mm_max_pd(v_result, v_boundLower);
v_result = _mm_min_pd(v_result, v_boundUpper);
v_phase = _mm_add_pd(v_phase, v_result);
v_pB = _mm_load_pd(pB + 4);
v_pC = _mm_load_pd(pC + 4);
v_result = _mm_mul_pd(v_bp0, v_pB);
v_result = _mm_add_pd(v_result, v_pC);
v_result = _mm_mul_pd(v_result, v_radiansPerSample);
v_result = _mm_max_pd(v_result, v_boundLower);
v_result = _mm_min_pd(v_result, v_boundUpper);
v_phase = _mm_add_pd(v_phase, v_result);
v_pB = _mm_load_pd(pB + 6);
v_pC = _mm_load_pd(pC + 6);
v_result = _mm_mul_pd(v_bp0, v_pB);
v_result = _mm_add_pd(v_result, v_pC);
v_result = _mm_mul_pd(v_result, v_radiansPerSample);
v_result = _mm_max_pd(v_result, v_boundLower);
v_result = _mm_min_pd(v_result, v_boundUpper);
v_phase = _mm_add_pd(v_phase, v_result);
v_pB = _mm_load_pd(pB + 8);
v_pC = _mm_load_pd(pC + 8);
v_result = _mm_mul_pd(v_bp0, v_pB);
v_result = _mm_add_pd(v_result, v_pC);
v_result = _mm_mul_pd(v_result, v_radiansPerSample);
v_result = _mm_max_pd(v_result, v_boundLower);
v_result = _mm_min_pd(v_result, v_boundUpper);
// ... fmod?
}
但我真的不确定如何替换while (phase >= TWOPI) { phase -= TWOPI; }
(这基本上是C++中的经典fmod
)。
有什么花哨的内在吗?在此列表中找不到任何内容。 分裂+某种火箭位移位?
正如评论所说,看起来在这里你可以用比较 +andpd
来做一个被屏蔽的减法。 只要您永远不能减去超过一个减法回到所需范围,这就可以了。
喜欢
const __m128d v2pi = _mm_set1_pd(TWOPI);
__m128d needs_range_reduction = _mm_cmpge_pd(vphase, v2pi);
__m128d offset = _mm_and_pd(needs_range_reduction, v2pi); // 0.0 or 2*Pi
vphase = _mm_sub_pd(vphase, offset);
要实现实际(缓慢)fmod
而不过多担心有效数的最后几位,您需要执行integer_quotient = floor(x/y)
(或者可能是rint(x/y)
或ceil
),然后x - y * integer_quotient
。floor
/rint
/ceil
使用 SSE4.1_mm_round_pd
或_mm_floor_pd()
都很便宜。 这将为您提供余数,它可以是负数,就像整数除法一样。
我敢肯定,有一些数字技术可以更好地避免在灾难性取消减去两个附近的数字之前四舍五入误差。 如果您关心精度,请去检查。 (当你不太关心精度时,使用double
向量有点愚蠢;不妨使用float
,每个向量完成两倍的工作)。 如果输入比模数大得多,则不可避免地会损失精度,并且最小化临时舍入误差可能非常重要。 但除此之外,精度只会是一个问题,除非您关心非常接近零的结果中的相对误差,而x
几乎是y
的精确倍数。 (接近零的结果,只有有效数的底部几位留给精度。
如果没有SSE4.1,有一些技巧,比如加减一个足够大的数字。 转换为整数并返回对pd
来说更糟糕,因为打包转换指令也会解码为一些随机 uop。 更不用说 32 位整数并不能覆盖整个double
范围,但如果您的输入如此之大,那么您的降程精度就会被搞砸。
如果您有 FMA,则可以避免乘法和 sub 的y * integer_quotient
部分的舍入误差。_mm_fmsub_pd
.
- 如何将enable-if与模板参数和参数包一起使用
- 如何将PERF_AMPLE_READ与mmap一起使用
- 如何将两个不同矢量的同一位置的两个元素组合在一起
- 如何将C++中的库和头与MinGW一起使用
- 将--whole archive链接器选项与CMake和具有其他库依赖项的库一起使用
- 为什么我不能将 rand() 与数组的大小一起使用?
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 不能将复制初始化与隐式转换的多个步骤一起使用
- 将fold表达式与std::一起用于两个元组
- spdlog标头仅与外部fmt一起使用.spdlog错误:'内部':不是'fmt'
- 将 std::allocate_shared 与多态资源分配器一起使用
- 为什么常量词在重载运算符中不与 ostream 对象一起使用<<?
- 将 OpenCV 与 CMAKE 中的项目一起构建为第三方库的正确方法
- 将 exprtk 与自定义类的对象一起使用
- 将 std::set 与基于键的比较器一起使用
- 将 C++ 类与 Rcpp 一起使用,从 C 或 R 修改它
- 如何将 Eigen::Ref 与 pybind11 一起使用?
- 如何将AERT_Allocate与 std:vector 一起使用
- 如何使我的 sizeof sum 结构与空参数包一起工作
- 将 % 与 SSE2 一起使用?