Skip to content

Commit

Permalink
Update docs, config, and error handling; adjust styles
Browse files Browse the repository at this point in the history
Updated documentation links from absolute URLs to `xref` links for better integration with the documentation system. Modified the `docfx.json` configuration to include a `sitemap` section and updated the `outputFormat` to `mref`. Changed the `MemoryStreamSlim` class to throw a `NotSupportedException` for the `GetBuffer` method in expandable mode. Added a new method `ThrowNotSupportedException_FeatureNotSupported` in `ThrowHelper` to handle unsupported feature exceptions. Updated the solution file to reflect changes in the project structure, including the removal of some GitHub and WorkFlows project entries. Updated CSS file paths in the solution file to reflect the new custom template directory. Added a new localized string `NotSupported_FeatureNotAvailable` in `Strings.Designer.cs`. Added a new CSS rule in `main.css` to style an image with the ID `logo`, setting its height to 2rem and its right margin to 3rem.
  • Loading branch information
kzdev-net committed Oct 13, 2024
1 parent a644400 commit c33c92b
Show file tree
Hide file tree
Showing 15 changed files with 49 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The following parameters were used in the benchmarks. These will appear as colum

#### ZeroBuffers

When `true`, the stream is created with the option to zero out memory buffers when they are no longer used. When `false`, the stream is created with the option to not zero out memory buffers specified. For the `MemoryStreamSlim` class, the [`ZeroBufferBehavior`](/api/KZDev.PerfUtils.MemoryStreamSlimOptions.ZeroBufferBehavior.html) option is set to `OnRelease` to provide a fair comparison to the other classes.
When `true`, the stream is created with the option to zero out memory buffers when they are no longer used. When `false`, the stream is created with the option to not zero out memory buffers specified. For the `MemoryStreamSlim` class, the [`ZeroBufferBehavior`](xref:KZDev.PerfUtils.MemoryStreamSlimOptions.ZeroBufferBehavior) option is set to `OnRelease` to provide a fair comparison to the other classes.

The `MemoryStream` class has no option to zero out memory buffers (used memory is always cleared), so this parameter does not apply to that class.

Expand Down
2 changes: 1 addition & 1 deletion Source/Docs/articles/dynamic-throughput-benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ When `true`, the stream is instantiated with the current loop iteration data siz

#### ZeroBuffers

When `true`, the stream is created with the option to zero out memory buffers when they are no longer used. When `false`, the stream is created with the option to not zero out memory buffers specified. For the `MemoryStreamSlim` class, the [`ZeroBufferBehavior`](/api/KZDev.PerfUtils.MemoryStreamSlimOptions.ZeroBufferBehavior.html) option is set to `OnRelease` to provide a fair comparison to the other classes.
When `true`, the stream is created with the option to zero out memory buffers when they are no longer used. When `false`, the stream is created with the option to not zero out memory buffers specified. For the `MemoryStreamSlim` class, the [`ZeroBufferBehavior`](xref:KZDev.PerfUtils.MemoryStreamSlimOptions.ZeroBufferBehavior) option is set to `OnRelease` to provide a fair comparison to the other classes.

The `MemoryStream` class has no option to zero out memory buffers (used memory is always cleared), so this parameter does not apply to that class.

Expand Down
4 changes: 2 additions & 2 deletions Source/Docs/articles/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# PerfUtils Documentation

This documentation is for the `KZDev.PerfUtils` package, which contains the `MemoryStreamSlim` class. The following sections provide insight and information on usage, how the class operates, and information on performance benchmarks. For a full API reference, see the [API Reference](/api/KZDev.PerfUtils.html).
This documentation is for the `KZDev.PerfUtils` package, which contains the `MemoryStreamSlim` class. The following sections provide insight and information on usage, how the class operates, and information on performance benchmarks. For a full API reference, see the [API Reference](xref:KZDev.PerfUtils).

## MemoryStreamSlim

The standard `MemoryStream` is a class in the .NET Class Library that represents a stream of bytes stored in memory. It is a very useful class for working with in-memory data, but it has some limitations. One of the main limitations is that it uses a single byte array to store the data, which can result in a lot of garbage collection pressure when working with large amounts of memory or cases where many `MemoryStream` instances are created and disposed of frequently.

The [`MemoryStreamSlim`](/api/KZDev.PerfUtils.MemoryStreamSlim.html) class is specifically tailored to improve performance in cases where using `MemoryStream` can result in a lot of GC pressure but it also has better overall throughput performance than the standard MemoryStream in most use cases.
The [`MemoryStreamSlim`](xref:KZDev.PerfUtils.MemoryStreamSlim) class is specifically tailored to improve performance in cases where using `MemoryStream` can result in a lot of GC pressure but it also has better overall throughput performance than the standard MemoryStream in most use cases.

This documentation provides information on how to use the `MemoryStreamSlim` class, how it works, and how it can help improve performance in your applications.

Expand Down
6 changes: 3 additions & 3 deletions Source/Docs/articles/memory-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ You can help manage the memory used by MemoryStreamSlim instances in your applic

By default, `MemoryStreamSlim` will zero out its memory buffers when they are no longer used and before the memory segments are cached for future use. This is done for security reasons to prevent sensitive data from being left in memory after it has been used but is no longer needed. However, zeroing out memory buffers can be time-consuming, especially for large buffers.

You can control how the memory buffers are cleared by setting the `MemoryStreamSlimOptions` [`ZeroBufferBehavior`](/api/KZDev.PerfUtils.MemoryStreamSlimOptions.ZeroBufferBehavior.html) property, which can be set globally as a default value or, per instance, when instantiating `MemoryStreamSlim`. The ZeroBufferBehavior property can be set to one of the following [MemoryStreamSlimZeroBufferOption](/api/KZDev.PerfUtils.MemoryStreamSlimZeroBufferOption.html) values:
You can control how the memory buffers are cleared by setting the `MemoryStreamSlimOptions` [`ZeroBufferBehavior`](xref:KZDev.PerfUtils.MemoryStreamSlimOptions.ZeroBufferBehavior) property, which can be set globally as a default value or, per instance, when instantiating `MemoryStreamSlim`. The ZeroBufferBehavior property can be set to one of the following [MemoryStreamSlimZeroBufferOption](xref:KZDev.PerfUtils.MemoryStreamSlimZeroBufferOption) values:

- `None`: No clearing of memory buffers is performed. This is the fastest option, but it can leave potentially sensitive data in memory. For streams that don't contain any sensitive data, this can be a good option to improve performance.
- `OutOfBand`: **This is the default behavior.** . Memory buffers are cleared out-of-band, meaning the clearing is done on a separate thread. This can help reduce the latency impact on the thread disposing of or reducing the capacity on MemoryStreamSlim. Still, it can briefly leave information in memory before it is cleared and returned to the buffer cache. This hybrid approach keeps potentially sensitive information out of memory while providing better performance for the threads using `MemoryStreamSlim`. While this processes in near real time, it is not instantaneous.
Expand All @@ -29,7 +29,7 @@ using (Stream stream = MemoryStreamSlim.Create(options => options.ZeroBufferBeha

## Releasing Memory

`MemoryStreamSlim` provides a static [`ReleaseMemoryBuffers`](/api/KZDev.PerfUtils.MemoryStreamSlim.ReleaseMemoryBuffers.html) method that allows you to release the memory buffers being cached for use by the stream instances. This hints to the system that the memory buffers are no longer needed and can be released for garbage collection. After calling this method, the memory buffers will be released as soon as possible based on current usage and other factors. Still, the release of memory may not be immediate.
`MemoryStreamSlim` provides a static [`ReleaseMemoryBuffers`](xref:KZDev.PerfUtils.MemoryStreamSlim.ReleaseMemoryBuffers) method that allows you to release the memory buffers being cached for use by the stream instances. This hints to the system that the memory buffers are no longer needed and can be released for garbage collection. After calling this method, the memory buffers will be released as soon as possible based on current usage and other factors. Still, the release of memory may not be immediate.

```csharp
using KZDev.PerfUtils;
Expand All @@ -46,7 +46,7 @@ After calling 'ReleaseMemoryBuffers', future instances of `MemoryStreamSlim` wil

To avoid large heap fragmentation and reduce the number of allocations and deallocations, `MemoryStreamSlim` will, by default, allocate the memory buffer segments from the LOH and keep them available in a cache for future use. This does help avoid LOH fragmentation, but the memory is still allocated from the managed heap. Every full GC cycle will still have to review references to these segments in the LOH, and the memory buffers will still be subject to the same GC pressure as any other managed memory.

To avoid managed memory and GC pressure caused by these memory buffers altogether, you can configure the system to allocate the large memory buffer segments from the native memory heap. This is an AppDomain wide setting and must be done before creating any `MemoryStreamSlim` instances. You configure this by setting the [`UseNativeLargeMemoryBuffers`](/api/KZDev.PerfUtils.MemoryStreamSlim.UseNativeLargeMemoryBuffers.html) static property to true before creating any `MemoryStreamSlim` instances.
To avoid managed memory and GC pressure caused by these memory buffers altogether, you can configure the system to allocate the large memory buffer segments from the native memory heap. This is an AppDomain wide setting and must be done before creating any `MemoryStreamSlim` instances. You configure this by setting the [`UseNativeLargeMemoryBuffers`](xref:KZDev.PerfUtils.MemoryStreamSlim.UseNativeLargeMemoryBuffers) static property to true before creating any `MemoryStreamSlim` instances.

The behavior of the `MemoryStreamSlim` instances will not differ when using native memory. The buffers remain internal to the `MemoryStreamSlim` and are never exposed publicly. This can be useful to avoid LOH fragmentation resulting from other large objects in your application being intermixed with the memory buffers used by MemoryStreamSlim. By putting the memory buffers in native memory, these fragmentation issues can be avoided.

Expand Down
10 changes: 5 additions & 5 deletions Source/Docs/articles/memorystreamslim.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

The .NET class library provides a `MemoryStream` class representing a stream of bytes stored in memory. It operates two implied modes: expandable (dynamic) or fixed, determined by how you instantiate the `MemoryStream` instance. In expandable mode, the `MemoryStream` class uses a single-byte array to store the data, and the array is resized as needed to accommodate the data as the length and capacity change. In fixed mode, the `MemoryStream` class uses a fixed-size byte array to store the data provided to the constructor during instantiation.

The [`MemoryStream`](https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream) class is optimized for and works well in fixed mode, providing `Stream` semantics to a byte array you have already allocated. However, in expandable mode, the `MemoryStream` class can result in poor performance and a lot of garbage collection pressure when working with large amounts of memory, many `MemoryStream` instances being created and disposed of frequently, or when the stream instances grow dynamically.
The [`MemoryStream`](xref:System.IO.MemoryStream) class is optimized for and works well in fixed mode, providing `Stream` semantics to a byte array you have already allocated. However, in expandable mode, the `MemoryStream` class can result in poor performance and a lot of garbage collection pressure when working with large amounts of memory, many `MemoryStream` instances being created and disposed of frequently, or when the stream instances grow dynamically.

Microsoft released a separate [`RecyclableMemoryStream`](https://www.nuget.org/packages/Microsoft.IO.RecyclableMemoryStream) package that addresses many issues with the expandable mode of the `MemoryStream` class. Unfortunately, the `RecyclableMemoryStream` package is somewhat cumbersome to use. It also has several limitations, including requiring an application to have consistent usage and behavior patterns and then tune configuration options to those patterns. In some scenarios, this class can also perform much worse than the standard `MemoryStream` class. One mistake with the `RecyclableMemoryStream` class is that it doesn't focus strictly on fixing the expandable mode of the `MemoryStream` class but instead tries to provide a more general-purpose memory stream class that can be used for a fixed mode scenario as well. The issue is that `RecyclableMemoryStream` typically has worse throughput performance than the standard `MemoryStream` class in most fixed-mode scenarios.

[`MemoryStreamSlim`](/api/KZDev.PerfUtils.MemoryStreamSlim.html) is a memory stream that is optimized for performance. It is a drop-in replacement for the `MemoryStream` class and is designed to be used in scenarios where performance and memory management are crucial. This class is instrumental in scenarios where you need expandable memory-based stream instances that are substantially large, the stream size needs or the general use cases vary a lot, or you use memory-based streams frequently. Since the standard `MemoryStream` works so well as a `Stream` semantic API wrapper around existing byte arrays, `MemoryStreamSlim` does not attempt to fix what isn't broken and focuses on the expandable mode use cases. This is discussed more below.
[`MemoryStreamSlim`](xref:KZDev.PerfUtils.MemoryStreamSlim) is a memory stream that is optimized for performance. It is a drop-in replacement for the `MemoryStream` class and is designed to be used in scenarios where performance and memory management are crucial. This class is instrumental in scenarios where you need expandable memory-based stream instances that are substantially large, the stream size needs or the general use cases vary a lot, or you use memory-based streams frequently. Since the standard `MemoryStream` works so well as a `Stream` semantic API wrapper around existing byte arrays, `MemoryStreamSlim` does not attempt to fix what isn't broken and focuses on the expandable mode use cases. This is discussed more below.

## Memory Traffic

Expand All @@ -24,7 +24,7 @@ This memory management is only utilized when the `MemoryStreamSlim` instance is

## Usage

To create a `MemoryStreamSlim` instance, use one of the [`Create`](/api/KZDev.PerfUtils.MemoryStreamSlim.Create.html) static overload methods on the MemoryStreamSlim class. These methods allow you to create a `MemoryStreamSlim` instance with an optional specified initial capacity, select options for the stream instance, or provide an existing byte array to wrap.
To create a `MemoryStreamSlim` instance, use one of the [`Create`](xref:KZDev.PerfUtils.MemoryStreamSlim.Create) static overload methods on the MemoryStreamSlim class. These methods allow you to create a `MemoryStreamSlim` instance with an optional specified initial capacity, select options for the stream instance, or provide an existing byte array to wrap.

Besides creation, the `MemoryStreamSlim` class can be used exactly like the standard `MemoryStream` class. It implements all the same current methods and properties as `MemoryStream` so that you can use it as a drop-in replacement in your code. Deprecated and outdated methods and properties are not implemented in `MemoryStreamSlim`.

Expand All @@ -38,7 +38,7 @@ using (Stream stream = MemoryStreamSlim.Create(1024, options => options.ZeroBuff
}
```

The one notable difference between `MemoryStreamSlim` and `MemoryStream` behavior is the implementation of the [GetBuffer](/api/KZDev.PerfUtils.MemoryStreamSlim.GetBuffer.html) method. For fixed mode, the `GetBuffer` method works exactly as the `MemoryStream` class does, returning the underlying byte array that the stream is using if the instance was created with the 'publiclyVisible' parameter set to `true`, otherwise throwing an exception. For expandable mode `MemoryStreamSlim` instances, the `GetBuffer` method will strictly throw a `NotSupportedException`. This is a purposeful design decision to prevent misuse of the `MemoryStreamSlim` class in scenarios where the underlying buffer is not guaranteed to be contiguous in memory, and there are better and safer ways to access the data in the stream than getting direct access to the buffer. The `MemoryStreamSlim` class is designed to be a high-performance memory stream, and the `GetBuffer` method is not a high-performance method in the general case, and results of direct buffer access can be unpredictable and unsafe.
The one notable difference between `MemoryStreamSlim` and `MemoryStream` behavior is the implementation of the [GetBuffer](xref:KZDev.PerfUtils.MemoryStreamSlim.GetBuffer*) method. For fixed mode, the `GetBuffer` method works exactly as the `MemoryStream` class does, returning the underlying byte array that the stream is using if the instance was created with the 'publiclyVisible' parameter set to `true`, otherwise throwing an exception. For expandable mode `MemoryStreamSlim` instances, the `GetBuffer` method will strictly throw a `NotSupportedException`. This is a purposeful design decision to prevent misuse of the `MemoryStreamSlim` class in scenarios where the underlying buffer is not guaranteed to be contiguous in memory, and there are better and safer ways to access the data in the stream than getting direct access to the buffer. The `MemoryStreamSlim` class is designed to be a high-performance memory stream, and the `GetBuffer` method is not a high-performance method in the general case, and results of direct buffer access can be unpredictable and unsafe.

`RecyclableMemoryStream` on the other hand, supports calling `GetBuffer` and suggests using it over `ToArray` for performance reasons to get a byte array representation of the stream contents. The documentation has many warnings about the potential performance implications of using `GetBuffer` however and the need to be careful with its use. In addition, the first call to `GetBuffer` will cause the `RecyclableMemoryStream` class's internal workings to change so that performance could degrade significantly for that instance from that point on.

Expand All @@ -61,7 +61,7 @@ By default, `MemoryStreamSlim` will zero out the memory buffers that are used wh

### Releasing Memory

`MemoryStreamSlim` provides a [`ReleaseMemoryBuffers`](/api/KZDev.PerfUtils.MemoryStreamSlim.ReleaseMemoryBuffers.html) method that that allows you to release the memory buffers cached for use by new stream instances. Releasing this memory can be useful when you want to free up cached memory that is no longer needed. [Read More](./memory-management.md#releasing-memory)
`MemoryStreamSlim` provides a [`ReleaseMemoryBuffers`](xref:KZDev.PerfUtils.MemoryStreamSlim.ReleaseMemoryBuffers) method that that allows you to release the memory buffers cached for use by new stream instances. Releasing this memory can be useful when you want to free up cached memory that is no longer needed. [Read More](./memory-management.md#releasing-memory)

### Native Memory

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The amount of data to write to the stream in each loop of the operation. The dat

#### ZeroBuffers

When `true`, the stream is created with the option to zero out memory buffers when they are no longer used. When `false`, the stream is created with the option to not zero out memory buffers specified. For the `MemoryStreamSlim` class, the [`ZeroBufferBehavior`](/api/KZDev.PerfUtils.MemoryStreamSlimOptions.ZeroBufferBehavior.html) option is set to `OnRelease` to provide a fair comparison to the other classes.
When `true`, the stream is created with the option to zero out memory buffers when they are no longer used. When `false`, the stream is created with the option to not zero out memory buffers specified. For the `MemoryStreamSlim` class, the [`ZeroBufferBehavior`](xref:KZDev.PerfUtils.MemoryStreamSlimOptions.ZeroBufferBehavior) option is set to `OnRelease` to provide a fair comparison to the other classes.

The `MemoryStream` class has no option to zero out memory buffers (used memory is always cleared), so this parameter does not apply to that class.

Expand Down
Loading

0 comments on commit c33c92b

Please sign in to comment.