【问题标题】:Fast ArcSin implementation or approximation in c#c# 中的快速 ArcSin 实现或近似
【发布时间】:2016-12-01 18:16:28
【问题描述】:

我需要在我的项目中多次计算 ASin。在 C# 中,它需要很多时间。

我使用系统命名空间中的 Math.Asin()

问题是有什么方法可以在 C# 中更快地实现 Asin 函数。

任何近似算法或其他可以更快工作的实现?

【问题讨论】:

  • 如果一个近似答案对您来说足够好,那么您应该考虑预先计算一个查找表。您可以根据需要使用尽可能多的条目以获得所需的准确性。
  • 我不能使用太多额外的内存,但仍然需要一个很好的近似值。所以预先计算对我不起作用。
  • 如果没有其他方法可以实现 asin ,那么我需要逼近函数,但还是找不到合适的
  • 真的是asin 是瓶颈,还是因为不断的范围检查而需要安全 C# 代码变慢的数组?将代码中慢的部分标记为unsafe 以避免范围检查是否有帮助? -- asin(x) 本质上是atan2(x,sqrt(1-x*x)),其中atan2sqrt 通常由FPU 处理,必须非常小心地选择替代方案,因为它们很容易超过操作数。
  • 你真的分析过你的代码吗?

标签: c# math


【解决方案1】:

在 cmets 和测试基准中进行了一些辩论之后,很明显我在下面给出的解决方案并不能真正提高 System.Math.Asin() 的性能。事实上,这两个调用几乎可以忽略不计,对任何应用程序都不会产生巨大影响。如果您遇到性能问题,您的原因可能是以下之一:

  1. 您的 arcsin 调用实际上并不是瓶颈。您需要分析您的应用程序以确定瓶颈。过早优化是一个常见错误,可能会导致大量时间浪费。
  2. 您调用该函数的次数过多。您可能有充分的理由多次调用函数,但调用可能会被优化为更高级别。可能的解决方案是使用并行调用或更改操作顺序以减少调用。如果您拨打y = sin(x); z = asin(y) 之类的电话,那么您正在拨打额外的不必要电话。这是一个简单的例子,但许多更复杂的计算在数学上可能会产生类似的效果。
  3. 您在不应该调用的地方调用了该函数。例如,如果您尝试在 GUI 或渲染线程中进行计算,您将遇到性能问题并且缺乏响应能力。这是一个常见的设计错误,应注意不应在 GUI 线程中完成计算。
  4. 您的用例不可行。如果您正在执行实时数据转换和可视化之类的操作,那么您可以实时处理的数据量是有上限的。这取决于硬件,除了将计算卸载到具有更多处理能力的地方之外,没有什么可以做的。像这样的案例就是云计算可以派上用场的地方。

以上几点,下面的解决方案仍然是 C# 优化的有效途径。请注意,在分析应用程序之前不应进行优化,这样您才能真正知道瓶颈就在您认为的位置。请注意,这不是优化的唯一途径。并行处理或选择不同算法等路径也是有效的。


有点不同的解决方案,但您可以尝试使用类似于this question 的答案来加载 C 标准库。 Windows 上的 C 标准库是msvcrt.dll,应该包含一个函数asin

[DllImport("msvcrt.dll", EntryPoint="asin",
ExactSpelling=false, CharSet=CharSet.Unicode,
SetLastError=true)]
static extern double asin(double radians);

//calling the function
static void Main()
{
    double x = asin(0);
}

如果这还不够快,您可以用 C 编写一个快速的 asin 算法。this question 与您的类似。您还可以创建一个更快的平方根函数以用于此解决方案。

根据您需要的准确度,如果您的角度接近 0,您还可以进行泰勒级数近似。您也可以将角度移动到接近零,但这需要一点更多的诡计。

【讨论】:

  • 为什么软链接过程应该比 CLI 调用基本相同的代码更快?
  • 我的假设是 System.Math.Asin() 正在由 .NET 运行时解释,而不是在机器级别。如果是这种情况,C 版本应该会快一点。如果不是,它们应该大致相同。唯一确定的方法是基准测试。如果性能相同,C 中的自定义实现应该比 C# 中的相同实现更快。由于 OP 似乎需要比大多数情况所需的更高性能,将一些计算移植到 C 应该能够挤出一些额外的性能。
  • 我相信在 CLI 运行时调用 asin 就像对 IEEE 754 定义的数学函数的其他调用一样是硬编码的,应该没有进一步的解释子例程,除了参数检查。
  • 我将this code 放在一起尝试对这两种情况进行基准测试。由此,这两种方法得出的结果几乎相同。但除了一些异常值外,我还看到每次调用都有 0 或 1 个滴答声,这让我想知道它的时间是否正确。我认为这可能是编译器的优化,但我已将这些点保存到文件中并读回它们以尝试摆脱编译器预先计算的任何值。如果结果是正确的,我认为 OP 应该考虑他是否真的有问题。
  • 我仍然认为,如果这真的是慢的部分,那么就涉及到一些循环for(k=0; k<N; k++) y[k]=Math.asin(x[k])。这可能可以通过执行y[N-1]=Math.asin(x[N-1]); 来触发可能的范围检查错误,然后在unsafe 代码块中执行循环来加速。
猜你喜欢
  • 2013-12-08
  • 2011-01-21
  • 1970-01-01
  • 1970-01-01
  • 2012-01-01
  • 1970-01-01
  • 2011-03-29
  • 2017-02-18
  • 1970-01-01
相关资源
最近更新 更多