Skip to content

Commit

Permalink
slice buffer improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
jtattermusch committed Aug 9, 2019
1 parent ec14872 commit ded29be
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 13 deletions.
95 changes: 91 additions & 4 deletions src/csharp/Grpc.Core.Tests/Internal/SliceBufferSafeHandleTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,99 @@ namespace Grpc.Core.Internal.Tests
public class SliceBufferSafeHandleTest
{
[TestCase]
public void BasicTest()
public void Complete_EmptyBuffer()
{

using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
sliceBuffer.Complete();
CollectionAssert.AreEqual(new byte[0], sliceBuffer.ToByteArray());
}
}

// other ideas:
// AdjustTailSpace(0) if previous tail size is 0... (better for SliceBufferSafeHandle)
[TestCase]
public void Complete_TailSizeZero()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
var origPayload = GetTestBuffer(10);
origPayload.AsSpan().CopyTo(sliceBuffer.GetSpan(origPayload.Length));
sliceBuffer.Advance(origPayload.Length);
// call complete where tail space size == 0
sliceBuffer.Complete();
CollectionAssert.AreEqual(origPayload, sliceBuffer.ToByteArray());
}
}

[TestCase]
public void Complete_TruncateTailSpace()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
var origPayload = GetTestBuffer(10);
var dest = sliceBuffer.GetSpan(origPayload.Length + 10);
origPayload.AsSpan().CopyTo(dest);
sliceBuffer.Advance(origPayload.Length);
// call complete where tail space needs to be truncated
sliceBuffer.Complete();
CollectionAssert.AreEqual(origPayload, sliceBuffer.ToByteArray());
}
}

[TestCase]
public void SliceBufferIsReusable()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
var origPayload = GetTestBuffer(10);
origPayload.AsSpan().CopyTo(sliceBuffer.GetSpan(origPayload.Length));
sliceBuffer.Advance(origPayload.Length);
sliceBuffer.Complete();
CollectionAssert.AreEqual(origPayload, sliceBuffer.ToByteArray());

sliceBuffer.Reset();

var origPayload2 = GetTestBuffer(20);
origPayload2.AsSpan().CopyTo(sliceBuffer.GetSpan(origPayload2.Length));
sliceBuffer.Advance(origPayload2.Length);
sliceBuffer.Complete();
CollectionAssert.AreEqual(origPayload2, sliceBuffer.ToByteArray());

sliceBuffer.Reset();

CollectionAssert.AreEqual(new byte[0], sliceBuffer.ToByteArray());
}
}

[TestCase]
public void SliceBuffer_SizeHintZero()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
var destSpan = sliceBuffer.GetSpan(0);
Assert.IsTrue(destSpan.Length > 0); // some non-zero size memory is made available

sliceBuffer.Reset();

var destMemory = sliceBuffer.GetMemory(0);
Assert.IsTrue(destMemory.Length > 0);
}
}

// Advance() - multiple chunks...

// other TODOS:

// -- provide a null instance of SliceBufferSafeHandle
//-- order of UnsafeSerialize in AsyncCall methods...

private byte[] GetTestBuffer(int length)
{
var testBuffer = new byte[length];
for (int i = 0; i < testBuffer.Length; i++)
{
testBuffer[i] = (byte) i;
}
return testBuffer;
}
}
}
33 changes: 24 additions & 9 deletions src/csharp/Grpc.Core/Internal/SliceBufferSafeHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace Grpc.Core.Internal
/// </summary>
internal class SliceBufferSafeHandle : SafeHandleZeroIsInvalid, IBufferWriter<byte>
{
const int DefaultTailSpaceSize = 4096; // default buffer to allocate if no size hint is provided
static readonly NativeMethods Native = NativeMethods.Get();
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<SliceBufferSafeHandle>();

Expand Down Expand Up @@ -72,7 +73,7 @@ public void Advance(int count)
// Use GetSpan when possible for better efficiency.
public Memory<byte> GetMemory(int sizeHint = 0)
{
GetSpan(sizeHint);
EnsureBufferSpace(sizeHint);
if (memoryManagerLazy == null)
{
memoryManagerLazy = new SliceMemoryManager();
Expand All @@ -84,13 +85,7 @@ public Memory<byte> GetMemory(int sizeHint = 0)
// provides access to the "tail space" of this buffer.
public unsafe Span<byte> GetSpan(int sizeHint = 0)
{
GrpcPreconditions.CheckArgument(sizeHint >= 0);
if (tailSpaceLen < sizeHint)
{
// TODO: should we ignore the hint sometimes when
// available tail space is close enough to the sizeHint?
AdjustTailSpace(sizeHint);
}
EnsureBufferSpace(sizeHint);
return new Span<byte>(tailSpacePtr.ToPointer(), tailSpaceLen);
}

Expand Down Expand Up @@ -135,7 +130,27 @@ public byte[] ToByteArray()
return result;
}

// Gets data of server_rpc_new completion.
private void EnsureBufferSpace(int sizeHint)
{
GrpcPreconditions.CheckArgument(sizeHint >= 0);
if (sizeHint == 0)
{
// if no hint is provided, keep the available space within some "reasonable" boundaries.
// This is quite a naive approach which could use some fine-tuning, but currently in most case we know
// the required buffer size in advance anyway, so this approach seems good enough for now.
if (tailSpaceLen < DefaultTailSpaceSize /2 )
{
AdjustTailSpace(DefaultTailSpaceSize);
}
}
else if (tailSpaceLen < sizeHint)
{
// if hint is provided, always make sure we provide at least that much space
AdjustTailSpace(sizeHint);
}
}

// make sure there's exactly requestedSize bytes of continguous buffer space at the end of this slice buffer
private void AdjustTailSpace(int requestedSize)
{
GrpcPreconditions.CheckArgument(requestedSize >= 0);
Expand Down

0 comments on commit ded29be

Please sign in to comment.