假设您必须计算定义域介于 0.01 和 360.01 之间的正弦(余弦或正切 - 任意)。 (使用 C#)
什么会更高效?
- 使用 Math.Sin
- 使用具有预先计算值的查找数组
我预计在给定域的情况下,选项 2 会快得多。在域的精度 (0.0000n) 的什么点上,计算的性能超过了查找。
最佳答案
更新:通读到最后。看起来查找表毕竟比 Math.Sin 快。
我猜查找方法会比 Math.Sin 更快。我还想说它会更快很多,但罗伯特的回答让我觉得我仍然想要对此进行基准测试以确定。我做了很多音频缓冲处理,我注意到有这样的方法:
for (int i = 0; i < audiodata.Length; i++)
{
audiodata[i] *= 0.5;
}
执行速度明显快于
for (int i = 0; i < audiodata.Length; i++)
{
audiodata[i] = Math.Sin(audiodata[i]);
}
如果 Math.Sin 和简单乘法之间的差异很大,我猜想 Math.Sin 和查找之间的差异也会很大。
不过,我不知道,我装有 Visual Studio 的电脑在地下室,我太累了,没时间花 2 分钟来确定这个问题。
更新:好的,测试这个花了 2 多分钟(更像是 20 分钟),但看起来Math.Sin 至少比查找表快两倍(使用字典)。这是使用 Math.Sin 或查找表执行 Sin 的类:
public class SinBuddy
{
private Dictionary<double, double> _cachedSins
= new Dictionary<double, double>();
private const double _cacheStep = 0.01;
private double _factor = Math.PI / 180.0;
public SinBuddy()
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += _cacheStep)
{
double angleRadians = angleDegrees * _factor;
_cachedSins.Add(angleDegrees, Math.Sin(angleRadians));
}
}
public double CacheStep
{
get
{
return _cacheStep;
}
}
public double SinLookup(double angleDegrees)
{
double value;
if (_cachedSins.TryGetValue(angleDegrees, out value))
{
return value;
}
else
{
throw new ArgumentException(
String.Format("No cached Sin value for {0} degrees",
angleDegrees));
}
}
public double Sin(double angleDegrees)
{
double angleRadians = angleDegrees * _factor;
return Math.Sin(angleRadians);
}
}
这是测试/计时代码:
SinBuddy buddy = new SinBuddy();
System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
int loops = 200;
// Math.Sin
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.Sin(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
// lookup
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.SinLookup(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
使用 0.01 度的步长值并在整个值范围内循环 200 次(如此代码中所示)使用 Math.Sin 大约需要 1.4 秒,使用字典查找表大约需要 3.2 秒。将步长值降低到 0.001 或 0.0001 会使查找对 Math.Sin 的性能更差。此外,这个结果更支持使用 Math.Sin,因为 SinBuddy.Sin 在每次调用时都会执行乘法运算,将以度为单位的角度转换为以弧度为单位的角度,而 SinBuddy.SinLookup 只是进行直接查找。
这是在便宜的笔记本电脑上(没有双核或任何花哨的东西)。罗伯特,你这个男人! (但我仍然认为我应该得到支票,因为我完成了工作)。
更新 2:事实证明,停止并重新启动秒表不会重置经过的毫秒数,因此查找速度似乎只快了一半,因为它的时间包括了 Math.Sin 调用的时间.另外,我重新阅读了这个问题,意识到你是在谈论将值缓存在一个简单的数组中,而不是使用字典。这是我修改后的代码(我保留旧代码作为对后代的警告):
public class SinBuddy
{
private Dictionary<double, double> _cachedSins
= new Dictionary<double, double>();
private const double _cacheStep = 0.01;
private double _factor = Math.PI / 180.0;
private double[] _arrayedSins;
public SinBuddy()
{
// set up dictionary
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += _cacheStep)
{
double angleRadians = angleDegrees * _factor;
_cachedSins.Add(angleDegrees, Math.Sin(angleRadians));
}
// set up array
int elements = (int)(360.0 / _cacheStep) + 1;
_arrayedSins = new double[elements];
int i = 0;
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += _cacheStep)
{
double angleRadians = angleDegrees * _factor;
//_cachedSins.Add(angleDegrees, Math.Sin(angleRadians));
_arrayedSins[i] = Math.Sin(angleRadians);
i++;
}
}
public double CacheStep
{
get
{
return _cacheStep;
}
}
public double SinArrayed(double angleDegrees)
{
int index = (int)(angleDegrees / _cacheStep);
return _arrayedSins[index];
}
public double SinLookup(double angleDegrees)
{
double value;
if (_cachedSins.TryGetValue(angleDegrees, out value))
{
return value;
}
else
{
throw new ArgumentException(
String.Format("No cached Sin value for {0} degrees",
angleDegrees));
}
}
public double Sin(double angleDegrees)
{
double angleRadians = angleDegrees * _factor;
return Math.Sin(angleRadians);
}
}
以及测试/时序代码:
SinBuddy buddy = new SinBuddy();
System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
int loops = 200;
// Math.Sin
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.Sin(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
// lookup
timer = new System.Diagnostics.Stopwatch();
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.SinLookup(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
// arrayed
timer = new System.Diagnostics.Stopwatch();
timer.Start();
for (int i = 0; i < loops; i++)
{
for (double angleDegrees = 0; angleDegrees <= 360.0;
angleDegrees += buddy.CacheStep)
{
double d = buddy.SinArrayed(angleDegrees);
}
}
timer.Stop();
MessageBox.Show(timer.ElapsedMilliseconds.ToString());
这些结果完全不同。使用 Math.Sin 大约需要 850 毫秒,字典查找表大约需要 1300 毫秒,基于数组的查找表大约需要 600 毫秒。 所以看起来(正确编写的 [gulp])查找表实际上比使用 Math.Sin 快一点,但幅度不大。
请自行验证这些结果,因为我已经证明了我的无能。
关于c# - 计算与查找表的正弦值性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1382322/