【问题标题】:Are these statements equivalent (static variables, const variables, and generics)这些语句是否等效(静态变量、常量变量和泛型)
【发布时间】:2018-09-19 02:19:12
【问题描述】:

所以我正在编写一个自定义平滑步长函数,允许我编辑平滑量。

基本上,在低因子下,平滑步长实际上是线性的,但在高因子下,曲线很大。

执行此操作的代码如下:

float smoothstep(float x, float factor) {
    float c = 0.5f / ((1.0f / (1.0f + exp(-factor))) - 0.5f);
    return c * ((1.0f / (1.0 + exp(-factor * (2.0f * x - 1.0f)))) - 0.5f) + 0.5;
}

现在,由于该方法每帧会被调用几千次,因此我正在尝试对其进行优化,以便在运行时计算 c。我希望编译器可以做到这一点,因为我传递了有限数量的因素。因此,为因子传入的变量将始终写入例如smoothstep(x, 1.0)。到目前为止,我实际上会传递一个因素。

所以我重写了这样的函数以使用泛型参数

template <int F>
float smoothstep(float x) {
    const float factor = (float)F / 100.0;
    static const float c = 0.5f / ((1.0f / (1.0f + exp(-factor))) - 0.5f);
    return c * ((1.0f / (1.0 + exp(-factor * (2.0f * x - 1.0f)))) - 0.5f) + 0.5;
}

这是相当不理想的,因为您只能使用整数作为模板参数,因此我必须传入一个 100 倍于我实际需要的 int。话虽如此,我相信这个解决方案会导致编译器预先计算因子和 c。对吧?

现在我的问题有没有办法在没有泛型的情况下做到这一点这是我对此采取的两个措施

float smoothstep(float x, float factor) {
    const float c = 0.5f / ((1.0f / (1.0f + exp(-factor))) - 0.5f);
    return c * ((1.0f / (1.0 + exp(-factor * (2.0f * x - 1.0f)))) - 0.5f) + 0.5;
}

float smoothstep(float x, float factor) {
    static const float c = 0.5f / ((1.0f / (1.0f + exp(-factor))) - 0.5f);
    return c * ((1.0f / (1.0 + exp(-factor * (2.0f * x - 1.0f)))) - 0.5f) + 0.5;
}

我希望在其中一种情况下,预编译器(Clang 和 G++)看到这些并为我传入的每个有限因子预先计算 c。

我的理解是const 是一个关键字,用于向预编译器和编译器发出一个变量不会被更改的信号,因此它应该考虑对其进行优化。

我还认为作用域变量上的static 会向编译器建议该变量属于该函数,从而让编译器预先计算它并将其包含在每个因子的函数定义中。

但是我非常怀疑我对conststatic 的理解是否正确,因此我猜测这些定义都没有预先计算c。我怎么了?有没有一种非通用的方法可以让预编译器预计算 c?

【问题讨论】:

  • 使函数内联并确保您已开启优化?
  • inline 是否只是一种向编译器发出信号进行预处理的方法,以便将 int add(int x, int y) {return x + y}int a = add(b, c) 之类的代码预处理为 int a = b+c 实质上删除了范围?
  • 不。此处使用的 static 关键字将可怕地搞砸一切。第一次调用该函数将初始化静态变量。恭喜!无论factor 是什么,这个静态变量现在都将保持这个值,在随后调用这个函数时!
  • @SamVarshavchik 刚刚的 const 版本怎么样?
  • 允许编译器执行编译器可以证明没有可观察到的副作用的任何优化。但是编译器不需要这样做,并且不能保证它会足够复杂以内联函数调用并确定c 的计算对于传递给此函数的所有连续调用只能执行一次在同一个factor。帮助编译器解决这个问题的最佳机会是重组factorconstexpr 函数单独计算,并作为参数传递给smoothstep()。

标签: c++ g++ clang


【解决方案1】:

只需创建函数inline(这也意味着该函数应该在头文件中)并确保您已进行优化。

示例:https://godbolt.org/z/rztmWe - 如您所见,exp 只有一次调用。

不幸的是,我认为没有保证可以达到这种效果 - 您必须依赖编译器优化。另一方面,现在很多编译器优化都相当可靠。

【讨论】:

  • 通用解决方案也这样做吗?当你说函数需要在标题中时,你的意思是它的实现吗?
  • @J.Doe Generic solution also does that。是的,执行。实现也必须在通用解决方案的标题中。 (否则编译器看不到函数的定义,所以如果它不知道它是做什么的,它肯定不会生成它的特殊版本)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-14
  • 2021-04-20
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
  • 2016-01-29
相关资源
最近更新 更多