使用预先计算的平移阵列的快速Sin/Cos

Gil*_*lad 15 c# optimization performance

我有以下代码使用预先计算的内存表进行Sin/Cos函数.在下面的示例中,该表具有1024*128项,涵盖从0到2pi的所有Sin/Cos值.我知道我可以使用Sin/Cos对称并只保留1/4的值,但是在计算值时我会有更多'ifs'.

private const double PI2 = Math.PI * 2.0; 
private const int TABLE_SIZE = 1024 * 128;
private const double TABLE_SIZE_D = (double)TABLE_SIZE;
private const double FACTOR = TABLE_SIZE_D / PI2;

private static double[] _CosineDoubleTable;
private static double[] _SineDoubleTable;
Run Code Online (Sandbox Code Playgroud)

设置转换表

private static void InitializeTrigonometricTables(){
   _CosineDoubleTable = new double[TABLE_SIZE];
   _SineDoubleTable = new double[TABLE_SIZE];

   for (int i = 0; i < TABLE_SIZE; i++){
      double Angle = ((double)i / TABLE_SIZE_D) * PI2;
      _SineDoubleTable[i] = Math.Sin(Angle);
      _CosineDoubleTable[i] = Math.Cos(Angle);
   }
}
Run Code Online (Sandbox Code Playgroud)

价值是弧度的两倍.

Value %= PI2;  // In case that the angle is larger than 2pi
if (Value < 0) Value += PI2; // in case that the angle is negative
int index = (int)(Value * FACTOR); //from radians to index and casted in to an int
double sineValue = _SineDoubleTable[index]; // get the value from the table
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种更快的方法来做到这一点.以上4行是整个过程的约25%(执行数十亿次).

dtb*_*dtb 21

您可以尝试使用不安全的代码来消除数组边界检查.
但即使是不安全的优化版本似乎也不会出现在Math.Sin附近.

基于随机值的1'000'000'000次迭代的结果:

(1) 00:00:57.3382769  // original version
(2) 00:00:31.9445928  // optimized version
(3) 00:00:21.3566399  // Math.Sin
Run Code Online (Sandbox Code Playgroud)

码:

static double SinOriginal(double Value)
{
    Value %= PI2;
    if (Value < 0) Value += PI2;
    int index = (int)(Value * FACTOR);
    return _SineDoubleTable[index];
}

static unsafe double SinOptimized(double* SineDoubleTable, double Value)
{
    int index = (int)(Value * FACTOR) % TABLE_SIZE;
    return (index < 0) ? SineDoubleTable[index + TABLE_SIZE]
                       : SineDoubleTable[index];
}
Run Code Online (Sandbox Code Playgroud)

测试程序:

InitializeTrigonometricTables();
Random random = new Random();

SinOriginal(random.NextDouble());
var sw = System.Diagnostics.Stopwatch.StartNew();
for (long i = 0; i < 1000000000L; i++)
{
    SinOriginal(random.NextDouble());
}
sw.Stop();
Console.WriteLine("(1) {0}  // original version", sw.Elapsed);

fixed (double* SineDoubleTable = _SineDoubleTable)
{
    SinOptimized(SineDoubleTable, random.NextDouble());
    sw = System.Diagnostics.Stopwatch.StartNew();
    for (long i = 0; i < 1000000000L; i++)
    {
        SinOptimized(SineDoubleTable, random.NextDouble());
    }
    sw.Stop();
    Console.WriteLine("(2) {0}  // optimized version", sw.Elapsed);
}

Math.Sin(random.NextDouble());
sw = System.Diagnostics.Stopwatch.StartNew();
for (long i = 0; i < 1000000000L; i++)
{
    Math.Sin(random.NextDouble());
}
sw.Stop();
Console.WriteLine("(3) {0}  // Math.Sin", sw.Elapsed);
Run Code Online (Sandbox Code Playgroud)

  • +1过早优化,万恶之源等等.这里有好的教训 (2认同)

Cha*_*tie 9

我假设泰勒扩张对你没用.所以如果你想使用一个表:你只需要一个一半的表.

  1. cos(x) = sin(pi/2-x).
  2. sin(pi + x) = -sin(x)

您可以使您的代码不分支.首先转换为int格式.

int index = (int)(Value * FACTOR);
index %= TABLE_SIZE; // one instuction (mask)
index = (index >= 0) ? index :TABLE_SIZE-index; // one instruction isel
double sineValue = _SineDoubleTable[index];
Run Code Online (Sandbox Code Playgroud)

无论如何,与Math.Sin比较.简介简介Priofile.(在实际示例中,缓存未命中可能会降低代码速度.)