Slow performance in Rust compared to Go and C# with math, especially f64::tan()
I am a C# programmer, completely new to Rust, Go and Kotlin and I have been writing some tests to evaluate the performance of C#, Rust, Go and Kotlin in various situations starting with multi threaded heavy CPU bound math calculations.
I was expecting Rust to be far ahead of the rest based on what I read but surprisingly it came out third behind Go and C#.
The tests where all in optimized release mode (cargo build --release for Rust).
To remove other variables I tested it with only one thread, repeating the tests 5 times and taking the smallest value:
- Go: 3212ms
- Rust with "f64::sin/f64::cos" instead of "f64::tan": 3888ms
- C#: 4019ms
- Rust: 4243ms
- Kotlin/JVM: 8552ms
- Kotlin/Native: 8786ms
The main bottleneck part is this calculation run in a very long for loop:
sum = ((sum.abs().sqrt()*(f64::from(i)+2.1).ln()*PI).abs() + (f64::from(i)*f64::from(i).sin().max(f64::from(i).tan())*f64::from(i)/7.605).abs() - 1.87*f64::from(i)/2.3)/(sum + sum*4.4 + sum/1.1456 + f64::from(i)*8.98) + f64::from(i).atan2(sum);
which I actually rewrote like this using constant function pointer types with no visible reductions of speed:
sum = (ABS(SQRT(ABS(sum))*LN(I32TOF64(i)+2.1)*PI) + ABS(I32TOF64(i)*MAX(SIN(I32TOF64(i)),TAN(I32TOF64(i)))*I32TOF64(i)/7.605) - 1.87*I32TOF64(i)/2.3)/(sum + sum*4.4 + sum/1.1456 + I32TOF64(i)*8.98) + ATAN2(I32TOF64(i), sum);
Don't try to understand it, it doesn't mean anything, the various values and functions where chosen with a reason but it doesn't matter for this post.
After testing each function separately it looks like f64::tan() was by far the slowest compared to other languages, while other functions where way faster.
Cos and Sin where also slow but faster that Tan so writing Sin/Cos instead of tan increased the speed above C# but still behind Go as can be seen above.
I am sure more optimizations can be done but that was not the purpose of this test, the equations needed to be the same on all platforms.
I looked to see how Tan is implemented and it looks like both Go and Rust take it from cmath, Rust uses the C code directly and wraps it while Go translated it into native Go.
I am not sure why Rust Tan is so much slower, I assume some unnecessary conversions might take place but I didn't have time to further debug.
The much faster functions are not from cmath and seem to use some compiler optimizations.
I am writing this more like a bug report, since I am very new to Rust I didn't want to use the official channels because I might be wrong.
Or maybe I did something wrong?
Unexpected behavior like this does not sound good for starting a large project because you might start it using the fast functions and after the project grows you add a bunch of the very slow ones and then suddenly the project slows to a crawl... in C# and Go performance is usually more predictable even if it's slower on average.