【问题标题】:A faster but less accurate fsin for Intel asm?英特尔 asm 的更快但不太准确的 fsin?
【发布时间】:2014-07-13 07:49:39
【问题描述】:

由于x86下用于计算sin(x)函数的函数fsin可以追溯到奔腾时代,而且显然它甚至不使用SSE寄存器,我想知道是否有更新更好的指令集用于计算三角函数。

我习惯于用 C++ 编写代码并进行一些asm 优化,因此任何适合从 C++ 到 C 到 asm 的管道的东西都适合我。

谢谢。


我现在在 Linux 64 位下,使用 gccclang(即使是艰难的 clang 并没有真正提供任何与 FPU 相关的优化 AFAIK)。

编辑

  • 我已经实现了一个sin 函数,它通常比std::sin 快2 倍,即使打开sse
  • 我的功能永远不会比fsin 慢,即使是强硬的fsin 通常也更准确,但考虑到fsin 永远不会胜过我的sin 实现,我现在将保留我的sin,也是我的sin 完全可移植,而 fsin 仅适用于 x86。
  • 我需要这个来进行实时计算,所以我会用精度来换取速度,我认为我可以使用 4-5 位小数的精度。
  • 拒绝基于表的方法,我没有使用它,它会破坏缓存,让一切变慢,请不要使用基于内存访问或查找表的算法。

【问题讨论】:

  • 您能否更具体地说明您认为 SSE2 对您的案件没有帮助的原因?
  • 那你的情况是什么?你能详细说明你的问题吗?根据我对该论文的理解:它们将余弦表示为原始算术运算的函数,因此可以使用 SSE 对其进行矢量化,从而允许您同时计算 4 个余弦。
  • @user2485710 您需要在问题中说明这些目标:希望在精度范围内获得更快的速度,否则没有人能够提供帮助......
  • @OliCharlesworth 我喜欢你的 PRNG 方法,但我认为它不会按照要求精确到小数点后 4-5 位

标签: c++ c assembly intel trigonometry


【解决方案1】:

如果你对一个近似值没问题(我假设你是,如果你想击败硬件),你应该看看 Nick 在 DevMaster 上的 sin 实现:

http://devmaster.net/posts/9648/fast-and-accurate-sine-cosine

他有两个版本:一个“快速而草率”的方法和一个“缓慢而准确”的方法。一对夫妇回答有人估计相对误差分别为 12% 和 0.2%。我自己完成了一个实现,发现运行时间是我机器上硬件时间的 1/14 和 1/8。

希望有帮助!

PS:如果你自己这样做,你可以重构慢/准确的方法以避免乘法,并比尼克的版本略有改进,但我不记得具体是如何......

【讨论】:

  • 嗯,读起来很长,我正在阅读它,但现在我想我需要一些时间来处理它和相关的选项。但看起来那些人或多或少是游戏开发者,他们对此非常满意。
  • “你可以重构慢/准确的方法来避免乘法,并且比尼克的版本稍微改进”当霍纳形式是对一个人的多项式评估方案的改进时,应该避免对所谓的“快速准确”的实施。这篇博文的标题应该是“fast and inaccurate sine”,因为这两个版本都是这样。
  • @user2485710 标题写着“快速准确的正弦/余弦”。它没有说“近似”。任何返回 IEEE 754 数字的函数都可以假定其准确性受到该格式的限制。当函数产生的结果与实际结果相差 1 ULP 以内时,可以认为该函数是准确的。这篇文章再次描述的是一个不准确且快速的正弦函数(来自从未听说过霍纳方案的人)。
  • @user2485710 使用您需要的定义区间来扩充您的问题,我将向您展示一个可能比您已经编写的函数更具有绝对准确性的函数(我假设您对绝对精度感兴趣. 你也应该更清楚)
  • @user2485710 - Pascal 说帖子的标题中应该包含“近似”一词,因为该方法在典型的浮点意义上准确。 Horner's Method 是(除其他外)一种查找计算高效的多项式形式的方法。在这里应用它给了我比原始代码略微(百分之五?)的加速。次要,但如果您追求速度,那肯定是值得的。
【解决方案2】:

如果您需要针对 -π … π 上的绝对精度优化的正弦近似值,请使用:

X *(1 + X * X *(-0.1661251158026961831813227851437597220432 + X * X *(8.03943560729777481878247432892823524338e-3 + X * X * -1.4941402004593877749503989396238510717e-4)) P>

可以通过以下方式实现:

float xx = x * x;
float s = x + (x * xx) * (-0.16612511580269618f + xx * (8.0394356072977748e-3f + xx * -1.49414020045938777495e-4f));

也许还有optimized depending on the characteristics of your target architecture。此外,在链接的博客文章中未注明,如果您在汇编中实现此功能,请使用 FMADD 指令。如果用 C 或 C++ 实现,如果您使用 fmaf() C99 标准函数,请确保生成 FMADD。模拟版本比乘法和加法要昂贵得多,因为fmaf() 所做的并不完全等同于乘法加加法(因此仅这样实现它是不正确的)。

sin(x) 与上述多项式在 -π 和 π 图之间的区别如下:

多项式被优化以减少它与 -π 和 π 之间的 sin(x) 之间的差异,而不仅仅是有人认为是个好主意。

如果您只需要 [-1 … 1] 定义区间,则可以通过忽略其余部分使多项式在该区间上更准确。在此定义间隔内再次运行 the optimization algorithm 会产生:

X *(1 + X * X *(-1.666659904470566774477504230733785739156e-1 + X * X *(8.329797530524482484880881032235130379746e-3 + X * X *( - 1.928379009208489415662312713847811393721e-4))) P>

绝对误差图:

如果这对你来说太准确了,可以optimize a polynomial of lower degree for the same objective。那么绝对误差会更大,但你会节省一两个乘法。

【讨论】:

  • 我无法理解您的推理,您选择什么算法来推导第一个公式和其他公式?请记住,我需要为所有其他功能执行此操作,因此我需要一个算法。
  • @user2485710 好吧,你的问题是关于罪的,所以我回答了关于罪的问题。无论如何,使用的方法是 Remez 算法,它提供的内容在我的答案已经提供的链接中得到了非常清楚的解释:lolengine.net/blog/2011/12/21/better-function-approximations。使用它不需要了解它的工作原理(我没有)。
  • @user2485710 必须了解的是多项式逼近原理(否则,您最终会尝试使用 aX^2 + bX 形式的多项式来逼近 sin,而您有到处打电话给abs(),这很荒谬,就像Xavier Holt的回答中的“尼克版本”一样)。您还需要有关浮点的基本事实,以便您知道将 X 的系数固定为 1 是有益的。我使用了 LolRemez,可以从我已经提供的链接中获得,但是由于以上所有原因,正确使用它很复杂
  • @user2485710 1) en.wikipedia.org/wiki/Approximation_theory 。有这方面的书。我不会给你写一本书。 2)如果您不喜欢它,请不要使用它。 Maple 等工具中有可用的实现,但我无法访问这些工具,也没有任何迹象表明它们的实现更干净。您知道此代码未打包在最终产品中,对吗? 3)我给了你我使用的所有东西的链接,但是如果你拒绝我使用的工具,因为对你来说太“可怕”了,我无法进一步帮助你。
  • “从你的回答中我无法提取任何名称、参考或算法”Remez 算法。我给了你一个开源实现的名称和链接。 “您正在从其他来源复制和粘贴代码”实际上,我正在为您运行该算法,因为它使用起来很复杂,而且您的问题是关于 更快但不太准确的 fsin。不客气。 LolRemez 附带了一个教程,lolengine.net/wiki/doc/maths/remez,但你已经以“可怕”为由拒绝了该实现,我不知道还有任何其他免费的实现或教程。
猜你喜欢
  • 2015-08-27
  • 2012-02-27
  • 1970-01-01
  • 2016-09-23
  • 1970-01-01
  • 2016-09-05
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多