MCPcopy Index your code
hub / github.com/bitshifter/mathbench-rs

github.com/bitshifter/mathbench-rs @0.4.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release 0.4.0 ↗ · + Follow
244 symbols 388 edges 20 files 15 documented · 6%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

mathbench

Build Status

mathbench is a suite of unit tests and benchmarks comparing the output and performance of a number of different Rust linear algebra libraries for common game and graphics development tasks.

mathbench is written by the author of glam and has been used to compare the performance of glam with other similar 3D math libraries targeting games and graphics development, including:

The benchmarks

All benchmarks are performed using Criterion.rs. Benchmarks are logically into the following categories:

  • return self - attempts to measure overhead of benchmarking each type.
  • single operations - measure the performance of single common operations on types, e.g. a matrix inverse, vector normalization or multiplying two matrices.
  • throughput operations - measure the performance of common operations on batches of data. These measure operations that would commonly be processing batches of input, for example transforming a number of vectors with the same matrix.
  • workload operations - these attempt to recreate common workloads found in game development to try and demonstrate performance on real world tasks.

Despite best attempts, take the results of micro benchmarks with a pinch of salt.

Operation benchmarks

  • matrix benches - performs common matrix operations such as transpose, inverse, determinant and multiply.
  • rotation 3d benches - perform common 3D rotation operations.
  • transform 2d & 3d benches - bench special purpose 2D and 3D transform types. These can be compared to 3x3 and 4x4 matrix benches to some extent.
  • transformations benches - performs affine transformations on vectors - uses the best available type for the job, either matrix or transform types depending on the library.
  • vector benches - perform common vector operations.

Workload benchmarks

  • euler bench - performs an Euler integration on arrays of 2D and 3D vectors

The benchmarks are currently focused on f32 types as that is all glam currently supports.

Crate differences

Different libraries have different features and different ways of achieving the same goal. For the purpose of trying to get a performance comparison sometimes mathbench compares similar functionality, but sometimes it's not exactly the same. Below is a list of differences between libraries that are notable for performance comparisons.

Matrices versus transforms

The euclid library does not support generic square matrix types like the other libraries tested. Rather it has 2D and 3D transform types which can transform 2D and 3D vector and point types. Each library has different types for supporting transforms but euclid is unique amongst the libraries tested in that is doesn't have generic square matrix types.

The Transform2D is stored as a 3x2 row major matrix that can be used to transform 2D vectors and points.

Similarly Transform3D is used for transforming 3D vectors and points. This is represented as a 4x4 matrix so it is more directly comparable to the other libraries however it doesn't support some operations like transpose.

There is no equivalent to a 2x2 matrix type in euclid.

Matrix inverse

Note that cgmath and nalgebra matrix inverse methods return an Option whereas glam and euclid do not. If a non-invertible matrix is inverted by glam or euclid the result will be invalid (it will contain NaNs).

Quaternions versus rotors

Most libraries provide quaternions for performing rotations except for ultraviolet which provides rotors.

Wide benchmarks

All benchmarks are gated as either "wide" or "scalar". This division allows us to more fairly compare these different styles of libraries.

"scalar" benchmarks operate on standard scalar f32 values, doing calculations on one piece of data at a time (or in the case of a "horizontal" SIMD library like glam, one Vec3/Vec4 at a time).

"wide" benchmarks operate in a "vertical" AoSoA (Array-of-Struct-of-Arrays) fashion, which is a programming model that allows the potential to more fully use the advantages of SIMD operations. However, it has the cost of making algorithm design harder, as scalar algorithms cannot be directly used by "wide" architectures. Because of this difference in algorithms, we also can't really directly compare the performance of "scalar" vs "wide" types because they don't quite do the same thing (wide types operate on multiple pieces of data at the same time).

The "wide" benchmarks still include glam, a scalar-only library, as a comparison. Even though the comparison is somewhat apples-to-oranges, in each of these cases, when running "wide" benchmark variants, glam is configured to do the exact same amount of final work, producing the same outputs that the "wide" versions would. The purpose is to give an idea of the possible throughput benefits of "wide" types compared to writing the same algorithms with a scalar type, at the cost of extra care being needed to write the algorithm.

To learn more about AoSoA architecture, see this blog post by the author of nalgebra which goes more in depth to how AoSoA works and its possible benefits. Also take a look at the "Examples" section of ultraviolet's README, which contains a discussion of how to port scalar algorithms to wide ones, with the examples of the Euler integration and ray-sphere intersection benchmarks from mathbench.

Note that the nalgebra_f32x4 and nalgebra_f32x8 benchmarks require a Rust

Additionally the f32x8 benchmarks will require the AVX2 instruction set, to enable that you will need to build with RUSTFLAGS='-C target-feature=+avx2.

Build settings

The default profile.bench settings are used, these are documented in the cargo reference.

Some math libraries are optimized to use specific instruction sets and may benefit building with settings different to the defaults. Typically a game team will need to decided on a minimum specification that they will target. Deciding on a minimum specifiction dictates the potential audience size for a project. This is an important decision for any game and it will be different for every project. mathbench doesn't want to make assumptions about what build settings any particular project may want to use which is why default settings are used.

I would encourage users who to use build settigs different to the defaults to run the benchmarks themselves and consider publishing their results.

Benchmark results

The following is a table of benchmarks produced by mathbench comparing glam performance to cgmath, nalgebra, euclid, vek, pathfinder_geometry, static-math and ultraviolet on f32 data.

These benchmarks were performed on an [Intel i7-4710HQ] CPU on Linux. They were compiled with the 1.49.0-nightly (ffa2e7ae8 2020-10-24) Rust compiler. Lower (better) numbers are highlighted within a 2.5% range of the minimum for each row.

The versions of the libraries tested were:

  • cgmath - 0.17.0
  • euclid - 0.22.1
  • glam - 0.10.0
  • nalgebra - 0.23.0
  • pathfinder_geometry - 0.5.1
  • static-math - 0.1.7
  • ultraviolet - 0.5.1
  • vek - 0.12.0 (repr_c types)

See the full [mathbench report] for more detailed results.

Scalar benchmarks

Run with the command:

cargo +nightly bench --features scalar scalar
benchmark glam cgmath nalgebra euclid vek pathfinder static-math ultraviolet
euler 2d x10000 16.18 us 16.3 us 16.25 us 16.23 us 16.25 us 10.42 us 11.84 us 16.28 us
euler 3d x10000 16.13 us 32.09 us 25.49 us 32.2 us 32.21 us 16.23 us 34.74 us 32.07 us
matrix2 determinant 2.0417 ns 2.1235 ns 2.1118 ns N/A 2.1132 ns 2.1182 ns 2.1173 ns 2.1161 ns
matrix2 inverse 2.8321 ns 8.4686 ns 7.6035 ns N/A N/A 3.4420 ns 8.3189 ns 5.8985 ns
matrix2 mul matrix2 6.1247 ns 4.8130 ns 2.5461 ns N/A 11.6360 ns 2.5541 ns 4.7587 ns 4.7334 ns
matrix2 mul vector2 x1 2.8408 ns 2.6186 ns 2.6343 ns N/A 5.4199 ns 2.1706 ns 2.6969 ns 2.6153 ns
matrix2 mul vector2 x100 276.1011 ns 237.2400 ns 243.3239 ns N/A 545.8342 ns 220.7986 ns 264.6844 ns 236.9462 ns
matrix2 return self 2.8687 ns 2.8712 ns 2.8892 ns N/A 2.8857 ns 2.4157 ns 2.8777 ns 2.9764 ns
matrix2 transpose 2.2713 ns 3.0883 ns 2.2310 ns N/A 3.0914 ns N/A 3.0835 ns 3.0775 ns
matrix3 determinant 3.8307 ns 3.7721 ns 3.8148 ns N/A 3.8240 ns N/A 3.8223 ns 8.9148 ns
matrix3 inverse 15.2042 ns 18.2388 ns 12.7075 ns N/A N/A N/A 12.8133 ns 22.1096 ns
matrix3 mul matrix3 10.4010 ns 11.2899 ns 10.3583 ns N/A 40.1530 ns N/A 10.1117 ns 11.2713 ns
matrix3 mul vector3 x1 4.7889 ns 4.4906 ns 4.3330 ns N/A 13.2860 ns N/A 4.7966 ns 4.4801 ns
matrix3 mul vector3 x100 0.5121 us 0.4669 us 0.4754 us N/A 1.348 us N/A 0.4767 us 0.4728 us
matrix3 return self 5.4364 ns 5.4451 ns 5.4552 ns N/A 5.4463 ns N/A 5.4450 ns 5.4534 ns
matrix3 transpose 10.0869 ns 10.1385 ns 10.0176 ns N/A 10.1395 ns N/A 10.8063 ns 9.7977 ns
matrix4 determinant 6.1510 ns 11.6457 ns 52.3414 ns 17.0240 ns 18.3800 ns N/A 16.9031 ns 8.5125 ns
matrix4 inverse 16.5764 ns 47.0562 ns 69.0789 ns 65.0189 ns 299.8796 ns N/A 52.1599 ns 42.0630 ns
matrix4 mul matrix4 7.5811 ns 26.6004 ns 8.2055 ns 11.5513 ns 91.5766 ns N/A 21.0343 ns 26.5077 ns
matrix4 mul vector4 x1 3.1131 ns 6.8211 ns 3.5017 ns N/A 23.9593 ns N/A 7.0599 ns 6.8278 ns
matrix4 mul vector4 x100 0.6175 us 0.768 us 0.6271 us N/A 2.26 us N/A 0.8465 us 0.7875 us
matrix4 return self 7.3269 ns 7.1310 ns 7.3162 ns N/A 7.3160 ns N/A 7.2881 ns 7.1189 ns
matrix4 transpose 7.4352 ns 12.0065 ns 14.8833 ns N/A 11.8665 ns N/A 16.1124 ns 12.6715 ns
ray-sphere intersection x10000 16.09 us 16.12 us 90.09 us 16.06 us 69.34 us N/A N/A 16.12 us
rotation3 inverse 2.2081 ns 3.4053 ns 7.6562 ns 3.3040 ns 3.3085 ns N/A 3.4015 ns 3.2964 ns
rotation3 mul rotation3 3.3522 ns 6.9520 ns 7.0618 ns 7.1581 ns 7.0768 ns N/A 7.5341 ns 7.0063 ns
rotation3 mul vector3 x1 6.5538 ns 7.4976 ns 7.9157 ns 7.5374 ns 17.6267 ns N/A 7.4405 ns 8.6141 ns
rotation3 mul vector3 x100 0.6592 us 0.7402 us 0.7623 us 0.7663 us 1.786 us N/A 0.742 us 0.8601 us
rotation3 return self 2.8756 ns 2.8689 ns 2.8714 ns N/A 2.8778 ns N/A 2.8630 ns 2.8725 ns
transform point2 x1 4.9832 ns 2.8866 ns 4.8093 ns 2.8645 ns 12.92

Extension points exported contracts — how you extend this code

FloatCompare (Interface)
Trait used by the `assert_approx_eq` macro for floating point comparisons. [29 implementers]
tests/support/mod.rs
BenchValue (Interface)
(no doc) [38 implementers]
src/lib.rs

Core symbols most depended-on inside this repo

random_angle_radians
called by 85
src/lib.rs
random_nonzero_f32
called by 47
src/lib.rs
random_mint_quat
called by 34
src/lib.rs
random_f32x4
called by 12
src/lib.rs
random_f32x8
called by 12
src/lib.rs
random_f32x16
called by 12
src/lib.rs
as_str
called by 10
tools/buildbench/src/main.rs
random_mint_vec3
called by 9
src/lib.rs

Shape

Function 227
Method 11
Class 2
Enum 2
Interface 2

Languages

Rust98%
Python2%

Modules by API surface

src/lib.rs110 symbols
tests/mat.rs20 symbols
tools/buildbench/src/main.rs14 symbols
tests/vec.rs12 symbols
benches/matrix4.rs12 symbols
benches/matrix3.rs12 symbols
benches/matrix2.rs12 symbols
benches/vector3.rs10 symbols
benches/transformations.rs8 symbols
benches/rotation3.rs8 symbols
tests/quat.rs5 symbols
scripts/summary.py5 symbols

For agents

$ claude mcp add mathbench-rs \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact