[#20] Optimize memory usage
All checks were successful
DCO / DCO (pull_request) Successful in 27s

Provide custom buffer and use ArrayPool

Signed-off-by: Pavel Gross <p.gross@yando.com>
This commit is contained in:
Pavel Gross 2024-08-05 11:21:05 +03:00
parent 6083834582
commit 18126ea763
3 changed files with 76 additions and 35 deletions

View file

@ -1,6 +1,7 @@
using System.Collections.Specialized; using FrostFS.SDK.ModelsV2;
using System.Collections.Specialized;
using System.IO; using System.IO;
using FrostFS.SDK.ModelsV2;
namespace FrostFS.SDK.ClientV2.Parameters; namespace FrostFS.SDK.ClientV2.Parameters;
public sealed class PrmObjectPut : IContext, ISessionToken public sealed class PrmObjectPut : IContext, ISessionToken
@ -31,6 +32,11 @@ public sealed class PrmObjectPut : IContext, ISessionToken
/// <value>Size of the buffer</value> /// <value>Size of the buffer</value>
public int BufferMaxSize { get; set; } public int BufferMaxSize { get; set; }
/// <summary>
/// Allows to define a buffer for chunks to manage by the memory allocation and releasing.
/// </summary>
public byte[]? CustomBuffer { get; set; }
/// <summary> /// <summary>
/// FrostFS request X-Headers /// FrostFS request X-Headers
/// </summary> /// </summary>

View file

@ -307,7 +307,21 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
chunkSize = (int)Math.Min(restBytes, (ulong)chunkSize); chunkSize = (int)Math.Min(restBytes, (ulong)chunkSize);
var chunkBuffer = ArrayPool<byte>.Shared.Rent(chunkSize); bool isRentBuffer = false;
byte[]? chunkBuffer = null;
try
{
if (args.CustomBuffer != null)
{
chunkBuffer = args.CustomBuffer;
}
else
{
chunkBuffer = env.GetArrayPool(Constants.ObjectChunkSize).Rent(chunkSize);
isRentBuffer = true;
}
var sentBytes = 0; var sentBytes = 0;
// 0 means no limit from client, so server side cut is performed // 0 means no limit from client, so server side cut is performed
@ -347,6 +361,14 @@ internal class ObjectServiceProvider(ObjectService.ObjectServiceClient client, C
return new PutObjectResult(ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()), sentBytes); return new PutObjectResult(ObjectId.FromHash(response.Body.ObjectId.Value.ToByteArray()), sentBytes);
} }
finally
{
if (isRentBuffer && chunkBuffer != null)
{
ArrayPool<byte>.Shared.Return(chunkBuffer);
}
}
}
private async Task<ObjectStreamer> GetUploadStream(PrmObjectPut args, Context ctx) private async Task<ObjectStreamer> GetUploadStream(PrmObjectPut args, Context ctx)
{ {

View file

@ -4,11 +4,14 @@ using Grpc.Net.Client;
using System; using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using FrostFS.SDK.Cryptography; using FrostFS.SDK.Cryptography;
using System.Buffers;
namespace FrostFS.SDK.ClientV2; namespace FrostFS.SDK.ClientV2;
public class ClientEnvironment(Client client, ECDsa key, OwnerId owner, GrpcChannel channel, ModelsV2.Version version) : IDisposable public class ClientEnvironment(Client client, ECDsa key, OwnerId owner, GrpcChannel channel, ModelsV2.Version version) : IDisposable
{ {
private ArrayPool<byte> _arrayPool;
internal OwnerId Owner { get; } = owner; internal OwnerId Owner { get; } = owner;
internal GrpcChannel Channel { get; private set; } = channel; internal GrpcChannel Channel { get; private set; } = channel;
internal ModelsV2.Version Version { get; } = version; internal ModelsV2.Version Version { get; } = version;
@ -18,6 +21,16 @@ public class ClientEnvironment(Client client, ECDsa key, OwnerId owner, GrpcChan
internal ClientKey Key { get; } = new ClientKey(key); internal ClientKey Key { get; } = new ClientKey(key);
/// <summary>
/// Custom pool is used for predefined sizes of buffers like grpc chunk
/// </summary>
internal ArrayPool<byte> GetArrayPool(int size)
{
_arrayPool ??= ArrayPool<byte>.Create(size, 256);
return _arrayPool;
}
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);