Skip to content

Commit f25baf3

Browse files
committedDec 22, 2021
ReadMe
1 parent 8249f8c commit f25baf3

File tree

1 file changed

+141
-1
lines changed

1 file changed

+141
-1
lines changed
 

‎README.md

+141-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,141 @@
1-
LargeArray
1+
NativeMemoryArray
2+
===
3+
[![GitHub Actions](https://github.com/Cysharp/NativeMemoryArray/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/NativeMemoryArray/actions) [![Releases](https://img.shields.io/github/release/Cysharp/NativeMemoryArray.svg)](https://github.com/Cysharp/NativeMemoryArray/releases)
4+
5+
NativeMemoryArray is a native-memory backed array for .NET and Unity. The array size of C# is limited to maximum index of 0x7FFFFFC7(2,147,483,591), [Array.MaxLength](https://docs.microsoft.com/en-us/dotnet/api/system.array.maxlength). In terms of `bytes[]`, it is about 2GB. This is very cheep in the modern world. We handle the 4K/8K videos, large data set of deep-learning, huge 3D scan data of point cloud, etc.
6+
7+
`NativeMemoryArray<T>` provides the native-memory backed array, it supports infinity length, `Span<T>` and `Memory<T>` slices, `IBufferWriter<T>`, `ReadOnlySeqeunce<T>` and .NET 6's new Scatter/Gather I/O API.
8+
9+
For example, easy to read huge data in-memory.
10+
11+
```csharp
12+
// for example, load large file.
13+
using var handle = File.OpenHandle("4GBfile.bin", FileMode.Open, FileAccess.Read, options: FileOptions.Asynchronous);
14+
var size = RandomAccess.GetLength(handle);
15+
16+
// via .NET 6 Scatter/Gather API
17+
using var array = new NativeMemoryArray<byte>(size);
18+
await RandomAccess.ReadAsync(handle, array.AsMemoryList(), 0);
19+
```
20+
21+
For example, easy to read/write huge data in streaming via `IBufferWriter<T>`, `MemorySequence`.
22+
23+
```csharp
24+
public static async Task ReadFromAsync(NativeMemoryArray<byte> buffer, Stream stream, CancellationToken cancellationToken = default)
25+
{
26+
var writer = buffer.CreateBufferWriter();
27+
28+
int read;
29+
while ((read = await stream.ReadAsync(writer.GetMemory(), cancellationToken).ConfigureAwait(false)) != 0)
30+
{
31+
writer.Advance(read);
32+
}
33+
}
34+
35+
public static async Task WriteToAsync(NativeMemoryArray<byte> buffer, Stream stream, CancellationToken cancellationToken = default)
36+
{
37+
foreach (var item in buffer.AsMemorySequence())
38+
{
39+
await stream.WriteAsync(item, cancellationToken);
40+
}
41+
}
42+
```
43+
44+
Even if you don't need to deal with huge data, this uses native-memory, so it doesn't use the C# heap. If you are in a situation where you can manage the memory properly, you will have a performance advantage.
45+
46+
Getting Started
47+
---
48+
For .NET, use NuGet. For Unity, please read [Unity](#Unity) section.
49+
50+
PM> Install-Package [NativeMemoryArray](https://www.nuget.org/packages/NativeMemoryArray)
51+
52+
NativeMemoryArray provides only simple `Cysharp.Collections.NativeMemoryArray<T>` class. It has `where T : unmanaged` constraint so you can only use struct that not includes reference type.
53+
54+
The difference with `Span<T>` is that `NativeMemoryArray<T>` itself is a class, so it can be placed in a field. This means that, unlike `Span<T>`, it is possible to ensure some long lifetime. Since you can make a slice of `Memory<T>`, you can also throw it into Async methods. Also, the length limit of `Span<T>` is up to int.MaxValue (roughly 2GB), however `NativeMemoryArray<T>` can be larger than that.
55+
56+
The main advantages are as follows
57+
58+
* Allocates from native memory, so it does not use the C# heap.
59+
* There is no limit of 2GB, and infinite length can be allocated as long as memory allows.
60+
* Can pass directly via `IBufferWriter<T>` to `MessagePackSerializer`, `System.Text.Json.Utf8JsonWriter`, `System.IO.Pipelines`, etc.
61+
* Can pass directly via `ReadOnlySequence<T>` to `Utf8JsonWriter`, `System.IO.Pipelines`, etc.
62+
* Can pass huge data directly via `IReadOnlyList<(ReadOnly)Memory<T>>` to `RandomAccess` (Scatter/Gather API).
63+
64+
All `NativeMemoryArray<T>` APIs are as follows
65+
66+
* `NativeMemoryArray(long length, bool skipZeroClear = false)`
67+
* `long Length`
68+
* `ref T this[long index]`
69+
* `ref T GetPinnableReference()`
70+
* `Span<T> AsSpan()`
71+
* `Span<T> AsSpan(long start)`
72+
* `Span<T> AsSpan(long start, int length)`
73+
* `Memory<T> AsMemory()`
74+
* `Memory<T> AsMemory(long start)`
75+
* `Memory<T> AsMemory(long start, int length)`
76+
* `bool TryGetFullSpan(out Span<T> span)`
77+
* `IBufferWriter<T> CreateBufferWriter()`
78+
* `SpanSequence AsSpanSequence(int chunkSize = int.MaxValue)`
79+
* `MemorySequence AsMemorySequence(int chunkSize = int.MaxValue)`
80+
* `IReadOnlyList<Memory<T>> AsMemoryList(int chunkSize = int.MaxValue)`
81+
* `IReadOnlyList<ReadOnlyMemory<T>> AsReadOnlyMemoryList(int chunkSize = int.MaxValue)`
82+
* `ReadOnlySequence<T> AsReadOnlySequence(int chunkSize = int.MaxValue)`
83+
* `SpanSequence GetEnumerator()`
84+
* `void Dispose()`
85+
86+
`NativeMemoryArray<T>` allocates memory by [NativeMemory.Alloc/AllocZeroed](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.nativememory) so you need to call `Dispose()` or use `using scope`. In the default, allocated memory is zero-cleared. You can configure via `bool skipZeroClear`.
87+
88+
`AsSpan()` and `AsMemory()` are APIs for Slice. Returned `Span` and `Memory` possible to allow write operation so you can pass to the Span operation methods. `Span` and `Memory` have limitation of length(int.MaxValue) so if length is omitted, throws exception if array is larger. Using `TryGetFullSpan()` detect can get single full span or not. `AsSpanSequence()` and `AsMemorySequence()` are iterate chunked all data via foreach. Using foreach directly as same as `AsSpanSequence()`.
89+
90+
```csharp
91+
long written = 0;
92+
foreach (var chunk in array)
93+
{
94+
// do anything
95+
written += chunk.Length;
96+
}
97+
```
98+
99+
Getting a pointer is almost the same as getting an array. It can be passed as is or with an indexer.
100+
101+
```csharp
102+
// buffer = NativeArray<byte>
103+
104+
fixed (byte* p = buffer)
105+
{
106+
}
107+
108+
fixed (byte* p = &buffer[42])
109+
{
110+
}
111+
```
112+
113+
`CreateBufferWriter()` allows you to get an `IBufferWriter<T>`. This can be passed directly to `MessagePackSerializer.Serialize`, etc., or used in cases such as reading from a `Stream`, where it is retrieved and written chunk by chunk from the beginning.
114+
115+
The `ReadOnlySequence<T>` you can get with `AsReadOnlySequence()` can be passed directly to `MessagePackSerializer.Deserialize`, and [SequenceReader](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.sequencereader-1) is useful to processing large data via streaming.
116+
117+
`AsMemoryList()` and `AsReadOnlySequence()` are convinient data structure for [RandomAccess](https://docs.microsoft.com/en-us/dotnet/api/system.io.randomaccess).`Read/Write` API.
118+
119+
For the simple buffer processing, we provide some utility extension methods.
120+
121+
```csharp
122+
public static async Task ReadFromAsync(this NativeMemoryArray<byte> buffer, Stream stream, IProgress<int>? progress = null, CancellationToken cancellationToken = default)
123+
public static async Task WriteToFileAsync(this NativeMemoryArray<byte> buffer, string path, FileMode mode = FileMode.Create, IProgress<int>? progress = null, CancellationToken cancellationToken = default)
124+
public static async Task WriteToAsync(this NativeMemoryArray<byte> buffer, Stream stream, int chunkSize = int.MaxValue, IProgress<int>? progress = null, CancellationToken cancellationToken = default)
125+
```
126+
127+
This utility is excluded the .NET Standard 2.0 environment since runtime API limitation.
128+
129+
Unity
130+
---
131+
You can install via UPM git URL package or asset package(NativeMemoryArray.*.unitypackage) available in [NativeMemoryArray/releases](https://github.com/Cysharp/NativeMemoryArray/releases) page.
132+
133+
* `https://github.com/Cysharp/NativeMemoryArray.git?path=src/NativeMemoryArray.Unity/Assets/Plugins/NativeMemoryArray`
134+
135+
NativeMemoryArray requires `System.Memory.dll`, `System.Buffer.dll`, `System.Runtime.CompilerServices.Unsafe.dll`. It is not included in git URL so you need get from others or install via .unitypackage only once.
136+
137+
The difference between `NativeArray<T>` and `NativeArray<T>` in Unity is that `NativeArray<T>` is a container for efficient interaction with the Unity Engine(C++) side. `NativeMemoryArray<T>` has a different role because it is for C# side only.
138+
139+
License
140+
---
141+
This library is licensed under the MIT License.

0 commit comments

Comments
 (0)