Skip to content

Commit

Permalink
Improve documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
aalmada committed Feb 6, 2024
1 parent e9ef10b commit cfb59cb
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Dealing with SIMD in .NET for optimized code can be complex, but this library offers a practical solution. It provides a reusable and highly-optimized iterations on `Span<T>`, enabling the application of both pre-defined and custom operations to each element.

Using generics, the library accommodates any type embracing [generic math](https://aalmada.github.io/Generic-math-in-dotnet.html).
Using generics, the library accommodates any type embracing [generic math](https://learn.microsoft.com/en-us/dotnet/standard/generics/math).

Within the library, you'll find pre-defined operations such as `Sqrt()`, `Sin()`, `Negate()`, `Add()`, `Divide()`, `Multiply()`, `AddMultiply()`, `Sum()`, `Average()`, and many more.

Expand Down
22 changes: 11 additions & 11 deletions docs/articles/Benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,25 @@ AMD Ryzen 9 7940HS w/ Radeon 780M Graphics, 1 CPU, 16 logical and 8 physical cor
Vector512 : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
```

It performs the following bechmarks

- `Baseline_*` - using a simple iteration without explicit optimizations.
- `LINQ_*` - using LINQ (when available).
- `System_*` - using `System.Numerics.Tensors` (only for `Single`; `float` in C#, or `float32` in F#).
- `NetFabric_*` - using `NetFabric.Numerics.Tensors`.

Every benchmark encompassed four distinct jobs:
Notice that this sytem supports vectorization up to 512 bits. The benchmarks will be performed without vectorization and for each vectorization size. Every benchmark encompassed four distinct jobs:

- `Scalar` - without any SIMD support
- `Vector128` - utilizing 128-bit SIMD support
- `Vector256` - utilizing 256-bit SIMD support
- `Vector512` - utilizing 512-bit SIMD support

It performs the following bechmarks:

- `Baseline_*` - using a simple iteration without explicit optimizations.
- `LINQ_*` - using LINQ (when available).
- `System_*` - using `System.Numerics.Tensors` (only for `Single`; `float` in C#, or `float32` in F#).
- `NetFabric_*` - using `NetFabric.Numerics.Tensors`.

The full benchmarking source code can be found [here](https://github.com/NetFabric/NetFabric.Numerics.Tensors/tree/main/src/NetFabric.Numerics.Tensors.Benchmarks).

### Add
### Addition

Benchmarks performing addition on two spans (tensors), each containing 1,000 items,
Benchmarks performing addition on two spans, each containing 1,000 items, outputing to another span.

The following serves as the baseline against which performance is evaluated:

Expand Down Expand Up @@ -214,7 +214,7 @@ It additionally compares with the performance of LINQ's `Sum()`. However, it's w

### Sum2D

Benchmarks performing the sum of the 2D vectors in a span (tensor), containing 1,000 vectors. The vector is a value type containing two fields of the same type.
Benchmarks performing the sum of the 2D vectors in a span, containing 1,000 vectors. The vector is a value type containing two fields of the same type.

It uses the same baseline as for the `Sum` benchmarks as it uses generics math to support any of these cases.

Expand Down
26 changes: 15 additions & 11 deletions docs/articles/Extending-the-library.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,13 @@ public interface ITernaryOperator<T1, T2, T3, TResult>

It's essential to note that these interfaces make use of [static virtual members](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/static-virtual-interface-members), a feature introduced in .NET 7. No instance of the operator is required to utilize the methods, and operators are pure, devoid of internal state.

Each operator must implement two methods that define the operation between elements of type `T` or vectors of type `Vector<T>`. Each interface specifies a different number of parameters.
Consider the square operator as an example, which functions as a unary operator designed to operate on a single source. The generic type `T` is restricted to `struct` and must implement `IMultiplyOperators<T, T, T`, indicating that only value types with the `*` operator implemented are suitable. The `Invoke` methods straightforwardly execute the square operation for either a single `T` value or a `Vector<T>` of values:
Each operator must implement two `Invoke` methods that define the operation between elements of type `T` or vectors of type `Vector<T>`. Each interface specifies a different number of parameters.

Consider the square operator as an example, which functions as a unary operator designed to operate on a single source. It implements the `IUnaryOperator<T>` interface. The generic type `T` is restricted to `struct` and must implement `IMultiplyOperators<T, T, T`, indicating that only value types with the `*` operator implemented are suitable. The `Invoke` methods straightforwardly execute the square operation for either a single `T` value or a `Vector<T>` of values:

```csharp
public readonly struct SquareOperator<T> : IUnaryOperator<T>
public readonly struct SquareOperator<T>
: IUnaryOperator<T>
where T : struct, IMultiplyOperators<T, T, T>
{
public static T Invoke(T x)
Expand All @@ -80,10 +82,11 @@ public readonly struct SquareOperator<T> : IUnaryOperator<T>
}
```

Similarly, consider an addition operator, a binary operator that works on two sources, the addends. The generic type `T` is confined to `struct` and must implement `IAdditionOperators<T, T, T>`, indicating that only value types with the `+` operator implemented are eligible. The `Invoke` methods straightforwardly perform the addition operation for either a single `T` value or a `Vector<T>` of values:
Similarly, consider an addition operator, a binary operator that works on two sources, the addends. It implements the `IBinaryOperator<T, T, T>` interface. The generic type `T` is confined to `struct` and must implement `IAdditionOperators<T, T, T>`, indicating that only value types with the `+` operator implemented are eligible. The `Invoke` methods straightforwardly perform the addition operation for either a single `T` value or a `Vector<T>` of values:

```csharp
readonly struct AddOperator<T> : IBinaryOperator<T, T, T>
readonly struct AddOperator<T>
: IBinaryOperator<T, T, T>
where T : struct, IAdditionOperators<T, T, T>
{
public static T Invoke(T x, T y)
Expand All @@ -94,10 +97,11 @@ readonly struct AddOperator<T> : IBinaryOperator<T, T, T>
}
```

Furthermore, consider an operator calculating the addition followed by multiplication of values. This is a ternary operator that handles three sources: the addends plus the multiplier. The generic type `T` is constrained to `struct`, `IAdditionOperators<T, T, T>`, and `IMultiplyOperators<T, T, T>`, indicating that only value types with the `+` and `*` operators implemented are applicable. The `Invoke` methods straightforwardly perform the addition operation followed by multiplication for either a single `T` value or a `Vector<T>` of values:
Furthermore, consider an operator calculating the addition followed by multiplication of values. This is a ternary operator that handles three sources: the two addends plus the multiplier. it implements the `ITernaryOperator<T, T, T, T>` interface. The generic type `T` is constrained to `struct`, `IAdditionOperators<T, T, T>`, and `IMultiplyOperators<T, T, T>`, indicating that only value types with the `+` and `*` operators implemented are applicable. The `Invoke` methods straightforwardly perform the addition operation followed by multiplication for either a single `T` value or a `Vector<T>` of values:

```csharp
readonly struct AddMultiplyOperator<T> : ITernaryOperator<T, T, T, T>
readonly struct AddMultiplyOperator<T>
: ITernaryOperator<T, T, T, T>
where T : struct, IAdditionOperators<T, T, T>, IMultiplyOperators<T, T, T>
{
public static T Invoke(T x, T y, T z)
Expand Down Expand Up @@ -126,9 +130,9 @@ public interface IAggregationOperator<T, TResult>
}
```

Each operator must implement a property that returns the identity value for the operation, which initializes the aggregation process. Additionally, operators must implement the two methods required by the `IBinaryOperator` interface, along with two additional methods that aggregate the final result.
Each operator must implement a property that returns the identity value for the operation, which initializes the aggregation process. Additionally, operators must implement the two `Invoke` methods required by the `IBinaryOperator<T, T, T>` interface, along with two additional `Invoke` methods that aggregate the final result.

Consider, for instance, an operator that calculates the sum of all elements in the source. This serves as an aggregation operator, providing a value. The generic type `T` is restricted to `struct`, `IAdditiveIdentity<T, T>`, and `IAdditionOperators<T, T, T>`, signifying that only value types with both the additive identity and the `+` operator implemented are suitable. The `Identity` initializes the sum using the additive identity. The `Invoke` methods handle the addition of `T` and `Vector<T>` values.
Consider, for instance, an operator that calculates the sum of all elements in the source. This serves as an aggregation operator, providing a value. It implements the `IAggregationOperator<T, T>` interface. The generic type `T` is restricted to `struct`, `IAdditiveIdentity<T, T>`, and `IAdditionOperators<T, T, T>`, signifying that only value types with both the additive identity and the `+` operator implemented are suitable. The `Identity` initializes the sum using the additive identity. The `Invoke` methods handle the addition of `T` and `Vector<T>` values.

```csharp
readonly struct SumOperator<T> : IAggregationOperator<T, T>
Expand All @@ -148,9 +152,9 @@ readonly struct SumOperator<T> : IAggregationOperator<T, T>
}
```

## Operators Incompatible with Vectorization
## Operators Unsuitable for Vectorization

It's essential to recognize that specific operators may have partial or no support when it comes to `Vector<T>`.
It's crucial to acknowledge that certain operators may lack full compatibility or support with `Vector<T>`. While alternative optimizations can still be applied, vectorization will have to be disabled for these operations.

All operator interfaces inherit from the following interface:

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/Getting-started.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Getting Started with NetFabric.Numerics.Tensors

To kick off your exploration of NetFabric.Numerics.Tensors, grab it conveniently from NuGet. Embed it as a dependency in your project, either by executing the command line steps (outlined on the NuGet page) or through your preferred dependency manager.
To kick off your exploration of NetFabric.Numerics.Tensors, grab it from [NuGet](https://www.nuget.org/packages/NetFabric.Numerics.Tensors). Embed it as a dependency in your project, either by executing the command line steps ([outlined on the NuGet page](https://www.nuget.org/packages/NetFabric.Numerics.Tensors)) or through your preferred dependency manager.

Import the library into your source code files as needed – include `using NetFabric.Numerics.Tensors;` in your C# files or `open NetFabric.Numerics.Tensors` if you're working in F#.

Expand Down

0 comments on commit cfb59cb

Please sign in to comment.