【问题标题】:Efficient SSE FP `floor()` / `ceil()` / `round()` Rounding Functions Without SSE4.1?没有 SSE4.1 的高效 SSE FP `floor()` / `ceil()` / `round()` 舍入函数?
【发布时间】:2019-05-30 01:17:26
【问题描述】:

我如何像这些函数一样将 __m128 浮点数向上/向下或最接近的整数舍入?

我需要这样做没有 SSE4.1 roundps (_mm_floor_ps / _mm_ceil_ps / _mm_round_ps(x, _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC). roundps 也可以截断为零,但我不需要对于这个应用程序。

我可以使用 SSE3 及更早版本。 (无 SSSE3 或 SSE4)

所以函数声明应该是这样的:

__m128 RoundSse( __m128 x )__m128 CeilSse( __m128 x )__m128 FloorSse( __m128 x )

【问题讨论】:

  • 起点是reddit.com/r/programming/comments/1p2yys/…。虽然它使用按引用而不是按值。那里的代码也会产生 VS 2017 的问题。
  • 你确定你真的需要 round,而 IEEE 默认的舍入模式不适合你吗? rintfnearbyintf 给你,而 round 使用 x86 在硬件中没有的特殊舍入模式。 round() for float in C++。 (如果您有 SSE4.1,可以在 roundps 之上使用一些指令来模拟它,因此如果您可以将 roundps 模拟到最近,您仍然可以模拟 round(),但它可能会更慢。)
  • Google for "sse_mathfun" - 这是一个有用的库,包含上述函数和许多其他函数。
  • @PaulR ,我宁愿在这里有一个实现,这样我才能理解它是如何工作的。对于其他搜索它的人也将是很好的参考。
  • DirectXMath,具体是this source fileXMVectorRoundXMVectorFloorXMVectorCeil的实现

标签: c optimization vectorization sse simd


【解决方案1】:

我从http://dss.stephanierct.com/DevBlog/?p=8发布代码:

应该采用按值形式(我刚刚从代码中删除了&,不确定是否可以):

static inline __m128 FloorSse(const __m128 x) {
    __m128i v0 = _mm_setzero_si128();
    __m128i v1 = _mm_cmpeq_epi32(v0, v0);
    __m128i ji = _mm_srli_epi32(v1, 25);
    __m128i tmp = _mm_slli_epi32(ji, 23); // I edited this (Added tmp) not sure about it
    __m128 j = _mm_castsi128_ps(tmp); //create vector 1.0f // I edited this not sure about it
    __m128i i = _mm_cvttps_epi32(x);
    __m128 fi = _mm_cvtepi32_ps(i);
    __m128 igx = _mm_cmpgt_ps(fi, x);
    j = _mm_and_ps(igx, j);
    return _mm_sub_ps(fi, j);
}

static inline __m128 CeilSse(const __m128 x) {
    __m128i v0 = _mm_setzero_si128();
    __m128i v1 = _mm_cmpeq_epi32(v0, v0);
    __m128i ji = _mm_srli_epi32(v1, 25);
    __m128i tmp = _mm_slli_epi32(ji, 23); // I edited this (Added tmp) not sure about it
    __m128 j = _mm_castsi128_ps(tmp); //create vector 1.0f // I edited this not sure about it
    __m128i i = _mm_cvttps_epi32(x);
    __m128 fi = _mm_cvtepi32_ps(i);
    __m128 igx = _mm_cmplt_ps(fi, x);
    j = _mm_and_ps(igx, j);
    return _mm_add_ps(fi, j);
}

static inline __m128 RoundSse(const __m128 a) {
    __m128 v0 = _mm_setzero_ps();             //generate the highest value < 2
    __m128 v1 = _mm_cmpeq_ps(v0, v0);
    __m128i tmp = _mm_castps_si128(v1); // I edited this (Added tmp) not sure about it
    tmp = _mm_srli_epi32(tmp, 2); // I edited this (Added tmp) not sure about it
    __m128 vNearest2 = _mm_castsi128_ps(tmp); // I edited this (Added tmp) not sure about it
    __m128i i = _mm_cvttps_epi32(a);
    __m128 aTrunc = _mm_cvtepi32_ps(i);        // truncate a
    __m128 rmd = _mm_sub_ps(a, aTrunc);        // get remainder
    __m128 rmd2 = _mm_mul_ps(rmd, vNearest2); // mul remainder by near 2 will yield the needed offset
    __m128i rmd2i = _mm_cvttps_epi32(rmd2);    // after being truncated of course
    __m128 rmd2Trunc = _mm_cvtepi32_ps(rmd2i);
    __m128 r = _mm_add_ps(aTrunc, rmd2Trunc);
    return r;
}


inline __m128 ModSee(const __m128 a, const __m128 aDiv) {
    __m128 c = _mm_div_ps(a, aDiv);
    __m128i i = _mm_cvttps_epi32(c);
    __m128 cTrunc = _mm_cvtepi32_ps(i);
    __m128 base = _mm_mul_ps(cTrunc, aDiv);
    __m128 r = _mm_sub_ps(a, base);
    return r;
}

【讨论】:

  • 您遗漏了博客中的额外信息,即在转换为整数并返回工作的范围之外,这些信息并不完全安全。该块具有这些的安全包装器。根据 reddit 讨论 (reddit.com/r/programming/comments/1p2yys/…),此更新版本的代码实际上确实适用于 Bruce Dawson 测试的整个输入范围。
  • 我也尝试将其切换为按值形式。我不确定我做对了。 VS 2017 在博客中的代码中也存在一些问题。我也尝试过修复它,但我再次不确定我是否正确地完成了它。这就是为什么我把它命名为 WIKI,以便人们可以编辑和改进它。如果您愿意,请张贴作为您的答案。我会将其标记并复制到 Wiki 中。
  • 将其更改为按值实际上就像删除 & 一样简单。不过,没有必要这样做。你应该改变的是*(__m128*)(&tmp) 应该是_mm_castsi128_ps(tmp),和类似的。这是个坏主意,尽管 __m128 是一个可能别名类型。
  • @PeterCordes,我确实删除了&。我只是不熟悉它在这种情况下的效果,因此不知道它是否有效。我还使用了您推荐的演员阵容。你现在怎么看?
  • 哦,我忘了这是一道 C 题。您不熟悉 C++ 参考资料吗?只要您的调用约定按值支持__m128 args,它就与任何其他对象没有什么不同。无论如何,您应该简化常量,用_mm_set1_ps(1.0)1.999999whatever 替换愚蠢的cmpeq 和整数。这段代码的作者试图超越编译器并阻止它从内存中加载常量,但使用cmpps 而不是pcmpeqd 来创建全1 位模式。是否值得动态生成这些常量是值得怀疑的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-16
  • 2017-02-19
  • 2010-09-08
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
相关资源
最近更新 更多