diff --git a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt index 80ace85905f..1d05981c6ad 100644 --- a/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt +++ b/src/core/Akka.API.Tests/CoreAPISpec.ApproveCore.approved.txt @@ -3239,6 +3239,15 @@ namespace Akka.IO.Buffers System.ArraySegment Rent(); System.Collections.Generic.IEnumerable> Rent(int minimumSize); } + public class SimpleBufferPool : Akka.IO.Buffers.IBufferPool + { + public SimpleBufferPool(Akka.Actor.ExtendedActorSystem system, Akka.Configuration.Config config) { } + public SimpleBufferPool(int bufferSize, int capacity) { } + public void Release(System.ArraySegment buf) { } + public void Release(System.Collections.Generic.IEnumerable> bufs) { } + public System.ArraySegment Rent() { } + public System.Collections.Generic.IEnumerable> Rent(int minimumSize) { } + } } namespace Akka.IO { diff --git a/src/core/Akka/Configuration/Pigeon.conf b/src/core/Akka/Configuration/Pigeon.conf index 49a1fce3478..1a27f524be7 100644 --- a/src/core/Akka/Configuration/Pigeon.conf +++ b/src/core/Akka/Configuration/Pigeon.conf @@ -609,6 +609,24 @@ akka { buffer-pool-limit = 1024 } + # Default implementation of `Akka.IO.Buffers.IBufferPool` interface. + # It pools and reuse allocated buffers that are released by requesters. + # This cuts down on the number of memory allocation and garbage collection + # for sequential socket connections. + simple-buffer-pool { + + # Class implementing `Akka.IO.Buffers.IBufferPool` interface, which + # will be created with this configuration. + class = "Akka.IO.Buffers.SimpleBufferPool, Akka" + + # Size of a single byte buffer in bytes. + buffer-size = 512 + + # Maximum number of byte buffer that can be pooled by this byte buffer pool + # instance. + buffer-pool-capacity = 512000 + } + # Default implementation of `Akka.IO.Buffers.IBufferPool` interface. # Instead of maintaining allocated buffers and reusing them # between different SocketAsyncEventArgs instances, it allocates new @@ -741,6 +759,24 @@ akka { buffer-pool-limit = 1024 } + # Default implementation of `Akka.IO.Buffers.IBufferPool` interface. + # It pools and reuse allocated buffers that are released by requesters. + # This cuts down on the number of memory allocation and garbage collection + # for sequential socket connections. + simple-buffer-pool { + + # Class implementing `Akka.IO.Buffers.IBufferPool` interface, which + # will be created with this configuration. + class = "Akka.IO.Buffers.SimpleBufferPool, Akka" + + # Size of a single byte buffer in bytes. + buffer-size = 512 + + # Maximum number of byte buffer that can be pooled by this byte buffer pool + # instance. + buffer-pool-capacity = 512000 + } + # A buffer pool used to acquire and release byte buffers from the managed # heap. Once byte buffer is no longer needed is can be released, landing # on the pool again, to be reused later. This way we can reduce a GC pressure @@ -836,6 +872,24 @@ akka { buffer-pool-limit = 1024 } + # Default implementation of `Akka.IO.Buffers.IBufferPool` interface. + # It pools and reuse allocated buffers that are released by requesters. + # This cuts down on the number of memory allocation and garbage collection + # for sequential socket connections. + simple-buffer-pool { + + # Class implementing `Akka.IO.Buffers.IBufferPool` interface, which + # will be created with this configuration. + class = "Akka.IO.Buffers.SimpleBufferPool, Akka" + + # Size of a single byte buffer in bytes. + buffer-size = 512 + + # Maximum number of byte buffer that can be pooled by this byte buffer pool + # instance. + buffer-pool-capacity = 512000 + } + # A buffer pool used to acquire and release byte buffers from the managed # heap. Once byte buffer is no longer needed is can be released, landing # on the pool again, to be reused later. This way we can reduce a GC pressure diff --git a/src/core/Akka/IO/Buffers/SimpleBufferPool.cs b/src/core/Akka/IO/Buffers/SimpleBufferPool.cs new file mode 100644 index 00000000000..10ff074e36d --- /dev/null +++ b/src/core/Akka/IO/Buffers/SimpleBufferPool.cs @@ -0,0 +1,79 @@ +// //----------------------------------------------------------------------- +// // +// // Copyright (C) 2009-2021 Lightbend Inc. +// // Copyright (C) 2013-2021 .NET Foundation +// // +// //----------------------------------------------------------------------- + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using Akka.Actor; +using Akka.Configuration; + +namespace Akka.IO.Buffers +{ + using ByteBuffer = ArraySegment; + + public class SimpleBufferPool : IBufferPool + { + private readonly ConcurrentStack _buffers = new ConcurrentStack(); + private readonly int _bufferSize; + private readonly int _capacity; + + public SimpleBufferPool(ExtendedActorSystem system, Config config) : this( + bufferSize: config.GetInt("buffer-size", 256), + capacity: config.GetInt("buffer-pool-capacity", 250000)) + { + } + + public SimpleBufferPool(int bufferSize, int capacity) + { + if (bufferSize <= 0) throw new ArgumentException("Buffer size must be positive number", nameof(bufferSize)); + if (capacity <= 0) throw new ArgumentException("Number of maximum pool capacity must be positive", nameof(capacity)); + + _bufferSize = bufferSize; + _capacity = capacity; + } + + public ByteBuffer Rent() + { + return _buffers.TryPop(out var buffer) + ? buffer + : new ByteBuffer(new byte[_bufferSize], 0, _bufferSize); + } + + public IEnumerable Rent(int minimumSize) + { + var buffersToGet = (int)Math.Ceiling((double) minimumSize / _bufferSize); + var result = new ByteBuffer[buffersToGet]; + var received = 0; + + while (received < buffersToGet) + { + result[received] = Rent(); + received++; + } + + return result; + } + + public void Release(ByteBuffer buf) + { + if(_buffers.Count < _capacity) + _buffers.Push(buf); + // We don't care about pool overrun, we just let the runtime GC them + } + + public void Release(IEnumerable bufs) + { + foreach (var buf in bufs) + { + if(_buffers.Count < _capacity) + _buffers.Push(buf); + else + return; // We don't care about the rest, let the runtime GC them + } + } + } +} \ No newline at end of file