Skip to content

Commit b93b258

Browse files
Merge pull request #444 from Sergio0694/update/highperformance-docs
Updates to HighPerformance docs
2 parents 0f0e957 + 1c2bb5e commit b93b258

File tree

8 files changed

+59
-224
lines changed

8 files changed

+59
-224
lines changed

docs/high-performance/Introduction.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ This package can be installed through NuGet, and it has the following multi-targ
1414
- .NET Standard 2.1
1515
- .NET Core 2.1
1616
- .NET Core 3.1
17+
- .NET 5
1718

1819
This means that you can use it from anything from UWP or legacy .NET Framework applications, games written in Unity, cross-platform mobile applications using Xamarin, to .NET Standard libraries and modern .NET Core 2.1 or .NET Core 3.1 applications. The API surface is almost identical in all cases, and lots of work has been put into backporting as many features as possible to older targets like .NET Standard 1.4 and .NET Standard 2.0. Except for some minor differences, you can expect the same APIs to be available on all target frameworks.
1920

20-
The reason why multi-targeting has been used is to allow the package to leverage all the latest APIs on modern runtimes (like .NET Core 3.1) whenever possible, while still offering most of its functionalities to all target platforms. It also makes it possible for .NET Core 2.1 to use all the APIs that it has in common with .NET Standard 2.1, even though the runtime itself doesn't fully implement .NET Standard 2.1. All of this would not have been possible without multi-targeting to both .NET Standard as well as individual runtimes.
21+
The reason why multi-targeting has been used is to allow the package to leverage all the latest APIs on modern runtimes (like .NET 5) whenever possible, while still offering most of its functionalities to all target platforms. It also makes it possible for .NET Core 2.1 to use all the APIs that it has in common with .NET Standard 2.1, even though the runtime itself doesn't fully implement .NET Standard 2.1. All of this would not have been possible without multi-targeting to both .NET Standard as well as individual runtimes.
2122

2223
Follow these steps to install the High Performance package:
2324

@@ -45,15 +46,19 @@ As the name suggests, the High Performance package contains a set of APIs that a
4546

4647
This package makes heavy use of APIs such as:
4748

48-
- [System.Buffers.ArrayPool<T>](https://docs.microsoft.com/dotnet/api/system.buffers.arraypool-1)
49-
- [System.Runtime.CompilerServices.Unsafe](https://docs.microsoft.com/dotnet/api/system.runtime.compilerservices.unsafe)
50-
- [System.Runtime.InteropServices.MemoryMarshal](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.memorymarshal)
51-
- [System.Threading.Tasks.Parallel](https://docs.microsoft.com/dotnet/api/system.threading.tasks.parallel)
49+
- [`System.Span<T>`](https://docs.microsoft.com/dotnet/api/system.span-1)
50+
- [`System.Memory<T>`](https://docs.microsoft.com/dotnet/api/system.memory-1)
51+
- [`System.Buffers.ArrayPool<T>`](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.arraypool-1)
52+
- [`System.Runtime.CompilerServices.Unsafe`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.unsafe)
53+
- [`System.Runtime.InteropServices.MemoryMarshal`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.memorymarshal)
54+
- [`System.Threading.Tasks.Parallel`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallel)
5255
5356
If you are already familiar with these APIs or even if you're just getting started with writing high performance code in C# and want a set of well tested helpers to use in your own projects, have a look at what's included in this package to see how you can use it in your own projects!
5457

5558
## Where to start?
5659

5760
Here are some APIs you could look at first, if you were already using one of those types mentioned above:
58-
- [MemoryOwner<T>](MemoryOwner.md) and [SpanOwner<T>](SpanOwner.md), if you were using `System.Buffers.ArrayPool<T>`.
59-
- [ParallelHelper](ParallelHelper.md), if you were using `System.Threading.Tasks.Parallel`.
61+
- [`Span2D<T>`](Span2D.md) and [`Memory2D<T>`](Memory2D.md), for a `Span<T>` and `Memory<T>`-like abstraction over 2D memory
62+
- [`MemoryOwner<T>`](MemoryOwner.md) and [`SpanOwner<T>`](SpanOwner.md), if you were using `System.Buffers.ArrayPool<T>`.
63+
- [`StringPool`](StringPool.md), for an `ArrayPool<T>`-like type to cache `string` instances
64+
- [`ParallelHelper`](ParallelHelper.md), if you were using `System.Threading.Tasks.Parallel`.

docs/high-performance/Memory2D.md

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ dev_langs:
99

1010
# Memory2D&lt;T>
1111

12-
The [`Memory2D<T>`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.memory.memory2d-1) is a type that mirrors the functionality of the [`Memory<T>`](https://docs.microsoft.com/dotnet/api/system.memory-1) type, with the difference being that it can be used to represent 2D memory locations. It is extremely flexible and is capable of wrapping a number of different types, including ND arrays (with explicit support for 1D, 2D, and 3D arrays) or `Memory<T>` instances. This type is meant to be used together with the `Span2D<T>` type, in the same way that `Memory<T>` is used along with [`Span<T>`](https://docs.microsoft.com/dotnet/api/system.span-1). For more info on the key differences and use case scenarios of these two types, you can read [this docs page](https://docs.microsoft.com/dotnet/standard/memory-and-spans/memory-t-usage-guidelines).
12+
The [`Memory2D<T>`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.memory2d-1) is a type that mirrors the functionality of the [`Memory<T>`](https://docs.microsoft.com/dotnet/api/system.memory-1) type, with the difference being that it can be used to represent 2D memory locations. It is extremely flexible and is capable of wrapping a number of different types, including ND arrays (with explicit support for 1D, 2D, and 3D arrays) or `Memory<T>` instances. This type is meant to be used together with the [`Span2D<T>`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.memory.span2d-1) type, in the same way that `Memory<T>` is used along with [`Span<T>`](https://docs.microsoft.com/dotnet/api/system.span-1). For more info on the key differences and use case scenarios of these two types, you can read [this docs page](https://docs.microsoft.com/dotnet/standard/memory-and-spans/memory-t-usage-guidelines).
13+
14+
> **Platform APIs:** [`Memory2D<T>`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.memory2d-1), [`Span2D<T>`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.span2d-1), [`ReadOnlyMemory2D<T>`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.readonlymemory2d-1)
1315
1416
## How it works
1517

@@ -80,39 +82,10 @@ Span2D<int> span = memory.Span;
8082
8183
```
8284

83-
## Properties
84-
85-
| Property | Return Type | Description |
86-
| -- | -- | -- |
87-
| Empty | Memory2D&lt;T> | Gets an empty `Memory2D{T}` instance |
88-
| IsEmpty | bool | Gets a value indicating whether the current `Memory2D{T}` instance is empty |
89-
| Size | int | Gets the length of the current `Memory2D{T}` instance |
90-
| Height | int | Gets the height of the underlying 2D memory area |
91-
| Width | int | Gets the width of the underlying 2D memory area |
92-
| Span | Span2D&lt;T> | Gets a `Span2D{T}` instance from the current memory |
93-
94-
## Methods
95-
96-
| Method | Return Type | Description |
97-
| -- | -- | -- |
98-
| Slice(int, int, int, int) | Memory2D&lt;T> | Slices the current instance with the specified parameters |
99-
10085
## ReadOnlyMemory2D&lt;T>
10186

102-
The [ReadOnlyMemory2D&lt;T>](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.memory.readonlymemory2d-1) is to the `Memory2D<T>` type what `ReadOnlyMemory<T>` is to `Memory<T>`. It exposes the same exact functionalities (minus the APIs that involve modifying the contents of the wrapped memory area) and provides a read-only view to arbitrary 2D memory locations. For more info on how this type works, you can refer to the paragraph on the `Memory2D<T>` type above.
103-
104-
## Sample Code
105-
106-
You can find more examples in our [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.HighPerformance.Shared)
107-
108-
## Requirements
109-
110-
| Device family | Universal, 10.0 or higher |
111-
| --- | --- |
112-
| Namespace | Microsoft.Toolkit.HighPerformance |
113-
| NuGet package | [Microsoft.Toolkit.HighPerformance](https://www.nuget.org/packages/Microsoft.Toolkit.HighPerformance/) |
87+
The [`ReadOnlyMemory2D<T>`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.readonlymemory2d-1) is to the `Memory2D<T>` type what `ReadOnlyMemory<T>` is to `Memory<T>`. It exposes the same exact functionalities (minus the APIs that involve modifying the contents of the wrapped memory area) and provides a read-only view to arbitrary 2D memory locations. For more info on how this type works, you can refer to the paragraph on the `Memory2D<T>` type above.
11488

115-
## API
89+
## Examples
11690

117-
* [Memory2D&lt;T> source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.HighPerformance/Memory)
118-
* [ReadOnlyMemory2D&lt;T> source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.HighPerformance/Memory)
91+
You can find more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.HighPerformance.Shared).

docs/high-performance/MemoryOwner.md

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ dev_langs:
99

1010
# MemoryOwner&lt;T>
1111

12-
The [MemoryOwner&lt;T>](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.buffers.memoryowner-1) is a buffer type implementing [`IMemoryOwner<T>`](https://docs.microsoft.com/dotnet/api/system.buffers.imemoryowner-1), an embedded length property and a series of performance oriented APIs. It is essentially a lightweight wrapper around the [`ArrayPool<T>`](https://docs.microsoft.com/dotnet/api/system.buffers.arraypool-1) type, with some additional helper utilities.
12+
The [`MemoryOwner<T>`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.buffers.memoryowner-1) is a buffer type implementing [`IMemoryOwner<T>`](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.imemoryowner-1), an embedded length property and a series of performance oriented APIs. It is essentially a lightweight wrapper around the [`ArrayPool<T>`](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.arraypool-1) type, with some additional helper utilities.
13+
14+
> **Platform APIs:** [`MemoryOwner<T>`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.buffers.memoryowner-1), [`AllocationMode`](https://docs.microsoft.com/dotnet/api/microsoft.toolkit.highperformance.buffers.allocationmode)
1315
1416
## How it works
1517

1618
`MemoryOwner<T>` has the following main features:
1719

18-
- One of the main issues of arrays returned by the `ArrayPool<T>` APIs and of the `IMemoryOwner<T>` instances returned by the `MemoryPool<T>` APIs is that the size specified by the user is only being used as a _minum_ size: the actual size of the returned buffers might actually be greater. `MemoryOwner<T>` solves this by also storing the original requested size, so that [`Memory<T>`](https://docs.microsoft.com/dotnet/api/system.memory-1) and [`Span<T>`](https://docs.microsoft.com/dotnet/api/system.span-1) instances retrieved from it will never need to be manually sliced.
20+
- One of the main issues of arrays returned by the `ArrayPool<T>` APIs and of the `IMemoryOwner<T>` instances returned by the [`MemoryPool<T>`](https://docs.microsoft.com/dotnet/api/system.buffers.memorypool-1) APIs is that the size specified by the user is only being used as a _minum_ size: the actual size of the returned buffers might actually be greater. `MemoryOwner<T>` solves this by also storing the original requested size, so that [`Memory<T>`](https://docs.microsoft.com/en-us/dotnet/api/system.memory-1) and [`Span<T>`](https://docs.microsoft.com/en-us/dotnet/api/system.span-1) instances retrieved from it will never need to be manually sliced.
1921
- When using `IMemoryOwner<T>`, getting a `Span<T>` for the underlying buffer requires first to get a `Memory<T>` instance, and then a `Span<T>`. This is fairly expensive, and often unnecessary, as the intermediate `Memory<T>` might actually not be needed at all. `MemoryOwner<T>` instead has an additional `Span` property which is extremely lightweight, as it directly wraps the internal `T[]` array being rented from the pool.
2022
- Buffers rented from the pool are not cleared by default, which means that if they were not cleared when being previous returned to the pool, they might contain garbage data. Normally, users are required to clear these rented buffers manually, which can be verbose especially when done frequently. `MemoryOwner<T>` has a more flexible approach to this, through the `Allocate(int, AllocationMode)` API. This method not only allocates a new instance of exactly the requested size, but can also be used to specify which allocation mode to use: either the same one as `ArrayPool<T>`, or one that automatically clears the rented buffer.
2123
- There are cases where a buffer might be rented with a greater size than what is actually needed, and then resized afterwards. This would normally require users to rent a new buffer and copy the region of interest from the old buffer. Instead, `MemoryOwner<T>` exposes a `Slice(int, int)` API that simply return a new instance wrapping the specified area of interest. This allows to skip renting a new buffer and copying the items entirely.
@@ -79,7 +81,7 @@ Using this approach, buffers are now rented from a pool, which means that in mos
7981

8082
There are two main issues with the code above:
8183
- `ArrayPool<T>` might return buffers that have a size greater than the requested one. To work around this issue, we need to return a tuple which also indicates the actual used size into our rented buffer.
82-
- By simply returning an array, we need to be extra careful to properly track its lifetime and to return it to the appropriate pool. We might work around this issue by using [`MemoryPool<T>`](https://docs.microsoft.com/dotnet/api/system.buffers.memorypool-1) instead and by returning an `IMemoryOwner<T>` instance, but we still have the problem of rented buffers having a greater size than what we need. Additionally, `IMemoryOwner<T>` has some overhead when retrieving a `Span<T>` to work on, due to it being an interface, and the fact that we always need to get a `Memory<T>` instance first, and then a `Span<T>`.
84+
- By simply returning an array, we need to be extra careful to properly track its lifetime and to return it to the appropriate pool. We might work around this issue by using `MemoryPool<T>` instead and by returning an `IMemoryOwner<T>` instance, but we still have the problem of rented buffers having a greater size than what we need. Additionally, `IMemoryOwner<T>` has some overhead when retrieving a `Span<T>` to work on, due to it being an interface, and the fact that we always need to get a `Memory<T>` instance first, and then a `Span<T>`.
8385

8486
To solve both these issues, we can refactor this code again by using `MemoryOwner<T>`:
8587

@@ -98,35 +100,6 @@ public static MemoryOwner<byte> GetBytesFromFile(string path)
98100

99101
The returned `IMemoryOwner<byte>` instance will take care of disposing the underlying buffer and returning it to the pool when its [`IDisposable.Dispose`](https://docs.microsoft.com/dotnet/api/system.idisposable.dispose) method is invoked. We can use it to get a `Memory<T>` or `Span<T>` instance to interact with the loaded data, and then dispose the instance when we no longer need it. Additionally, all the `MemoryOwner<T>` properties (like `MemoryOwner<T>.Span`) respect the initial requested size we used, so we no longer need to manually keep track of the effective size within the rented buffer.
100102

101-
## Properties
102-
103-
| Property | Return Type | Description |
104-
| -- | -- | -- |
105-
| Length | int | Gets the number of items in the current instance |
106-
| Memory | System.Memory&lt;T> | Gets the memory belonging to this owner |
107-
| Span | System.Span&lt;T> | Gets a span wrapping the memory belonging to the current instance |
108-
| Empty | MemoryOwner&lt;T> | Gets an empty `MemoryOwner<T>` instance |
109-
110-
## Methods
111-
112-
| Method | Return Type | Description |
113-
| -- | -- | -- |
114-
| Allocate(int) | Memory&lt;T> | Creates a new `MemoryOwner<T>` instance with the specified parameters |
115-
| Allocate(int, AllocationMode) | Memory&lt;T> | Creates a new `MemoryOwner<T>` instance with the specified parameters |
116-
| DangerousGetReference() | ref T | Returns a reference to the first element within the current instance, with no bounds check |
117-
| Slice(int, int) | MemoryOwner&lt;T> | Slices the buffer currently in use and returns a new `MemoryOwner<T>` instance |
118-
119-
## Sample Code
120-
121-
You can find more examples in our [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.HighPerformance.Shared/Buffers)
122-
123-
## Requirements
124-
125-
| Device family | Universal, 10.0.16299.0 or higher |
126-
| --- | --- |
127-
| Namespace | Microsoft.Toolkit.HighPerformance |
128-
| NuGet package | [Microsoft.Toolkit.HighPerformance](https://www.nuget.org/packages/Microsoft.Toolkit.HighPerformance/) |
129-
130-
## API
103+
## Examples
131104

132-
* [MemoryOwner&lt;T> source code](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/Microsoft.Toolkit.HighPerformance/Buffers)
105+
You can find more examples in the [unit tests](https://github.com/Microsoft/WindowsCommunityToolkit//blob/master/UnitTests/UnitTests.HighPerformance.Shared/Buffers).

0 commit comments

Comments
 (0)