Skip to content

Commit

Permalink
Change ByteString to use memory and support unsafe create without copy
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Nov 8, 2020
1 parent 5f7f389 commit 9b4fb0b
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 75 deletions.
5 changes: 4 additions & 1 deletion Makefile.am
Expand Up @@ -89,6 +89,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf.Benchmarks/BenchmarkDatasetConfig.cs \
csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs \
csharp/src/Google.Protobuf.Benchmarks/Benchmarks.cs \
csharp/src/Google.Protobuf.Benchmarks/ByteStringBenchmark.cs \
csharp/src/Google.Protobuf.Benchmarks/Google.Protobuf.Benchmarks.csproj \
csharp/src/Google.Protobuf.Benchmarks/GoogleMessageBenchmark.cs \
csharp/src/Google.Protobuf.Benchmarks/ParseMessagesBenchmark.cs \
Expand Down Expand Up @@ -171,6 +172,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf.sln \
csharp/src/Google.Protobuf/ByteArray.cs \
csharp/src/Google.Protobuf/ByteString.cs \
csharp/src/Google.Protobuf/ByteStringAsync.cs \
csharp/src/Google.Protobuf/CodedInputStream.cs \
csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs \
csharp/src/Google.Protobuf/CodedOutputStream.cs \
Expand Down Expand Up @@ -268,7 +270,8 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/WriteContext.cs \
csharp/src/Google.Protobuf/WriteBufferHelper.cs \
csharp/src/Google.Protobuf/UnknownField.cs \
csharp/src/Google.Protobuf/UnknownFieldSet.cs
csharp/src/Google.Protobuf/UnknownFieldSet.cs \
csharp/src/Google.Protobuf/UnsafeByteOperations.cs

java_EXTRA_DIST= \
java/README.md \
Expand Down
72 changes: 72 additions & 0 deletions csharp/src/Google.Protobuf.Benchmarks/ByteStringBenchmark.cs
@@ -0,0 +1,72 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2019 Google Inc. All rights reserved.
// https://github.com/protocolbuffers/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

using BenchmarkDotNet.Attributes;

namespace Google.Protobuf.Benchmarks
{
/// <summary>
/// Benchmarks using ByteString.
/// </summary>
[MemoryDiagnoser]
public class ByteStringBenchmark
{
private const int Zero = 0;
private const int Kilobyte = 1024;
private const int _128Kilobytes = 1024 * 128;
private const int Megabyte = 1024 * 1024;
private const int _10Megabytes = 1024 * 1024 * 10;

byte[] byteBuffer;

[GlobalSetup]
public void GlobalSetup()
{
byteBuffer = new byte[PayloadSize];
}

[Params(Zero, Kilobyte, _128Kilobytes, Megabyte, _10Megabytes)]
public int PayloadSize { get; set; }

[Benchmark]
public ByteString CopyFrom()
{
return ByteString.CopyFrom(byteBuffer);
}

[Benchmark]
public ByteString UnsafeWrap()
{
return UnsafeByteOperations.UnsafeWrap(byteBuffer);
}
}
}
64 changes: 64 additions & 0 deletions csharp/src/Google.Protobuf.Test/ByteStringTest.cs
Expand Up @@ -34,6 +34,9 @@
using System.Text;
using NUnit.Framework;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
#if !NET35
using System.Threading.Tasks;
#endif
Expand All @@ -54,6 +57,7 @@ public void Equality()
EqualityTester.AssertInequality(b1, b3);
EqualityTester.AssertInequality(b1, b4);
EqualityTester.AssertInequality(b1, null);
EqualityTester.AssertEquality(ByteString.Empty, ByteString.Empty);
#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)
Assert.IsTrue(b1 == b1);
Assert.IsTrue(b1 == b2);
Expand All @@ -63,6 +67,7 @@ public void Equality()
Assert.IsTrue((ByteString) null == null);
Assert.IsFalse(b1 != b1);
Assert.IsFalse(b1 != b2);
Assert.IsTrue(ByteString.Empty == ByteString.Empty);
#pragma warning disable 1718
Assert.IsTrue(b1 != b3);
Assert.IsTrue(b1 != b4);
Expand Down Expand Up @@ -154,6 +159,65 @@ public void CopyFromPortion()
Assert.AreEqual(3, bs[1]);
}

[Test]
public void CopyTo()
{
byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
ByteString bs = ByteString.CopyFrom(data);

byte[] dest = new byte[data.Length];
bs.CopyTo(dest, 0);

CollectionAssert.AreEqual(data, dest);
}

[Test]
public void GetEnumerator()
{
byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
ByteString bs = ByteString.CopyFrom(data);

IEnumerator<byte> genericEnumerator = bs.GetEnumerator();
Assert.IsTrue(genericEnumerator.MoveNext());
Assert.AreEqual(0, genericEnumerator.Current);

IEnumerator enumerator = ((IEnumerable)bs).GetEnumerator();
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual(0, enumerator.Current);

// Call via LINQ
CollectionAssert.AreEqual(bs.Span.ToArray(), bs.ToArray());
}

[Test]
public void UnsafeWrap()
{
byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
ByteString bs = UnsafeByteOperations.UnsafeWrap(data.AsMemory(2, 3));
ReadOnlySpan<byte> s = bs.Span;

Assert.AreEqual(3, s.Length);
Assert.AreEqual(2, s[0]);
Assert.AreEqual(3, s[1]);
Assert.AreEqual(4, s[2]);

// Check that the value is not a copy
data[2] = byte.MaxValue;
Assert.AreEqual(byte.MaxValue, s[0]);
}

[Test]
public void WriteToStream()
{
byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6 };
ByteString bs = ByteString.CopyFrom(data);

MemoryStream ms = new MemoryStream();
bs.WriteTo(ms);

CollectionAssert.AreEqual(data, ms.ToArray());
}

[Test]
public void ToStringUtf8()
{
Expand Down

0 comments on commit 9b4fb0b

Please sign in to comment.