关于优化的数学函数、范围和区间
About optimized math functions, ranges and intervals
我试图让我的头脑围绕着那些为游戏和渲染引擎编写数学函数代码的人如何有效地使用优化的数学函数;让我进一步解释一下。
在这些领域中对快速三角函数有很高的需求,有时你可以优化sin
,cos
或其他函数,通过将它们重写为仅对给定区间有效的不同形式,通常这意味着你对f(x)
的近似只是针对第一象限,即0 <= x <= pi/2
。
现在你的f(x)
的输入仍然是关于所有4个象限,但真正的公式只覆盖了该区间的1/4,直接的解决方案是通过分析输入来检测象限,看看它属于哪个范围,然后你相应地调整公式的结果,如果输入来自一个象限,不是第一象限。
这在理论上是好的,但这也提出了一些非常糟糕的问题,特别是考虑到你正在做的这一切都是为了从你的CPU窃取几个周期(你也得到一个一致的实现,这不是平台依赖于一个硬编码的fsin
在英特尔x86 asm,只在x86上工作,并有一定的错误范围,所有这些可能不同于其他平台与其他asm指令),因此,您应该保持工作在并发和高性能水平。
我无法理解带有象限解决方案的"switch case"的原因是:
- 它只是阻止了可能的优化,即记忆,考虑到你通常想把
switch-case
放在实际计算f(x)
的相同函数中,可能情况可以通过在该函数之外实现f(x)
的公式来改善,但这将导致任何给定数学库维护的函数数量增加一倍 - 通过并发执行增加更多分支的可能性 一般来说,
- 不会产生更好、干净、干燥的代码,条件语句经常是潜在的bug来源,我真的不喜欢切换用例和类似的东西。
假设我可以在C或c++中实现我的跨平台f(x)
,该领域的程序员通常如何解决通过实际实现转换和映射输入,象限到结果的问题?
注意:在下面的回答中,我是在非常笼统地谈论代码。
假设我可以在C或c++中实现我的跨平台f(x),该领域的程序员通常如何解决通过实际实现转换和映射输入,象限到结果的问题?
一般的答案是:以最明显和最简单的方式达到你的目的。
我不完全确定我是否理解你的大多数论点/问题,但我有一种感觉,你在寻找实际上不存在的问题。你真的需要重新实现三角函数吗?不要落入美国国立卫生研究院(NIH)的陷阱。
的 解决方法是检测象限
是的!我喜欢简单明了的代码。代码的作用一目了然。现在,有时,只是有时,您必须做一些疯狂的事情来让它做您想做的事情:为了性能,或者避免您无法控制的bug。但是第一个版本应该是解决问题的最明显和最简单的代码。在此基础上,您可以进行测试、分析和基准测试,如果(仅当)发现性能或其他问题,那么您将进入疯狂的工作。
我想说这在理论上是好的,在大多数情况下和在实践中是好的,我绝对没有看到任何"坏"问题。在特定的角落案例或设计需求中的小问题。这在理论上是好的,但这也带来了一些非常糟糕的问题,
对你的一些具体评论有几点看法:
- f(x)的近似仅适用于第一象限:是的,这有很多原因。一个简单的原因是,大多数三角函数都有恒等式,所以你可以很容易地使用它们来减小输入参数的范围。这很重要,因为许多数值技术只能在特定的输入范围内工作,或者对于小输入更准确/性能。接下来,对于非常大的输入,你将不得不削减范围,无论如何,大多数数值技术工作,或至少工作在一个体面的数量的时间,并有足够的准确性。例如,看看
cos()
的泰勒展开,看看对于大输入和小输入,它需要多长时间才能充分收敛。 - 它只是阻止可能的优化:现在你的c++编译器很可能比你更擅长优化。有时不是这样,但一般的过程是让编译器进行优化,并且只在您测量并证明需要时进行手动优化。现在,仅仅通过观察来判断哪个代码更快是非常不直观的(你可以阅读关于性能问题的所有问题以及一些根本原因是多么疯狂)。
- 即记忆:我从未见过
double
函数的记忆。想想0和1之间有多少双精度数。现在,在精度降低的情况下,你可以利用它,但这很容易实现,作为一个定制的函数,为那个确切的情况量身定制。考虑到这一点,我不太确定如何实现double
函数的记忆,这实际上意味着任何东西,并且在此过程中不会失去准确性或性能。 - 增加并发执行更多分支的可能性:我不确定我会以并发的方式实现三角函数,但我认为这完全有可能获得一些性能上的好处。但是,编译器通常比你更擅长优化,所以让它做它的工作,然后基准测试/配置文件看看你是否真的需要做得更好。
- 不会导致更好、干净、干燥的代码:我不确定您在这里究竟是什么意思,或者"干代码"是什么意思。是的,有时候你会因为太多或太复杂的if/switch块而陷入麻烦,但我不认为4象限的简单检查适用于这里……这是一个非常基本和简单的例子。
- 所以对于任何平台,对于相同的x值,我得到相同的y:我的猜测是,在多个平台和系统上获得所有53位
double
的"精确"值是不可能的。如果你只有52位正确的结果是什么?这将是一个伟大的领域做一些测试,看看你得到什么。
我在C中使用三角函数超过20年,99%的时间我只是使用任何内置函数提供。在极少数情况下,我需要通过测试或基准测试证明更高的性能(或准确性),只有在这种情况下,我才会针对特定情况实际推出自己的自定义实现。我不会重写<math.h>
函数的整个范围,希望有一天我可能需要它们。
我建议尝试用尽可能多的方法编写这些函数,并做一些准确性和基准测试。这将给你一些实用的知识,给你一些关于你是否真的需要重新实现这些函数的硬数据。至少,这应该会给你一些实现这些类型函数的实际经验,并有可能回答你在这个过程中的许多问题。
- 是否可以依赖函数范围的静态变量来执行程序关闭期间调用的方法?
- 在函数范围内在堆栈上分配的数组在离开函数时是否总是被释放?
- 在 gcc/clang (C++) 中获取函数范围之外的标签地址
- 函数范围的静态变量如何导致与共享库中函数代码的未来使用不兼容
- 是否未定义将对函数范围变量的引用作为值返回
- 为什么当我的代码超出函数范围时,"does not name a type"出现编译器错误?
- 在函数范围之外的标头中使用 UDL
- 模板化函数范围内的条件模板化类型别名
- 获取提升程序选项以在函数范围之后保留
- C++ 链表 - 插入节点函数不会更新函数范围之外的值
- C++:将参数应用于函数范围
- 为什么基于指针交换两个值在函数范围之外不起作用?
- 类模板中定义的朋友函数范围定义的范围是什么?
- 在内联程序集中使用函数范围的标签
- 检测函数范围之外的未使用变量
- 静态变量生存期、文件范围与函数范围
- 是否可以编写一个在函数范围内使用时会抱怨的宏
- Arduino-setup()中声明的变量不在函数范围内
- C++-函数范围内的函数声明
- 我是否要删除函数范围内的内存?