Object Pool Benchmark Results




Date Added (UTC):

21 May 2024 @ 18:25

Date Updated (UTC):

21 May 2024 @ 18:25


.NET Version(s):

.NET 8

Tag(s):

#.Net8PerfImprovement #DBOperations #FileIO #ObjectMapping #StringBuilder


Added By:
Profile Image

Blog   
Wilmington, DE 19808, USA    
A dedicated executive technical architect who is focused on expanding organizations technology capabilities.

Benchmark Results:





Benchmark Code:



using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.ObjectPool;
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;

[MemoryDiagnoser]
public class ObjectPoolBenchmarks
{
    private ObjectPool<ReusableBuffer> _bufferPool;
    private readonly string _sampleText = "The Test Text...";

    [GlobalSetup]
    public void Setup()
    {
        var services = new ServiceCollection();
        services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
        services.AddSingleton<ObjectPool<ReusableBuffer>>(serviceProvider =>
        {
            var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
            var policy = new DefaultPooledObjectPolicy<ReusableBuffer>();
            return provider.Create(policy);
        });

        var serviceProvider = services.BuildServiceProvider();
        _bufferPool = serviceProvider.GetRequiredService<ObjectPool<ReusableBuffer>>();
    }

    [Benchmark]
    public string ComputeHashWithoutPooling()
    {
        var buffer = new byte[1024 * 1024]; // 1 MB
        for (var i = 0; i < _sampleText.Length; i++)
        {
            buffer[i] = (byte)_sampleText[i];
        }

        Span<byte> hash = stackalloc byte[32];
        SHA256.HashData(buffer.AsSpan(0, _sampleText.Length), hash);
        return Convert.ToHexString(hash);
    }

    [Benchmark]
    public string ComputeHashWithPooling()
    {
        var buffer = _bufferPool.Get();
        try
        {
            for (var i = 0; i < _sampleText.Length; i++)
            {
                buffer.Data[i] = (byte)_sampleText[i];
            }

            Span<byte> hash = stackalloc byte[32];
            SHA256.HashData(buffer.Data.AsSpan(0, _sampleText.Length), hash);
            return Convert.ToHexString(hash);
        }
        finally
        {
            _bufferPool.Return(buffer);
        }
    }
}

public class ReusableBuffer : IResettable
{
    public byte[] Data { get; } = new byte[1024 * 1024];

    public bool TryReset()
    {
        Array.Clear(Data);
        return true;
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<ObjectPoolBenchmarks>();
    }
}

// .NET 8 Lowered C# Code unavailable due to errors:
error CS0234: The type or namespace name 'ObjectPool' does not exist in the namespace 'Microsoft.Extensions' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'IResettable' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ObjectPool<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ObjectPoolProvider' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DefaultObjectPoolProvider' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DefaultPooledObjectPolicy<>' could not be found (are you missing a using directive or an assembly reference?)
error CS1061: 'ServiceCollection' does not contain a definition for 'BuildServiceProvider' and no accessible extension method 'BuildServiceProvider' accepting a first argument of type 'ServiceCollection' could be found (are you missing a using directive or an assembly reference?)

// .NET 8 IL Code unavailable due to errors:
error CS0234: The type or namespace name 'ObjectPool' does not exist in the namespace 'Microsoft.Extensions' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'ObjectPool<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'IResettable' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ObjectPoolProvider' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DefaultObjectPoolProvider' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DefaultPooledObjectPolicy<>' could not be found (are you missing a using directive or an assembly reference?)
error CS1061: 'ServiceCollection' does not contain a definition for 'BuildServiceProvider' and no accessible extension method 'BuildServiceProvider' accepting a first argument of type 'ServiceCollection' could be found (are you missing a using directive or an assembly reference?)

// .NET 8 Jit Asm Code unavailable due to errors:
error CS0234: The type or namespace name 'ObjectPool' does not exist in the namespace 'Microsoft.Extensions' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'ObjectPool<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'IResettable' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ObjectPoolProvider' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DefaultObjectPoolProvider' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DefaultPooledObjectPolicy<>' could not be found (are you missing a using directive or an assembly reference?)
error CS1061: 'ServiceCollection' does not contain a definition for 'BuildServiceProvider' and no accessible extension method 'BuildServiceProvider' accepting a first argument of type 'ServiceCollection' could be found (are you missing a using directive or an assembly reference?)


Benchmark Description:


## Object Pool Benchmark Results ### Overview This benchmark compares the performance of computing SHA256 hashes with and without using an object pool for buffer reuse. The objective is to measure the time taken and memory allocated for each method. ### Benchmark Setup - **Buffer Size**: 1 MB - **Sample Text**: "BenchmarkDotNetSampleText" - **Hash Algorithm**: SHA256 - **Object Pool**: `ObjectPool<ReusableBuffer>` ### Methods 1. **ComputeHashWithoutPooling**: - Allocates a new 1 MB buffer each time. - Computes the SHA256 hash. 2. **ComputeHashWithPooling**: - Uses an object pool to reuse a 1 MB buffer. - Computes the SHA256 hash. - Returns the buffer to the pool after use. ### Key Takeaways - **Performance**: - **With Pooling**: Faster (11.63 us) - **Without Pooling**: Slower (21.51 us) - **Memory Allocation**: - **With Pooling**: Significantly lower (152 B) - **Without Pooling**: Much higher (1048862 B) ### Conclusion Using an object pool for buffer reuse greatly improves performance and reduces memory allocation. This is beneficial for scenarios where large buffers are frequently needed and can be reused, leading to more efficient resource utilization.

The provided benchmark code is designed to compare the performance and memory usage of two different approaches for computing a SHA256 hash of a sample text: one using a manually managed byte array and the other utilizing an object pool to manage the byte array. This comparison is particularly relevant in scenarios where such operations are performed frequently, as in web servers or data processing applications, where efficient resource management can lead to significant performance improvements and reduced memory pressure. ### General Setup - **BenchmarkDotNet**: This is a powerful .NET library for benchmarking, providing detailed insights into the performance of .NET code. It is used here to define, run, and analyze the benchmarks. - **.NET Version**: While not explicitly mentioned, the code syntax and libraries used suggest it targets a recent .NET Core or .NET 5/6 version, given the usage of `Span<T>`, `stackalloc`, and the latest `SHA256` static methods. - **Configuration**: The `[MemoryDiagnoser]` attribute is used to enable memory diagnostics, allowing the benchmark to report on memory allocations in addition to execution time. - **Object Pooling Setup**: The `Setup` method initializes an object pool for `ReusableBuffer` objects using the `DefaultObjectPoolProvider`. This setup is crucial for the `ComputeHashWithPooling` benchmark method, as it relies on reusing instances of `ReusableBuffer` from the pool instead of allocating new ones for each operation. ### Benchmark Methods #### 1. `ComputeHashWithoutPooling()` - **Purpose**: This method measures the performance and memory usage of computing a SHA256 hash using a new byte array allocated for each operation. - **Performance Aspect**: It tests the overhead of allocating and initializing a new 1 MB byte array on each call, which includes both the time taken to allocate the memory and the time to fill it with the sample text's bytes. - **Expected Insights**: Running this benchmark provides insights into how costly memory allocation and deallocation can be in high-frequency scenarios. It serves as a baseline to understand the benefits of object pooling. #### 2. `ComputeHashWithPooling()` - **Purpose**: This method evaluates the performance and memory usage improvements by using an object pool to reuse `ReusableBuffer` instances for computing a SHA256 hash. - **Performance Aspect**: It focuses on the efficiency gains from avoiding frequent memory allocations by reusing objects. It measures the overhead of managing the object pool and the effectiveness of reusing objects in terms of execution time and memory usage. - **Expected Insights**: The results from this benchmark method should highlight the potential performance and memory usage benefits of object pooling. Specifically, it should show a reduction in memory allocations and possibly an improvement in execution time, compared to the non-pooling approach. ### Conclusion By comparing the results of these two benchmark methods, developers can gain valuable insights into the trade-offs between the simplicity of direct memory allocation and the efficiency of object pooling. Object pooling can significantly reduce GC (Garbage Collection) pressure and improve performance in scenarios involving frequent allocations and deallocations of large objects, at the cost of added complexity in object management. The specific results would depend on the .NET runtime version, the hardware it's running on, and the characteristics of the workload.


Benchmark Comments: