Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,21 @@
<PropertyGroup>
<Authors>John Kelly and Contributors</Authors>
<BaseOutputPath>$(BaseArtifactsPath)bin/$(BaseArtifactsPathSuffix)/</BaseOutputPath>
<Company>johnkellyoxford</Company>
<Company>John Kelly</Company>
<PackageOutputPath>$(BaseArtifactsPath)pkg/$(BaseArtifactsPathSuffix)/$(Configuration)/</PackageOutputPath>
<Product>MathSharp</Product>
<VersionPrefix>0.0.1</VersionPrefix>
<VersionSuffix>alpha</VersionSuffix>
<VersionPrefix>2.0.0</VersionPrefix>
<VersionSuffix>pre</VersionSuffix>
</PropertyGroup>

<!-- Default settings that are otherwise undefined -->
<PropertyGroup>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)MathSharp.snk</AssemblyOriginatorKeyFile>
<Copyright>Copyright � John Kelly and Contributors</Copyright>
<Description>A high speed vector maths library</Description>
<Copyright>Copyright (C) John Kelly and Contributors</Copyright>
<Description>MathSharp is a vector and matrix library written in C# using hardware intrinsics. Thanks to hardware acceleration, MathSharp is significantly faster than most mathematics libraries out there.</Description>
<Features>strict</Features>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<HighEntropyVA>true</HighEntropyVA>
<LangVersion>preview</LangVersion>
<MathSharpPublicKey>00240000048000009400000006020000002400005253413100040000010001000586cd22205c2991de00dea27c3455744874d5b9c4532ccee2bf8fa9b55752af42fa6170f641c20d1501cd3b9e6d22a1327cdaf19eb8eba23e8ed350a60930e7286417916e3a3a39798e69d8d521ac3af4c720cbcca2f2fbf5cee2c0b1c6be08ebba9051399148c663c3408513be9a2ff96e7816bb1b3713d1f30e13fa2e66a9</MathSharpPublicKey>
<NeutralLanguage>en-GB</NeutralLanguage>
<OverwriteReadOnlyFiles>true</OverwriteReadOnlyFiles>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand All @@ -74,4 +72,4 @@
<PackageReference Include="Microsoft.Net.Compilers.Toolset" IsImplicitlyDefined="true" />
</ItemGroup>

</Project>
</Project>
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# MathSharp

# Note: As of 2022 MathSharp is not recommended for general use
# Since its inception, the .NET intrinsics and vectors have increased in performance and functionality massively, and they are recommended for any new projects

[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/johnhk)
[![Buy Me A Coffee](/assets/buymecoffee.png)](https://www.buymeacoffee.com/johnhk)

[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/john-h-k/MathSharp/blob/master/LICENSE)
[![GitHub issues](https://img.shields.io/github/issues/john-h-k/MathSharp.svg)](https://GitHub.com/john-h-k/MathSharp/issues/)
[![GitHub stars](https://img.shields.io/github/stars/john-h-k/MathSharp.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/john-h-k/MathSharp/stargazers/)



|Configuration|Windows x86|Windows x64|Ubuntu 1604 x64|Mac OS x64|
Expand Down Expand Up @@ -122,7 +123,8 @@ Vector4 + Vector4;

### Results (within margin of error between MathSharp and System.Numerics)

![Vector Addition Benchmark](assets/Benchmarks/VectorAdditionBenchmark-barplot.png)
<!--![Vector Addition Benchmark](assets/Benchmarks/VectorAdditionBenchmark-barplot.png)-->
<img src="assets/Benchmarks/VectorAdditionBenchmark-barplot.png" width="500" height="500">

| Method | Mean | Error | StdDev |
|--------------- |----------:|----------:|----------:|
Expand Down
97 changes: 97 additions & 0 deletions docs/Comparisons-And-Selections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Comparisons

MathSharp offers all the comparisons you'd expect - but they don't necessarily work in the way you'd expect from working with other libraries.
Most comparison results return the same type you give them, rather than `bool` or multiple `bool`s. You might find this confusing, but it'll make sense soon.

Let's start with the simple comparisons. You want to check if all or any elements of a vector are true or false for a comparison. This is really easily achieved with extension methods.
For example - test if 2 vectors are equal:

```cs
public bool AreEqual(Vector128<float> left, Vector128<right> right)
{
var comp = Vector.CompareEqual(left, right);
return comp.AllTrue(); // returns true if all elements in the comparison are true, else false
}
```

Tada! It's that simple. The 4 simplest comparison extensions are:

```cs
AllTrue() - true if all elements of the comparison are true
AllFalse() - true if all elements of the comparison are false
AllFalse() - true if any elements of the comparison are true
AnyFalse() - true if any elements of the comparison are false
```

First, a quick brush up on bitwise operations. A bitwise `and`/`&`, results in a bit being set if both inputs are set. Else it is zero.
A bitwise `or`/`|`, results in a bit being set if either or both inputs are set. If both are zero, it is zero. E.g

```cs
// 0b indicates a binary literal. & is and, | is or
0b_1 & 0b_1 -> 1
0b_0 & 0b_1 -> 0
0b_1 & 0b_0 -> 0
0b_0 & 0b_0 -> 0

0b_1 | 0b_1 -> 1
0b_0 | 0b_1 -> 1
0b_1 | 0b_0 -> 1
0b_0 | 0b_0 -> 0
```

Also remember that all values on your computer are binary. Floating point types are complex, but they're still a chunk of binary data and nothing more.
So, let's give an example operation we want to do. Say we have a vector, and we want to multiply all numbers on it by another vector, if they are equal to the numbers in the other vector, else, we want to square root them.
E,g `<99, -5, 6, -2.3>, <99, 7, 0.1, -2.3>` would become `<99 * 99, Sqrt(-5), Sqrt(6), -2.3 * -2.3>`. Let's write this using `System.Numerics`

```cs
public Vector4 Foo(Vector4 left, Vector4 right)
{
var mul = left * right;
var sqrt = Vector4.SquareRoot(mul);

// No way to vectorise this :()
var x = left.X == right.X ? left.X * right.X : MathF.Sqrt(left.X);
var y = left.Y == right.Y ? left.Y * right.Y : MathF.Sqrt(left.Y);
var z = left.Z == right.Z ? left.Z * right.Z : MathF.Sqrt(left.Z);
var w = left.W == right.W ? left.W * right.W : MathF.Sqrt(left.W);
return new Vector4(x, y, z, w);
}
```

On my system, this takes about 3 nanoseconds. Not bad eh!

Now let's look at MathSharp implementation:

```cs
public Vector128<float> Foo(Vector128<float> left, Vector128<float> right)
{
var comp = Vector.CompareEqual(left, right);
var mul = Vector.Multiply(left, right);
var sqrt = Vector.Sqrt(mul);

return Vector.Select(sqrt, mul, comp);
}
```

Again on my system, this takes 0.3 nanoseconds. That's a 10x speedup!
So, what does it do?

First, it does the comparison. `Vector.CompareEqual` returns a `Vector128<float>`, as mentioned above. However, what's important is what is in that return.
Each element is either all-1s or all-0s as binary. This means, that you can use them as masks. For example, to select all elements of a vector that are equal to another, and zero the rest, you just do

```cs
Vector.And(left, Vector.CompareEqual(left, right));
```

E.g, with `<1, 2, 3, 4>` and `<1, 3, 3, 5>`, the comparison returns `<0xFFFFFFFF, 0, 0xFFFFFFFF, 0>`. When we perform the `and`, as we learnt above, the elements with 0 are discarded, and the ones with `0xFFFFFFFF` (which is all bits set) are kept.

Given the commonness of this operation, MathSharp provides 3 selection helper methods:

```cs
SelectWhereTrue(selector, vector)
SelectWhereFalse(selector, vector)
Select(selector, left, right)
```

`SelectWhereTrue` and `SelectWhereFalse` are reasonably self explanatory. They select elements from `vector` which correspond to `true` elements of the `selector` (usually created with a `Vector.Compare...` method).
`Select` works like a ternary expression, selecting elements from `left` where `selector` is true, and from `right` where `selector` is `false`. In the above example, we select the multiplied value when the values are equal, else we select the square root value.
66 changes: 66 additions & 0 deletions docs/Introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# What is MathSharp

MathSharp is a library designed around blazing-fast, platform-agnostic linear algebra and 3D maths.

## What are the advantages of MathSharp over its competitors?

MathSharp is faster! Check our [benchmarks](https://github.com/john-h-k/MathSharp/blob/master/README.md). We offer a more complete feature set than `System.Numerics`, with faster inclusion of new features (such as colour and collision support, which are currently in the works). We also offer fully-fledged double-precision vector, quaternion, and matrix types in .NET Core 3.0, which `System.Numerics` doesn't.

## That sounds great, but what does using MathSharp look like?

Let's take a relatively simple example. You have some code, using the `System.Numeric`s types, that just finds the cross product of 2 pairs of vectors, then finds the dot of that.

```cs
public void DotAndCross(Vector3 left0, Vector3 left1, Vector3 right0, Vector3 right1, out Vector3 result)
{
var leftDot = new Vector3(Vector3.Dot(left0, left1));
var rightDot = new Vector3(Vector3.Dot(right0, right1));
result = Vector3.Cross(leftDot, rightDot);
}
```

So, the first step is take the incoming `Vector3` params by reference. This isn't always necessary, but it allows you to take advantage of better loading on some platforms.

Then, we convert the `System.Numerics` types to the native SIMD types by using MathSharp's extension methods. `Load()` takes a `Vector3`, and returns a `Vector128<float>`.
We then use the static methods in `MathSharp.Vector` to perform our operations. Here, we want to perform a 3D dot and cross.

We then finally use the `Vector.Store` method to store out SIMD type into our storage type.

```cs
using MathSharp; // Include at the top

public void DotAndCross(in Vector3 left0, in Vector3 left1, in Vector3 right0, in Vector3 right1, out Vector3 result)
{
var leftDot = Vector.Dot3D(left0.Load(), left1.Load());
var rightDot = Vector.Dot3D(right0.Load(), right1.Load());

Vector.Store(Vector.Cross3D(leftDot, rightDot), out result);
}
```

Tada! You've just used MathSharp without even making a breaking change. This is still a great way to use it - but not the best.

The idiomatic, fastest way to use MathSharp, is to pass around the SIMD types as arguments and returns, and use the so-called storage types - such as `System.Numerics` and `OpenTk.Maths` types, as well as your custom storage types, for fields and members of other types. For more information about this as well as creating your own storage types, check out [storage](storage.md). For example, rewriting the above method to be idiomatic MathSharp would be

```cs
using MathSharp; // Include at the top

public Vector128<float> DotAndCross(Vector128<float> left0, Vector128<float> left1, Vector128<float> right0, Vector128<float> right1)
{
var leftDot = Vector.Dot3D(left0, left1);
var rightDot = Vector.Dot3D(right0, right1);

return Vector.Cross3D(leftDot, rightDot);
}
```

The key MathSharp types are:

* `Vector` - a static class containing methods to work with vectors
* `Matrix` - a static class containing methods to work with matrixes
* `Quaternion` - a static class containing methods to work with quaternions
* `MatrixSingle` - a struct used to represent a single-precision 4x4 matrix SIMD type

Comparisons in MathSharp are extremely powerful - allowing you to perform a far wide variety
of comparison and selection operations, and to use those comparisons in ways that other libraries simply don't. If you've ever worked with SIMD before, these comparisons will be intuitive to you. If not, they're still easy to learn, and unlock a wide range of operations. Check out our [comparisons](comparisons-and-selections.md) doc for more.

27 changes: 27 additions & 0 deletions docs/Storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Storage

MathSharp operations are designed to occur with locals and arguments, which are located on the stack or SIMD registers, allowing them to be fast and efficient. Because of this,
the types worked with are generally larger than you would expect or have specific alignment requirements, and don't distinguish between different dimensions. Generally, you should use other types
for fields and members of types, and then use SIMD types for passing parameters or returns. This will result in the best performance and the minimum number of conversions between storage and SIMD types.

By default, MathSharp supports using the `System.Numerics` types as storage types, and provides `Load()` extension methods in `Vector, Matrix, Quaternion`, and `Store(out T store)` methods too.
Writing your own storage types is trivial. Use the `FromVector` and `ToVector` methods to convert the types. As an example, here is a custom Vector3 storage type.

```cs
public struct MyVec3
{
public float X, Y, Z;

public Vector128<float> Load() => Vector.FromVector3D(in x);
}

public static class MyVec3Extensions
{
public static void Store(this Vector128<float> vector, out MyVec3 vec3) => Vector.ToVector3D(vector, vec3);
}
```

You can use the aligned variants of these methods (e.g `FromVector3DAligned`) if you can guarantee the passed value is always 16 byte aligned for `float`s, and 32 byte aligned for `double`s.

Passing by `in` is very important, because it ensures you are actually passing a reference. We don't take `ref` because we want to allow readonly members to be passed.
The load and from methods also have pointer overloads. Where these can be called without pinning, they should be used for potential performance benefits.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace MathSharp.Interactive.Benchmarks.Vector.Single
{
using Vector = MathSharp.Vector;
using Vector3 = OpenTK.Vector3;
using Vector3 = System.Numerics.Vector3;

public class CameraClassBenchmark
{
Expand All @@ -21,7 +21,7 @@ public class CameraClassBenchmark
[GlobalSetup]
public void Setup()
{
_pitch = 11f;
_pitch = 11f;
_yaw = 0.008888f;
_unitY = Vector128.Create(0f, 1f, 0f, 0f);
_unitYWrapper = _unitY;
Expand All @@ -38,12 +38,27 @@ public Vector128<float> MathSharp()
{
Vector128<float> front = _mathSharpFront;
front = Vector.Normalize3D(front);
Vector128<float> right = Vector.Normalize3D(Vector.CrossProduct3D(_unitY, front));
Vector128<float> right = Vector.Normalize3D(Vector.Cross3D(_unitY, front));

Vector128<float> up = Vector.Normalize3D(Vector.CrossProduct3D(front, right));
Vector128<float> up = Vector.Normalize3D(Vector.Cross3D(front, right));
return up;
}

public void DotAndCross(Vector3 left0, Vector3 left1, Vector3 right0, Vector3 right1, out Vector3 result)
{
var leftDot = new Vector3(Vector3.Dot(left0, left1));
var rightDot = new Vector3(Vector3.Dot(right0, right1));
result = Vector3.Cross(leftDot, rightDot);
}

public void DotAndCross(in Vector3 left0, in Vector3 left1, in Vector3 right0, in Vector3 right1, out Vector3 result)
{
var leftDot = Vector.Dot3D(left0.Load(), left1.Load());
var rightDot = Vector.Dot3D(right0.Load(), right1.Load());

Vector.Store(Vector.Cross3D(leftDot, rightDot), out result);
}

[Benchmark]
public Vector3 OpenTkMath()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using BenchmarkDotNet.Attributes;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using System.Text;

using MVector = MathSharp.Vector;

namespace MathSharp.Interactive.Benchmarks.Vector.Single
{
public class SelectionBenchmark
{
private Vector4 _value = new Vector4(99, -5, 6, -2.3f);
private Vector4 _multiply = new Vector4(99, 7, 0.1f, -2.3f);


private Vector128<float> _value1 = Vector128.Create(99, -5, 6, -2.3f);
private Vector128<float> _multiply1 = Vector128.Create(99, 7, 0.1f, -2.3f);

[Benchmark]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public Vector4 SysNumeric() => Foo(_value, _multiply);

[Benchmark]
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public Vector128<float> MathSharp() => Foo(_value1, _multiply1);

[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public Vector4 Foo(Vector4 left, Vector4 right)
{
var mul = left * right;
var sqrt = Vector4.SquareRoot(mul);
// No way to vectorise this :(
var x = left.X == right.X ? left.X * right.X : sqrt.X;
var y = left.Y == right.Y ? left.Y * right.Y : sqrt.Y;
var z = left.Z == right.Z ? left.Z * right.Z : sqrt.Z;
var w = left.W == right.W ? left.W * right.W : sqrt.W;
return new Vector4(x, y, z, w);
}

//[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
//public static Vector128<float> MulNegAndAbs_MathSharp(Vector128<float> value, Vector128<float> multiply)
//{
// var mul = Sse.Multiply(Sse.Xor(value, Vector128.Create(int.MinValue).AsSingle()), multiply);
// mul = Sse.Max(mul, value);
// return Sse.And(mul, Vector128.Create(~int.MinValue).AsSingle());
//}

[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public Vector128<float> Foo(Vector128<float> left, Vector128<float> right)
{
var comp = MVector.CompareEqual(left, right);
var mul = MVector.Multiply(left, right);
var sqrt = MVector.Sqrt(mul);

return MVector.Select(comp, mul, sqrt);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BenchmarkDotNet.Attributes;
using OpenTK;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand Down
6 changes: 3 additions & 3 deletions samples/MathSharp.Interactive/MathSharp.Interactive.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<PropertyGroup>
<NoWarn>1573;1591;NU1701;CS0649;CS1591;</NoWarn>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<LangVersion>preview</LangVersion>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>8.0</LangVersion>
<SignAssembly>false</SignAssembly>
</PropertyGroup>

Expand All @@ -17,7 +17,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="OpenTK" Version="3.0.1" />
</ItemGroup>

Expand Down
Loading