I have the following code doing Sin/Cos function using a pre-calculated memory table. in the following example the table has 1024*128 items covering all the Sin/Cos values from 0 to 2pi. I know I can use Sin/Cos symmetry and hold only 1/4 of the values but them I will have more 'ifs' when computing the value.
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;
Set the translation table
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);
}
}
The Value is a double in radians.
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
I'm looking for a faster way to do this. The above 4 lines are ~25% of the whole process (executed billions of times).
You could try to use unsafe code to eliminate array bounds checking.
But even a unsafe, optimized version does not seem to come anywhere near Math.Sin.
Results based on 1'000'000'000 iterations with random values:
(1) 00:00:57.3382769 // original version
(2) 00:00:31.9445928 // optimized version
(3) 00:00:21.3566399 // Math.Sin
Code:
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];
}
Test program:
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With