vexide icon indicating copy to clipboard operation
vexide copied to clipboard

Add support for floating point math in `vexide-core`.

Open tropicaaal opened this issue 1 year ago • 1 comments

What's the motivation for this feature?

vexide programs run in a no_std enviornment. Unfortunately, core is currently missing support for floating point math functions such as f64::sin and f64::cos, etc... which are all extremely important for anyone doing localization or controls work.

Describe the solution you'd like

There are several options we have, each with some level of tradeoffs. So as a quick summary:

  • The reason why these methods are not in core is because std's implementation is based off of LLVM intrinsics, which aren't guaranteed to be there for any given target. These intrinsics are by far going to be the most optimized for our platform, though and are available through the unstable compiler internal feature core_intriniscs. This doesn't cover every math function (only about 3/4 of them), as Rust std relies on the platform cmath implementations for the rest.
  • There is an official effort to port MUSL's libm to Rust under the rust-lang organization. It's important to note that this is NOT the typical C libm but rather a port of it to rust. While these work, they aren't fast due to using software floating point math. Our target has vfp3 support and therefore we probably waste performance by using this.
  • There's also the libm.a static library that ships with PROS. Now this is the one that i'm not sure about. I know next to nothing about where this library comes from or how it's implemented (I assume it comes from some part of newlib or glibc though). If it leverages the FPU, then it's going to perform much better than the official rust port, but we don't know if it does so that's going to need benchmarking. There's also the issue that it's written in C, and linking to C static libraries is expensive and means the compiler can't strip out the stuff we don't need.

So, from what I can tell we have maybe two or so options:

  • We can keep using the current recommended solution which is to slap num_traits into your project and use num_traits::real::Real, which reimplements the math functions in terms of rust's libm.
    • Drawback: Since it uses rust's libm, it's soft float only even though we have access to LLVM intrinsics that are much faster (I think, at least).
    • Drawback: It's an external crate and therefore is a level of boilerplate that is added to every project and is outside of vexide's version lifecycle. We could just have this in prelude and be done with it.
  • We can reimplement types in terms of core::intrinsics and either rust's libm or PROS libm.a
    • Drawback: core::intrinsics are unstable and will likely never be stabilized due to being a compiler internal. Womp womp.
    • I'm generally for this option, but deciding with one's better in terms of size/speed tradeoffs will need benchmarking.
    • If we go with Rust's libm it may or may not be slower.
    • If we go with PROS' libm.a, our baseline binary size will likely take a hit (not sure how much).

Describe the drawbacks, if any

Depending on which option we choose, floating point math may be slower than expected or add binary size.

Describe the alternative solutions, if any

See above

tropicaaal avatar May 17 '24 16:05 tropicaaal

Benchmark Results

To my surprise, it seems like the PROS libm is ~15x slower than the soft-float rust libm. image

Additionally we don't have support for LLVM intrinsics like I originally thought, leaving the rust libm as the only way forward with this. For now, it's probably just better to keep using num_traits' Float trait.

tropicaaal avatar May 17 '24 21:05 tropicaaal