C++ 将%与SSE2一起使用?

C++ 将%与SSE2一起使用?,c++,intrinsics,sse2,C++,Intrinsics,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 <

以下是我试图转换为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[语音索引];
双*pC=c[语音索引];
双*左=左;
双*右=音频右;
双相=0.0;
双bp0=mNoteFrequency*mHostPitch;
for(int-sampleIndex=0;sampleIndex=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?
}
double*pA=a;
double*pB=b[语音索引];
双*pC=c[语音索引];
双*左=左;
双*右=音频右;
双相=0.0;
双bp0=mNoteFrequency*mHostPitch;
__m128d v_边界=_mm_set1_pd(0.0);
__m128d v_boundUpper=_mm_set1_pd(PI);
__m128d v_bp0=_mm_set1_pd(bp0);
__m128d v_弧度采样=_mm_设置1_pd(mRadiansPerSample);
__m128d v_相位=_mm_设置1_pd(相位);
__m128d v_pB=_mm_载荷_pd(pB);
__m128d v_pC=_mm_负载_pd(pC);
__m128d v_结果=_mm_mul_pd(v_bp0,v_pB);
v_result=_mm_add_pd(v_result,v_pC);
v_结果=_mm_mul_pd(v_结果,v_弧度样本);
v_结果=_mm_max_pd(v_结果,v_边界);
v_result=_mm_min_pd(v_result,v_boundUpper);
对于(int-sampleIndex=0;sampleIndex
但是我不确定如何替换while(phase>=TWOPI){phase-=TWOPI;}(这基本上是C++中的经典
fmod

有什么奇特的内在因素吗?在这上面找不到任何线索。
除法+某种火箭位移位?

正如评论所说,在这种情况下,您可以使用compare+
和pd
使其成为一个带掩码的减法。只要你再减去一次就可以回到你想要的范围内,这是可行的


要实现一个实际的(慢的)
fmod
,而不太担心有效位的最后几位,您需要执行
integer\u商数=floor(x/y)
(或者
rint(x/y)
或者
ceil
),然后执行
x-y*integer\u商数
<代码>地板/
打印
/
天花板
使用SSE4.1
\u mm\u round\u pd
\u mm\u floor\u pd()
价格便宜。这将得到余数,余数可以是负数,就像整数除法一样

我确信有一些数字技术可以更好地避免在灾难性的相消之前通过减去两个相邻的数字而产生舍入误差。如果你关心精度,去检查一下。(在不太关心精度的情况下使用
double
向量有点愚蠢;最好使用
float
并使每个向量完成两倍的工作)。如果输入比模大很多,则不可避免地会损失精度,在临时输入中最小化舍入误差可能非常重要。但是,如果
x
几乎是
y
的精确倍数,那么精度只会是一个问题,除非您关心结果中非常接近于零的相对误差。(结果接近零时,只剩下有效位底部的几个位用于精度。)

在没有SSE4.1的情况下,有一些技巧,比如先加一个足够大的数,然后再减去一个足够大的数。对于
pd
,转换为整数并返回更糟糕,因为压缩转换指令也会解码为一些随机UOP。更不用说32位整数不能覆盖整个
double
范围,但是如果您的输入太大,那么您的范围缩减精度就会受到影响


如果您有,您可以避免乘法和sub的
y*整数商
部分的舍入错误。
\u mm\u fmsub\u pd

但您希望应用此校正的频率是多少,以及发生时需要减去多少个PI?也许比较和减法仍然是一条路要走。(我不知道SSE2。)@Rup:不知道:)这取决于
阶段
将如何成长……你甚至确定自动矢量化不适合你吗?当您在原始源代码上启用SSE2(例如,在g++上使用
-msse2
)时,是否检查了生成的代码?因为
阶段
在那一点上永远不会超过3pi。@markzzz我明白了。您仍然可以使用godbolt来检查gcc的功能,并理解(或者改进)它<代码>阶段
正在计算
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);