mathjs icon indicating copy to clipboard operation
mathjs copied to clipboard

Added type inferencing for Vector & Matrix operations in multiply.js (+performance boost!)

Open RandomGamingDev opened this issue 4 months ago • 11 comments

The Issue

mathjs has had an "issue" with performance (some of the first search results for "mathjs is slow":

  • https://stackoverflow.com/questions/54328275/math-js-is-slow-to-multiply-2-big-matrices
  • https://www.reddit.com/r/javascript/comments/275fi9/comment/iyj6t2n/?utm_source=share&utm_medium=web2x&context=3

Except it doesn't.

Why the slow performance and all the complaints then?


mathjs is quite powerful, but the idea that you should explicitly set the numeric type of your tensors is something that people simply don't come across when reading the documentation (here are some pages from the documentation):

  • https://mathjs.org/docs/reference/functions/multiply.html
  • https://mathjs.org/docs/datatypes/matrices.html

In fact, mathjs's documentation makes it look like explicitly defining the numeric type is completely unnecessary because of the default type assignment in the config of mathjs:

  • https://mathjs.org/docs/datatypes/numbers.html "Most functions can determine the type of output from the type of input: a number as input will return a number as output, a BigNumber as input returns a BigNumber as output. Functions which cannot determine the type of output from the input (for example math.evaluate) use the default number type, which can be configured when instantiating math.js"

This leads to extremely poor performance when people try to use mathjs:

  • Rendering 1000 hyper-cubes rotated around 2 axis using vanilla mathjs, no explicit numeric types (slow & avgs ~40ms/frame on my machine): https://editor.p5js.org/PotatoBoy/sketches/uPZZwvhgC

Which wouldn't exist had these explicit numeric types been used:

  • Rendering 1000 hyper-cubes rotated around 2 axis using vanilla mathjs with explicit numeric types (fast & avgs ~1-2ms/frame on my machine): https://editor.p5js.org/PotatoBoy/sketches/DsDHNojgz

Why this wasn't done before now and how this PR fixes the issue that caused it

It seems like adding type inferencing was previously considered, but then ignored because of an issue with changing element types:

  • Issue: https://github.com/josdejong/mathjs/issues/1709
  • Issue's issue: https://github.com/josdejong/mathjs/issues/1709#issuecomment-590008781

This PR fixes this by getting the type during the tensor operation itself

  • <tensor>.getDataType() is called to infer the datatype if it wasn't explicitly defined
  • While getting inferring the type every operation may seem slow, compared to the alternative of using the generic function, it's much much faster as seen by the previous examples and the solution's example and is well worth it for the performance.
  • The datatype of the generated tensor using the inferred datatype is changed right before returning it, but not for the instantiation parameter in order to future-proof it in case future optimized methods of loading tensor data are used so that they can be used for the operation.

The Solution this PR provides

Type inferencing boosts the performance to speeds nearly indistinguishable from the speeds of explicit types

  • Rendering 1000 hyper-cubes rotated around 2 axis using type inferencing mathjs, no explicit numeric types (fast & avgs ~1-2ms/frame on my machine): https://editor.p5js.org/PotatoBoy/sketches/01IsuOlTR

Lastly, I'd still recommend that the documentation more readily states and promotes the existence of these explicit types, but I'm not sure whether or not that would fit in with what mathjs is trying to go for in its documentation, and it mostly likely won't be necessary with type inferencing added so I won't be adding documentation doing that in this PR.

RandomGamingDev avatar Feb 07 '24 02:02 RandomGamingDev