October 21, 2024
Chicago 12, Melborne City, USA
C#

Are there some sort of hidden optimization for the sin() function in C?


Recently I’ve been looking at some methods of approximating sine, and tried comparing the speed difference between using a sine look-up table (LUT) with linear interpolation and the standard sin() function in C.

The way I tested the speed was through iterating over a range of angles from 0-pi/2 at a specific step angle using a for() loop, and calculating the sine of each angle. I then check the number of clock pulses elapsed to find the time it took to finish the calculation:

// **************** built-in sin() ****************
resetTimer();

for(float i = 0; i < _PI_2; i += 0.05)
  {
    volatile float value = sin(i);
  }

Serial.println(getTime());

// **************** sine LUT ****************
resetTimer();

for(float i = 0; i < _PI_2; i += 0.05)
  {
    volatile float value = _sin(i); // sine LUT
  }

Serial.println(getTime());

The test was ran on an 8-bit processor Arduino NANO that operates at 16MHz.

The lookup table was significantly faster at a step resolution of 0.05 radians (31 iterations):

0.05 rad resolution:

3620.00 us for built-in sin()

2225.37 us for sine LUT

However, when I increased the step resolution to 0.01 radians (157 iterations), the sin() function was instead much faster despite calculating more samples:

0.01 rad resolution:

1735.50 us for built-in sin()

2326.56 us for sine LUT

How did the sin() function take less time to calculate 157 samples compared to 31 samples??

From what I’ve read, the sin() function in C mainly approximates sine using the taylor series, but also switches between other algorithms based on the input angle. Is there some implementation tricks that causes this weird behaviour?

Arduino code simulated on https://wokwi.com/projects/new/arduino-nano

double _2_PI = 6.28318530718;
float _PI_2 = 1.57079632679;
float DOWNSCALER = 0.00003051757;
 
void initTimer()
{
    TCCR1A = 0;
    TCCR1B = bit(CS10);
    TCNT1 = 0;
}
 
void resetTimer()
{
    TCNT1 = 0;
}
 
float getTime()
{
    unsigned int cycles = TCNT1;
    float _ms = (float)(cycles - 1) / 16;
    return _ms;
}
 
int16_t sineLUT[65] = {0, 804, 1607, 2410, 3211, 4011, 4807, 5601, 6392, 7179, 7961, 8739, 9511, 10278, 11038, 11792, 12539, 13278, 14009, 14732, 15446, 16150, 16845, 17530, 18204, 18867, 19519, 20159, 
                    20787, 21402, 22004, 22594, 23169, 23731, 24278, 24811, 25329, 25831, 26318, 26789, 27244, 27683, 28105, 28510, 28897, 29268, 29621, 29955, 30272, 30571, 30851, 31113, 31356, 
                    31580, 31785, 31970, 32137, 32284, 32412, 32520, 32609, 32678, 32727, 32757, 32767};
 
float _sin(float angle)
{
        uint16_t i = (uint16_t)(angle / _2_PI * 65536.0f);
        int8_t frac = i & 0xFF;
        i = (i >> 8) & 0xFF;
        
        int t1, t2;
        
        if(i < 64){
            t1 = (int)sineLUT[i];
            t2 = (int)sineLUT[i+1];
        }
        else if(i < 128){
            t1 = (int)sineLUT[128 - i];
            t2 = (int)sineLUT[127 - i];
        }
        else if(i < 192){
            t1 = -(int)sineLUT[i - 128];
            t2 = -(int)sineLUT[i - 127];
        }
        else{
            t1 = -(int)sineLUT[256 - i];
            t2 = -(int)sineLUT[255 - i];
        }
        
    return DOWNSCALER * (t1 + (((t2 - t1) * frac) >> 8));
}
 
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  initTimer();
 
  // ***************** built-in sin() *****************
  resetTimer();
 
  for(float i = 0; i < _PI_2; i += 0.01)
  {
    volatile float value = sin(i);
  }
 
  Serial.print(getTime());
  Serial.println(" us for built-in sin()");
 
  // ***************** sine lookup *****************
  resetTimer();
 
  for(float i = 0; i < _PI_2; i += 0.01)
  {
    volatile float value = _sin(i);
  }
 
  Serial.print(getTime());
  Serial.println(" us for sine LUT");
 
}
 
void loop() {}



You need to sign in to view this answers

Leave feedback about this

  • Quality
  • Price
  • Service

PROS

+
Add Field

CONS

+
Add Field
Choose Image
Choose Video