Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cortex-M4F: 'libm::sin()' is 2 times slower than 'sin()' of C newlib-libm #246

Open
JOE1994 opened this issue Aug 28, 2020 · 2 comments
Open

Comments

@JOE1994
Copy link

JOE1994 commented Aug 28, 2020

Issue

I compared the elapsed time of a small program (in both C and Rust) that calls the double-precision sin() function 1 million times. Rust version was more than 2 times slower compared to the C version.

compared on NucleoF429ZI board Elapsed time (seconds)
C (newlib, gcc) 21.254157
Rust (build target: thumbv7em-none-eabihf) 45.066286

C & Rust program code comparison

  • C program (uses libm from newlib)
int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  MX_GPIO_Init();
  MX_USART3_UART_Init();
 
  double x = 0.0;
  int i = 0;
  
  ITM_SendChar('A'); // Start measuring time --------
  for(i = 0; i<1000000; i++)
    x += sin( (double) i );

  ITM_SendChar('A'); // Finish measuring time --------
  printf("\n%f\n", x);

  while(1) {}
}
  • Rust program (uses rust-lang/libm)
#[entry]
fn main() -> ! {
    if let (Some(dp), Some(cp)) = (
        stm32::Peripherals::take(),
        cortex_m::peripheral::Peripherals::take(),
    ) {
        // Set up the system clock. We want to run at 168MHz for this one.
        let rcc = dp.RCC.constrain();
        let clocks = rcc.cfgr.sysclk(168.mhz()).freeze();

        cortex_m::asm::bkpt(); // BKPT: Start measuring time --------
        
        let mut x: f64 = 0.0;
        for i in 0..1_000_000 {
            // Call `libm::sin()` 1 million times!
            x += libm::sin(i as f64);
        }
        
        cortex_m::asm::bkpt(); // BKPT: Finish measuring time --------

        hprintln!("{:.6}", x).unwrap();
    }

    loop { continue; }
}

What I tried so far

rust-lang/libm::sin was ported from musl libc, and its implementation is slightly different from newlib's sin() function. In my fork of rust-lang/libm, I changed the implementation of rust-lang/libm::sin to be equivalent to newlib libm's sin() function. Below is the performance comparison.

compared on NucleoF429ZI board Elapsed time (seconds)
C (newlib, gcc) 21.254157
Rust (sin impl equivalent to musl libc) 45.066286
Rust (sin impl equivalent to newlib) 42.326438

The change made the program take 2.74 seconds less than the program using the sin implementation of musl libc.
However, the performance gap is still significant between C and Rust 😿
Since the performance gap is significant even when the source codes are equivalent,
it seems that this issue has to be handled at the compiler level.

@Lokathor
Copy link
Contributor

this might sound obvious, but did you try the C code with clang? that is the C compiler closest to rustc after all.

@JOE1994
Copy link
Author

JOE1994 commented Sep 22, 2020

this might sound obvious, but did you try the C code with clang? that is the C compiler closest to rustc after all.

I'm struggling to have clang work for the cortex-m target, but I'll get back once I get it working :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants