【发布时间】: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 会向编译器建议该变量属于该函数,从而让编译器预先计算它并将其包含在每个因子的函数定义中。
但是我非常怀疑我对const 和static 的理解是否正确,因此我猜测这些定义都没有预先计算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。帮助编译器解决这个问题的最佳机会是重组factor由constexpr函数单独计算,并作为参数传递给smoothstep()。