有没有优化此功能的方法?

时间:2019-02-11 20:36:41

标签: c++ optimization integer-arithmetic

对于我正在处理的应用程序,我需要取两个整数,并使用特定的数学公式将它们加在一起。最终看起来像这样:

int16_t add_special(int16_t a, int16_t b) {
    float limit = std::numeric_limits<int16_t>::max();//32767 as a floating point value
    float a_fl = a, b_fl = b;
    float numerator = a_fl + b_fl;
    float denominator = 1 + a_fl * b_fl / std::pow(limit, 2);
    float final_value = numerator / denominator;
    return static_cast<int16_t>(std::round(final_value));
}

任何对物理学有一定了解的读者都会认识到,该公式与用于计算近光速速度总和的公式相同,此处的计算有意反映该计算。

编写的代码给出了我需要的结果:对于低数字,它们通常 加在一起,但是对于高数字,它们收敛到最大值32767,即

  • add_special(10, 15) == 25
  • add_special(100, 200) == 300
  • add_special(1000, 3000) == 3989
  • add_special(10000, 25000) == 28390
  • add_special(30000, 30000) == 32640

所有这些似乎都是正确的。

但是,问题在于编写的函数涉及先将数字转换为浮点值,然后再将其转换回整数。对于我所知道的数字,这似乎是不必要的绕道,作为其域的原理,它永远不会是整数。

是否有更快,更优化的方法来执行此计算?还是这是我可以创建的功能的最优化版本?

我正在使用MSVC 14.X为x86-64进行构建,尽管也适用于GCC的方法将是有益的。另外,我对现阶段的SSE / SIMD优化不感兴趣。我主要只是查看对数据执行的基本操作。

3 个答案:

答案 0 :(得分:2)

您可能会避免使用浮点数,并以整数类型进行所有计算:

constexpr int16_t add_special(int16_t a, int16_t b) {
    std::int64_t limit = std::numeric_limits<int16_t>::max();
    std::int64_t a_fl = a;
    std::int64_t b_fl = b;
    return static_cast<int16_t>(((limit * limit) * (a_fl + b_fl)
                                 + ((limit * limit + a_fl * b_fl) / 2)) /* Handle round */
                                / (limit * limit + a_fl * b_fl));
}

Demo

但是根据Benchmark来说,这些值并不更快。

答案 1 :(得分:1)

建议:

  • 使用32767.0*32767.0(这是一个常量)而不是std::pow(limit, 2)
  • 尽可能使用整数值(可能带有固定点)。仅这两个部门是一个问题。视需要使用浮点数(仅取决于它们)(取决于输入数据范围)。
  • 如果函数较小且合适,则将其设为inline

类似的东西:

int16_t add_special(int16_t a, int16_t b) {
    float numerator = int32_t(a) + int32_t(b); // Cannot overflow.
    float denominator = 1 + (int32_t(a) * int32_t(b)) / (32767.0 * 32767.0); //  Cannot overflow either.
    return (numerator / denominator) + 0.5; // Relying on implementation defined rounding. Not good but potentially faster than std::round().
}

上述唯一的风险是省略了显式舍入,因此您将获得一些隐式舍入。

答案 2 :(得分:1)

Johannes Overmann指出,通过避免使用std::round可以大大提高性能,但是结果却有些(很小)差异。

我尝试了其他一些小的更改HERE,似乎以下是一种更快的方法(至少对于该体系结构而言)

constexpr int32_t i_max = std::numeric_limits<int16_t>::max();
constexpr int64_t i_max_2 = static_cast<int64_t>(i_max) * i_max;

int16_t my_add_special(int16_t a, int16_t b)
{
    // integer multipication instead of floating point division
    double numerator = (a + b) * i_max_2; 
    double denominator = i_max_2 + a * b;
    // Approximated rounding instead of std::round
    return 0.5 + numerator / denominator;
}