Copied dotnet code from CosmosDB repository

This commit is contained in:
David Noble
2019-08-20 11:58:29 -07:00
parent b0e89b0dda
commit 31f3bc828b
201 changed files with 37803 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -0,0 +1,119 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma warning disable SA1401 // Fields should be private
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.RecordIO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Schemas;
using Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator;
using Microsoft.VisualStudio.TestTools.UnitTesting;
public class BenchmarkSuiteBase
{
private protected const int InitialCapacity = 2 * 1024 * 1024;
private protected LayoutResolverNamespace DefaultResolver = (LayoutResolverNamespace)SystemSchema.LayoutResolver;
private protected async Task<(List<Dictionary<Utf8String, object>>, LayoutResolverNamespace)> LoadExpectedAsync(string expectedFile)
{
LayoutResolverNamespace resolver = this.DefaultResolver;
List<Dictionary<Utf8String, object>> expected = new List<Dictionary<Utf8String, object>>();
using (Stream stm = new FileStream(expectedFile, FileMode.Open))
{
// Read a RecordIO stream.
MemorySpanResizer<byte> resizer = new MemorySpanResizer<byte>(BenchmarkSuiteBase.InitialCapacity);
Result r = await stm.ReadRecordIOAsync(
record =>
{
r = BenchmarkSuiteBase.LoadOneRow(record, resolver, out Dictionary<Utf8String, object> rowValue);
ResultAssert.IsSuccess(r);
expected.Add(rowValue);
return Result.Success;
},
segment =>
{
r = SegmentSerializer.Read(segment.Span, SystemSchema.LayoutResolver, out Segment s);
ResultAssert.IsSuccess(r);
Assert.IsNotNull(s.SDL);
resolver = new LayoutResolverNamespace(Namespace.Parse(s.SDL), resolver);
return Result.Success;
},
resizer);
ResultAssert.IsSuccess(r);
}
return (expected, resolver);
}
private protected static async Task WriteAllRowsAsync(
string file,
string sdl,
LayoutResolver resolver,
Layout layout,
List<Dictionary<Utf8String, object>> rows)
{
using (Stream stm = new FileStream(file, FileMode.Truncate))
{
// Create a reusable, resizable buffer.
MemorySpanResizer<byte> resizer = new MemorySpanResizer<byte>(BenchmarkSuiteBase.InitialCapacity);
// Write a RecordIO stream.
Result r = await stm.WriteRecordIOAsync(
new Segment("HybridRow.Tests.Perf Expected Results", sdl),
(long index, out ReadOnlyMemory<byte> body) =>
{
body = default;
if (index >= rows.Count)
{
return Result.Success;
}
StreamingRowGenerator writer = new StreamingRowGenerator(
BenchmarkSuiteBase.InitialCapacity,
layout,
resolver,
resizer);
Result r2 = writer.WriteBuffer(rows[(int)index]);
if (r2 != Result.Success)
{
return r2;
}
body = resizer.Memory.Slice(0, writer.Length);
return Result.Success;
});
ResultAssert.IsSuccess(r);
}
}
private protected static Result LoadOneRow(Memory<byte> buffer, LayoutResolver resolver, out Dictionary<Utf8String, object> rowValue)
{
RowBuffer row = new RowBuffer(buffer.Span, HybridRowVersion.V1, resolver);
RowReader reader = new RowReader(ref row);
return DiagnosticConverter.ReaderToDynamic(ref reader, out rowValue);
}
private protected static class ResultAssert
{
public static void IsSuccess(Result actual)
{
if (actual != Result.Success)
{
Assert.AreEqual(Result.Success, actual);
}
}
}
}
}

View File

@@ -0,0 +1,110 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using MongoDB.Bson.IO;
internal sealed class BsonJsonModelRowGenerator : IDisposable
{
private readonly MemoryStream stream;
private readonly BsonWriter writer;
public BsonJsonModelRowGenerator(int capacity)
{
this.stream = new MemoryStream(capacity);
this.writer = new BsonBinaryWriter(this.stream);
}
public int Length => (int)this.stream.Position;
public byte[] ToArray()
{
return this.stream.ToArray();
}
public void Reset()
{
this.stream.SetLength(0);
this.stream.Position = 0;
}
public void WriteBuffer(Dictionary<Utf8String, object> dict)
{
this.writer.WriteStartDocument();
foreach ((Utf8String propPath, object propValue) in dict)
{
this.JsonModelSwitch(propPath, propValue);
}
this.writer.WriteEndDocument();
}
public void Dispose()
{
this.writer.Dispose();
this.stream.Dispose();
}
private void JsonModelSwitch(Utf8String path, object value)
{
if (path != null)
{
this.writer.WriteName(path.ToString());
}
switch (value)
{
case null:
this.writer.WriteNull();
return;
case bool x:
this.writer.WriteBoolean(x);
return;
case long x:
this.writer.WriteInt64(x);
return;
case double x:
this.writer.WriteDouble(x);
return;
case string x:
this.writer.WriteString(x);
return;
case Utf8String x:
this.writer.WriteString(x.ToString());
return;
case byte[] x:
this.writer.WriteBytes(x);
return;
case Dictionary<Utf8String, object> x:
this.writer.WriteStartDocument();
foreach ((Utf8String propPath, object propValue) in x)
{
this.JsonModelSwitch(propPath, propValue);
}
this.writer.WriteEndDocument();
return;
case List<object> x:
this.writer.WriteStartArray();
foreach (object item in x)
{
this.JsonModelSwitch(null, item);
}
this.writer.WriteEndArray();
return;
default:
Contract.Assert(false, $"Unknown type will be ignored: {value.GetType().Name}");
return;
}
}
}
}

View File

@@ -0,0 +1,63 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using MongoDB.Bson;
using MongoDB.Bson.IO;
internal static class BsonReaderExtensions
{
public static void VisitBsonDocument(this BsonReader bsonReader)
{
bsonReader.ReadStartDocument();
BsonType type;
while ((type = bsonReader.ReadBsonType()) != BsonType.EndOfDocument)
{
string path = bsonReader.ReadName();
switch (type)
{
case BsonType.Array:
bsonReader.VisitBsonArray();
break;
case BsonType.Document:
bsonReader.VisitBsonDocument();
break;
default:
bsonReader.SkipValue();
break;
}
}
bsonReader.ReadEndDocument();
}
private static void VisitBsonArray(this BsonReader bsonReader)
{
bsonReader.ReadStartArray();
BsonType type;
while ((type = bsonReader.ReadBsonType()) != BsonType.EndOfDocument)
{
switch (type)
{
case BsonType.Array:
bsonReader.VisitBsonArray();
break;
case BsonType.Document:
bsonReader.VisitBsonDocument();
break;
default:
bsonReader.SkipValue();
break;
}
}
bsonReader.ReadEndArray();
}
}
}

View File

@@ -0,0 +1,303 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using MongoDB.Bson;
using MongoDB.Bson.IO;
internal sealed class BsonRowGenerator : IDisposable
{
private readonly MemoryStream stream;
private readonly BsonWriter writer;
private readonly Layout layout;
private readonly LayoutResolver resolver;
public BsonRowGenerator(int capacity, Layout layout, LayoutResolver resolver)
{
this.stream = new MemoryStream(capacity);
this.writer = new BsonBinaryWriter(this.stream);
this.layout = layout;
this.resolver = resolver;
}
public int Length => (int)this.stream.Position;
public byte[] ToArray()
{
return this.stream.ToArray();
}
public void Reset()
{
this.stream.SetLength(0);
this.stream.Position = 0;
}
public void WriteBuffer(Dictionary<Utf8String, object> dict)
{
this.writer.WriteStartDocument();
foreach (LayoutColumn c in this.layout.Columns)
{
this.LayoutCodeSwitch(c.Path, c.TypeArg, dict[c.Path]);
}
this.writer.WriteEndDocument();
}
public void Dispose()
{
this.writer.Dispose();
this.stream.Dispose();
}
private void LayoutCodeSwitch(UtfAnyString path, TypeArgument typeArg, object value)
{
if (!path.IsNull)
{
this.writer.WriteName(path);
}
switch (typeArg.Type.LayoutCode)
{
case LayoutCode.Null:
this.writer.WriteNull();
return;
case LayoutCode.Boolean:
this.writer.WriteBoolean(value == null ? default(bool) : (bool)value);
return;
case LayoutCode.Int8:
this.writer.WriteInt32(value == null ? default(sbyte) : (sbyte)value);
return;
case LayoutCode.Int16:
this.writer.WriteInt32(value == null ? default(short) : (short)value);
return;
case LayoutCode.Int32:
this.writer.WriteInt32(value == null ? default(int) : (int)value);
return;
case LayoutCode.Int64:
this.writer.WriteInt64(value == null ? default(long) : (long)value);
return;
case LayoutCode.UInt8:
this.writer.WriteInt32(value == null ? default(byte) : (byte)value);
return;
case LayoutCode.UInt16:
this.writer.WriteInt32(value == null ? default(ushort) : (ushort)value);
return;
case LayoutCode.UInt32:
this.writer.WriteInt32(value == null ? default(int) : unchecked((int)(uint)value));
return;
case LayoutCode.UInt64:
this.writer.WriteInt64(value == null ? default(long) : unchecked((long)(ulong)value));
return;
case LayoutCode.VarInt:
this.writer.WriteInt64(value == null ? default(long) : (long)value);
return;
case LayoutCode.VarUInt:
this.writer.WriteInt64(value == null ? default(long) : unchecked((long)(ulong)value));
return;
case LayoutCode.Float32:
this.writer.WriteDouble(value == null ? default(float) : (float)value);
return;
case LayoutCode.Float64:
this.writer.WriteDouble(value == null ? default(double) : (double)value);
return;
case LayoutCode.Float128:
Decimal128 d128 = default(Decimal128);
if (value != null)
{
Float128 f128 = (Float128)value;
d128 = unchecked(Decimal128.FromIEEEBits((ulong)f128.High, (ulong)f128.Low));
}
this.writer.WriteDecimal128(d128);
return;
case LayoutCode.Decimal:
this.writer.WriteDecimal128(value == null ? default(Decimal128) : new Decimal128((decimal)value));
return;
case LayoutCode.DateTime:
this.writer.WriteDateTime(value == null ? default(long) : ((DateTime)value).Ticks);
return;
case LayoutCode.UnixDateTime:
this.writer.WriteDateTime(value == null ? default(long) : ((UnixDateTime)value).Milliseconds);
return;
case LayoutCode.Guid:
this.writer.WriteString(value == null ? string.Empty : ((Guid)value).ToString());
return;
case LayoutCode.MongoDbObjectId:
this.writer.WriteObjectId(value == null ? default(ObjectId) : new ObjectId(((MongoDbObjectId)value).ToByteArray()));
return;
case LayoutCode.Utf8:
this.writer.WriteString(value == null ? string.Empty : ((Utf8String)value).ToString());
return;
case LayoutCode.Binary:
this.writer.WriteBytes(value == null ? default(byte[]) : (byte[])value);
return;
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
this.DispatchObject(typeArg, value);
return;
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
this.DispatchArray(typeArg, value);
return;
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
this.DispatchSet(typeArg, value);
return;
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
this.DispatchMap(typeArg, value);
return;
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
this.DispatchTuple(typeArg, value);
return;
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
this.DispatchNullable(typeArg, value);
return;
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
this.DispatchUDT(typeArg, value);
return;
default:
Contract.Assert(false, $"Unknown type will be ignored: {typeArg}");
return;
}
}
private void DispatchObject(TypeArgument typeArg, object value)
{
this.writer.WriteStartDocument();
// TODO: support properties in an object scope.
this.writer.WriteEndDocument();
}
private void DispatchArray(TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count == 1);
this.writer.WriteStartArray();
foreach (object item in (List<object>)value)
{
this.LayoutCodeSwitch(null, typeArg.TypeArgs[0], item);
}
this.writer.WriteEndArray();
}
private void DispatchTuple(TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count >= 2);
List<object> items = (List<object>)value;
Contract.Assert(items.Count == typeArg.TypeArgs.Count);
this.writer.WriteStartArray();
for (int i = 0; i < items.Count; i++)
{
object item = items[i];
this.LayoutCodeSwitch(null, typeArg.TypeArgs[i], item);
}
this.writer.WriteEndArray();
}
private void DispatchNullable(TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count == 1);
if (value != null)
{
this.LayoutCodeSwitch(null, typeArg.TypeArgs[0], value);
}
else
{
this.writer.WriteNull();
}
}
private void DispatchSet(TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count == 1);
this.writer.WriteStartArray();
foreach (object item in (List<object>)value)
{
this.LayoutCodeSwitch(null, typeArg.TypeArgs[0], item);
}
this.writer.WriteEndArray();
}
private void DispatchMap(TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count == 2);
this.writer.WriteStartArray();
foreach (object item in (List<object>)value)
{
this.DispatchTuple(typeArg, item);
}
this.writer.WriteEndArray();
}
private void DispatchUDT(TypeArgument typeArg, object value)
{
this.writer.WriteStartDocument();
Dictionary<Utf8String, object> dict = (Dictionary<Utf8String, object>)value;
Layout udt = this.resolver.Resolve(typeArg.TypeArgs.SchemaId);
foreach (LayoutColumn c in udt.Columns)
{
this.LayoutCodeSwitch(c.Path, c.TypeArg, dict[c.Path]);
}
this.writer.WriteEndDocument();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,293 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Tests involving generated (early bound) code compiled from schema based on a partial implementation
/// of Cassandra Hotel Schema described here: https://www.oreilly.com/ideas/cassandra-data-modeling .
/// <para>
/// The tests here differ from <see cref="SchematizedMicroBenchmarkSuite" /> in that they rely on
/// the schema being known at compile time instead of runtime. This allows code to be generated that
/// directly addresses the schema structure instead of dynamically discovering schema structure at
/// runtime.
/// </para>
/// </summary>
[TestClass]
public sealed class CodeGenMicroBenchmarkSuite : MicroBenchmarkSuiteBase
{
private const int GuestCount = 1000;
private const int HotelCount = 10000;
private const int RoomsCount = 10000;
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task ProtobufGuestsWriteBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.ProtobufWriteBenchmark("Guests", "Guests", CodeGenMicroBenchmarkSuite.GuestCount, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task ProtobufHotelWriteBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.ProtobufWriteBenchmark("Hotels", "Hotels", CodeGenMicroBenchmarkSuite.HotelCount, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task ProtobufRoomsWriteBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.ProtobufWriteBenchmark(
"Available_Rooms_By_Hotel_Date",
"Rooms",
CodeGenMicroBenchmarkSuite.RoomsCount,
expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task ProtobufGuestsReadBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.ProtobufReadBenchmark("Guests", "Guests", CodeGenMicroBenchmarkSuite.GuestCount, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task ProtobufHotelReadBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.ProtobufReadBenchmark("Hotels", "Hotels", CodeGenMicroBenchmarkSuite.HotelCount, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task ProtobufRoomsReadBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.ProtobufReadBenchmark(
"Available_Rooms_By_Hotel_Date",
"Rooms",
CodeGenMicroBenchmarkSuite.RoomsCount,
expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task CodeGenGuestsWriteBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.CodeGenWriteBenchmark(resolver, "Guests", "Guests", CodeGenMicroBenchmarkSuite.GuestCount, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task CodeGenHotelWriteBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.CodeGenWriteBenchmark(resolver, "Hotels", "Hotels", CodeGenMicroBenchmarkSuite.HotelCount, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task CodeGenRoomsWriteBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.CodeGenWriteBenchmark(
resolver,
"Available_Rooms_By_Hotel_Date",
"Rooms",
CodeGenMicroBenchmarkSuite.RoomsCount,
expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task CodeGenGuestsReadBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.CodeGenReadBenchmark(resolver, "Guests", "Guests", CodeGenMicroBenchmarkSuite.GuestCount, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task CodeGenHotelReadBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.CodeGenReadBenchmark(resolver, "Hotels", "Hotels", CodeGenMicroBenchmarkSuite.HotelCount, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task CodeGenRoomsReadBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
CodeGenMicroBenchmarkSuite.CodeGenReadBenchmark(
resolver,
"Available_Rooms_By_Hotel_Date",
"Rooms",
CodeGenMicroBenchmarkSuite.RoomsCount,
expected);
}
private static void ProtobufWriteBenchmark(
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
BenchmarkContext context = new BenchmarkContext
{
ProtobufWriter = new ProtobufRowGenerator(schemaName, BenchmarkSuiteBase.InitialCapacity)
};
MicroBenchmarkSuiteBase.Benchmark(
"CodeGen",
"Write",
dataSetName,
"Protobuf",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> tableValue) => { ctx.ProtobufWriter.WriteBuffer(tableValue); },
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> tableValue) => ctx.ProtobufWriter.Length,
expected);
}
private static void ProtobufReadBenchmark(
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
// Serialize input data to sequence of byte buffers.
List<byte[]> expectedSerialized = new List<byte[]>(expected.Count);
BenchmarkContext context = new BenchmarkContext
{
ProtobufWriter = new ProtobufRowGenerator(schemaName, BenchmarkSuiteBase.InitialCapacity)
};
foreach (Dictionary<Utf8String, object> tableValue in expected)
{
context.ProtobufWriter.WriteBuffer(tableValue);
expectedSerialized.Add(context.ProtobufWriter.ToArray());
}
MicroBenchmarkSuiteBase.Benchmark(
"CodeGen",
"Read",
dataSetName,
"Protobuf",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, byte[] tableValue) => ctx.ProtobufWriter.ReadBuffer(tableValue),
(ref BenchmarkContext ctx, byte[] tableValue) => tableValue.Length,
expectedSerialized);
}
private static void CodeGenWriteBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
BenchmarkContext context = new BenchmarkContext
{
CodeGenWriter = new CodeGenRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver)
};
MicroBenchmarkSuiteBase.Benchmark(
"CodeGen",
"Write",
dataSetName,
"HybridRowGen",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> tableValue) =>
{
ctx.CodeGenWriter.Reset();
Result r = ctx.CodeGenWriter.WriteBuffer(tableValue);
ResultAssert.IsSuccess(r);
},
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> tableValue) => ctx.CodeGenWriter.Length,
expected);
}
private static void CodeGenReadBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
// Serialize input data to sequence of byte buffers.
List<byte[]> expectedSerialized = new List<byte[]>(expected.Count);
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
BenchmarkContext context = new BenchmarkContext
{
CodeGenWriter = new CodeGenRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver)
};
foreach (Dictionary<Utf8String, object> tableValue in expected)
{
context.CodeGenWriter.Reset();
Result r = context.CodeGenWriter.WriteBuffer(tableValue);
ResultAssert.IsSuccess(r);
expectedSerialized.Add(context.CodeGenWriter.ToArray());
}
MicroBenchmarkSuiteBase.Benchmark(
"CodeGen",
"Read",
dataSetName,
"HybridRowGen",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, byte[] tableValue) =>
{
Result r = ctx.CodeGenWriter.ReadBuffer(tableValue);
ResultAssert.IsSuccess(r);
},
(ref BenchmarkContext ctx, byte[] tableValue) => tableValue.Length,
expectedSerialized);
}
}
}

View File

@@ -0,0 +1,872 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
internal ref struct CodeGenRowGenerator
{
private RowBuffer row;
private readonly HybridRowSerializer dispatcher;
public CodeGenRowGenerator(int capacity, Layout layout, LayoutResolver resolver, ISpanResizer<byte> resizer = default)
{
this.row = new RowBuffer(capacity, resizer);
this.row.InitLayout(HybridRowVersion.V1, layout, resolver);
switch (layout.Name)
{
case "Hotels":
this.dispatcher = new HotelsHybridRowSerializer(layout, resolver);
break;
case "Guests":
this.dispatcher = new GuestsHybridRowSerializer(layout, resolver);
break;
case "Available_Rooms_By_Hotel_Date":
this.dispatcher = new RoomsHybridRowSerializer(layout, resolver);
break;
default:
Contract.Fail($"Unknown schema will be ignored: {layout.Name}");
this.dispatcher = null;
break;
}
}
public int Length => this.row.Length;
public byte[] ToArray()
{
return this.row.ToArray();
}
public void Reset()
{
Layout layout = this.row.Resolver.Resolve(this.row.Header.SchemaId);
this.row.InitLayout(HybridRowVersion.V1, layout, this.row.Resolver);
}
public Result WriteBuffer(Dictionary<Utf8String, object> tableValue)
{
RowCursor root = RowCursor.Create(ref this.row);
return this.dispatcher.WriteBuffer(ref this.row, ref root, tableValue);
}
public Result ReadBuffer(byte[] buffer)
{
this.row = new RowBuffer(buffer.AsSpan(), HybridRowVersion.V1, this.row.Resolver);
RowCursor root = RowCursor.Create(ref this.row);
return this.dispatcher.ReadBuffer(ref this.row, ref root);
}
private abstract class HybridRowSerializer
{
public abstract Result WriteBuffer(ref RowBuffer row, ref RowCursor root, Dictionary<Utf8String, object> tableValue);
public abstract Result ReadBuffer(ref RowBuffer row, ref RowCursor root);
}
private sealed class GuestsHybridRowSerializer : HybridRowSerializer
{
private static readonly Utf8String GuestIdName = Utf8String.TranscodeUtf16("guest_id");
private static readonly Utf8String FirstNameName = Utf8String.TranscodeUtf16("first_name");
private static readonly Utf8String LastNameName = Utf8String.TranscodeUtf16("last_name");
private static readonly Utf8String TitleName = Utf8String.TranscodeUtf16("title");
private static readonly Utf8String EmailsName = Utf8String.TranscodeUtf16("emails");
private static readonly Utf8String PhoneNumbersName = Utf8String.TranscodeUtf16("phone_numbers");
private static readonly Utf8String AddressesName = Utf8String.TranscodeUtf16("addresses");
private static readonly Utf8String ConfirmNumberName = Utf8String.TranscodeUtf16("confirm_number");
private readonly LayoutColumn guestId;
private readonly LayoutColumn firstName;
private readonly LayoutColumn lastName;
private readonly LayoutColumn title;
private readonly LayoutColumn emails;
private readonly LayoutColumn phoneNumbers;
private readonly LayoutColumn addresses;
private readonly LayoutColumn confirmNumber;
private readonly StringToken emailsToken;
private readonly StringToken phoneNumbersToken;
private readonly StringToken addressesToken;
private readonly TypeArgumentList addressesFieldType;
private readonly AddressHybridRowSerializer addressSerializer;
private readonly LayoutScope.WriterFunc<Dictionary<Utf8String, object>> addressSerializerWriter;
public GuestsHybridRowSerializer(Layout layout, LayoutResolver resolver)
{
layout.TryFind(GuestsHybridRowSerializer.GuestIdName, out this.guestId);
layout.TryFind(GuestsHybridRowSerializer.FirstNameName, out this.firstName);
layout.TryFind(GuestsHybridRowSerializer.LastNameName, out this.lastName);
layout.TryFind(GuestsHybridRowSerializer.TitleName, out this.title);
layout.TryFind(GuestsHybridRowSerializer.EmailsName, out this.emails);
layout.TryFind(GuestsHybridRowSerializer.PhoneNumbersName, out this.phoneNumbers);
layout.TryFind(GuestsHybridRowSerializer.AddressesName, out this.addresses);
layout.TryFind(GuestsHybridRowSerializer.ConfirmNumberName, out this.confirmNumber);
layout.Tokenizer.TryFindToken(this.emails.Path, out this.emailsToken);
layout.Tokenizer.TryFindToken(this.phoneNumbers.Path, out this.phoneNumbersToken);
layout.Tokenizer.TryFindToken(this.addresses.Path, out this.addressesToken);
this.addressesFieldType = new TypeArgumentList(
new[]
{
new TypeArgument(LayoutType.TypedTuple, this.addresses.TypeArgs)
});
this.addressSerializer = new AddressHybridRowSerializer(resolver.Resolve(this.addresses.TypeArgs[1].TypeArgs.SchemaId), resolver);
this.addressSerializerWriter = this.addressSerializer.WriteBuffer;
}
public override Result WriteBuffer(ref RowBuffer row, ref RowCursor root, Dictionary<Utf8String, object> tableValue)
{
foreach ((Utf8String key, object value) in tableValue)
{
Result r;
switch (0)
{
case 0 when key.Equals(GuestsHybridRowSerializer.GuestIdName):
if (value != null)
{
r = LayoutType.Guid.WriteFixed(ref row, ref root, this.guestId, (Guid)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(GuestsHybridRowSerializer.FirstNameName):
if (value != null)
{
r = LayoutType.Utf8.WriteVariable(ref row, ref root, this.firstName, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(GuestsHybridRowSerializer.LastNameName):
if (value != null)
{
r = LayoutType.Utf8.WriteVariable(ref row, ref root, this.lastName, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(GuestsHybridRowSerializer.TitleName):
if (value != null)
{
r = LayoutType.Utf8.WriteVariable(ref row, ref root, this.title, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(GuestsHybridRowSerializer.EmailsName):
if (value != null)
{
root.Find(ref row, this.emailsToken);
r = LayoutType.TypedArray.WriteScope(
ref row,
ref root,
this.emails.TypeArgs,
(List<object>)value,
(ref RowBuffer row2, ref RowCursor childScope, List<object> context) =>
{
foreach (object item in context)
{
Result r2 = LayoutType.Utf8.WriteSparse(ref row2, ref childScope, (Utf8String)item);
if (r2 != Result.Success)
{
return r2;
}
childScope.MoveNext(ref row2);
}
return Result.Success;
});
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(GuestsHybridRowSerializer.PhoneNumbersName):
if (value != null)
{
root.Find(ref row, this.phoneNumbersToken);
r = LayoutType.TypedArray.WriteScope(ref row, ref root, this.phoneNumbers.TypeArgs, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
foreach (object item in (List<object>)value)
{
r = LayoutType.Utf8.WriteSparse(ref row, ref childScope, (Utf8String)item);
if (r != Result.Success)
{
return r;
}
childScope.MoveNext(ref row);
}
root.Skip(ref row, ref childScope);
}
break;
case 0 when key.Equals(GuestsHybridRowSerializer.AddressesName):
if (value != null)
{
root.Find(ref row, this.addressesToken);
r = LayoutType.TypedMap.WriteScope(
ref row,
ref root,
this.addresses.TypeArgs,
(this, (List<object>)value),
(ref RowBuffer row2, ref RowCursor childScope, (GuestsHybridRowSerializer _this, List<object> value) ctx) =>
{
foreach (object item in ctx.value)
{
Result r2 = LayoutType.TypedTuple.WriteScope(
ref row2,
ref childScope,
ctx._this.addresses.TypeArgs,
(ctx._this, (List<object>)item),
(ref RowBuffer row3, ref RowCursor tupleScope, (GuestsHybridRowSerializer _this, List<object> value) ctx2) =>
{
Result r3 = LayoutType.Utf8.WriteSparse(ref row3, ref tupleScope, (Utf8String)ctx2.value[0]);
if (r3 != Result.Success)
{
return r3;
}
tupleScope.MoveNext(ref row3);
return LayoutType.ImmutableUDT.WriteScope(
ref row3,
ref tupleScope,
ctx2._this.addresses.TypeArgs[1].TypeArgs,
(Dictionary<Utf8String, object>)ctx2.value[1],
ctx2._this.addressSerializerWriter);
});
if (r2 != Result.Success)
{
return r2;
}
childScope.MoveNext(ref row2);
}
return Result.Success;
});
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(GuestsHybridRowSerializer.ConfirmNumberName):
if (value != null)
{
r = LayoutType.Utf8.WriteVariable(ref row, ref root, this.confirmNumber, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
default:
Contract.Fail($"Unknown property name: {key}");
break;
}
}
return Result.Success;
}
public override Result ReadBuffer(ref RowBuffer row, ref RowCursor root)
{
Result r = LayoutType.Guid.ReadFixed(ref row, ref root, this.guestId, out Guid _);
if (r != Result.Success)
{
return r;
}
r = LayoutType.Utf8.ReadVariable(ref row, ref root, this.firstName, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
r = LayoutType.Utf8.ReadVariable(ref row, ref root, this.lastName, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
r = LayoutType.Utf8.ReadVariable(ref row, ref root, this.title, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
root.Find(ref row, this.emailsToken);
r = LayoutType.TypedArray.ReadScope(ref row, ref root, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
while (childScope.MoveNext(ref row))
{
r = LayoutType.Utf8.ReadSparse(ref row, ref childScope, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
}
root.Skip(ref row, ref childScope);
root.Find(ref row, this.phoneNumbersToken);
r = LayoutType.TypedArray.ReadScope(ref row, ref root, out childScope);
if (r != Result.Success)
{
return r;
}
while (childScope.MoveNext(ref row))
{
r = LayoutType.Utf8.ReadSparse(ref row, ref childScope, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
}
root.Skip(ref row, ref childScope);
root.Find(ref row, this.addressesToken);
r = LayoutType.TypedMap.ReadScope(ref row, ref root, out childScope);
if (r != Result.Success)
{
return r;
}
while (childScope.MoveNext(ref row))
{
r = LayoutType.TypedTuple.ReadScope(ref row, ref childScope, out RowCursor tupleScope);
if (r != Result.Success)
{
return r;
}
if (!tupleScope.MoveNext(ref row))
{
return Result.InvalidRow;
}
r = LayoutType.Utf8.ReadSparse(ref row, ref tupleScope, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
if (!tupleScope.MoveNext(ref row))
{
return Result.InvalidRow;
}
r = LayoutType.ImmutableUDT.ReadScope(ref row, ref tupleScope, out RowCursor valueScope);
if (r != Result.Success)
{
return r;
}
r = this.addressSerializer.ReadBuffer(ref row, ref valueScope);
if (r != Result.Success)
{
return r;
}
tupleScope.Skip(ref row, ref valueScope);
childScope.Skip(ref row, ref tupleScope);
}
root.Skip(ref row, ref childScope);
return LayoutType.Utf8.ReadVariable(ref row, ref root, this.confirmNumber, out Utf8Span _);
}
}
private sealed class HotelsHybridRowSerializer : HybridRowSerializer
{
private static readonly Utf8String HotelIdName = Utf8String.TranscodeUtf16("hotel_id");
private static readonly Utf8String NameName = Utf8String.TranscodeUtf16("name");
private static readonly Utf8String PhoneName = Utf8String.TranscodeUtf16("phone");
private static readonly Utf8String AddressName = Utf8String.TranscodeUtf16("address");
private readonly LayoutColumn hotelId;
private readonly LayoutColumn name;
private readonly LayoutColumn phone;
private readonly LayoutColumn address;
private readonly StringToken addressToken;
private readonly AddressHybridRowSerializer addressSerializer;
public HotelsHybridRowSerializer(Layout layout, LayoutResolver resolver)
{
layout.TryFind(HotelsHybridRowSerializer.HotelIdName, out this.hotelId);
layout.TryFind(HotelsHybridRowSerializer.NameName, out this.name);
layout.TryFind(HotelsHybridRowSerializer.PhoneName, out this.phone);
layout.TryFind(HotelsHybridRowSerializer.AddressName, out this.address);
layout.Tokenizer.TryFindToken(this.address.Path, out this.addressToken);
this.addressSerializer = new AddressHybridRowSerializer(resolver.Resolve(this.address.TypeArgs.SchemaId), resolver);
}
public override Result WriteBuffer(ref RowBuffer row, ref RowCursor root, Dictionary<Utf8String, object> tableValue)
{
foreach ((Utf8String key, object value) in tableValue)
{
Result r;
switch (0)
{
case 0 when key.Equals(HotelsHybridRowSerializer.HotelIdName):
if (value != null)
{
r = LayoutType.Utf8.WriteFixed(ref row, ref root, this.hotelId, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(HotelsHybridRowSerializer.NameName):
if (value != null)
{
r = LayoutType.Utf8.WriteVariable(ref row, ref root, this.name, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(HotelsHybridRowSerializer.PhoneName):
if (value != null)
{
r = LayoutType.Utf8.WriteVariable(ref row, ref root, this.phone, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(HotelsHybridRowSerializer.AddressName):
if (value != null)
{
root.Find(ref row, this.addressToken);
r = LayoutType.UDT.WriteScope(ref row, ref root, this.address.TypeArgs, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
r = this.addressSerializer.WriteBuffer(ref row, ref childScope, (Dictionary<Utf8String, object>)value);
if (r != Result.Success)
{
return r;
}
root.Skip(ref row, ref childScope);
}
break;
default:
Contract.Fail($"Unknown property name: {key}");
break;
}
}
return Result.Success;
}
public override Result ReadBuffer(ref RowBuffer row, ref RowCursor root)
{
Result r = LayoutType.Utf8.ReadFixed(ref row, ref root, this.hotelId, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
r = LayoutType.Utf8.ReadVariable(ref row, ref root, this.name, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
r = LayoutType.Utf8.ReadVariable(ref row, ref root, this.phone, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
root.Find(ref row, this.addressToken);
r = LayoutType.UDT.ReadScope(ref row, ref root, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
r = this.addressSerializer.ReadBuffer(ref row, ref childScope);
if (r != Result.Success)
{
return r;
}
root.Skip(ref row, ref childScope);
return Result.Success;
}
}
private sealed class RoomsHybridRowSerializer : HybridRowSerializer
{
private static readonly Utf8String HotelIdName = Utf8String.TranscodeUtf16("hotel_id");
private static readonly Utf8String DateName = Utf8String.TranscodeUtf16("date");
private static readonly Utf8String RoomNumberName = Utf8String.TranscodeUtf16("room_number");
private static readonly Utf8String IsAvailableName = Utf8String.TranscodeUtf16("is_available");
private readonly LayoutColumn hotelId;
private readonly LayoutColumn date;
private readonly LayoutColumn roomNumber;
private readonly LayoutColumn isAvailable;
// ReSharper disable once UnusedParameter.Local
public RoomsHybridRowSerializer(Layout layout, LayoutResolver resolver)
{
layout.TryFind(RoomsHybridRowSerializer.HotelIdName, out this.hotelId);
layout.TryFind(RoomsHybridRowSerializer.DateName, out this.date);
layout.TryFind(RoomsHybridRowSerializer.RoomNumberName, out this.roomNumber);
layout.TryFind(RoomsHybridRowSerializer.IsAvailableName, out this.isAvailable);
}
public override Result WriteBuffer(ref RowBuffer row, ref RowCursor root, Dictionary<Utf8String, object> tableValue)
{
foreach ((Utf8String key, object value) in tableValue)
{
Result r;
switch (0)
{
case 0 when key.Equals(RoomsHybridRowSerializer.HotelIdName):
if (value != null)
{
r = LayoutType.Utf8.WriteFixed(ref row, ref root, this.hotelId, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(RoomsHybridRowSerializer.DateName):
if (value != null)
{
r = LayoutType.DateTime.WriteFixed(ref row, ref root, this.date, (DateTime)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(RoomsHybridRowSerializer.RoomNumberName):
if (value != null)
{
r = LayoutType.UInt8.WriteFixed(ref row, ref root, this.roomNumber, (byte)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(RoomsHybridRowSerializer.IsAvailableName):
if (value != null)
{
r = LayoutType.Boolean.WriteFixed(ref row, ref root, this.isAvailable, (bool)value);
if (r != Result.Success)
{
return r;
}
}
break;
default:
Contract.Fail($"Unknown property name: {key}");
break;
}
}
return Result.Success;
}
public override Result ReadBuffer(ref RowBuffer row, ref RowCursor root)
{
Result r = LayoutType.Utf8.ReadFixed(ref row, ref root, this.hotelId, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
r = LayoutType.DateTime.ReadFixed(ref row, ref root, this.date, out DateTime _);
if (r != Result.Success)
{
return r;
}
r = LayoutType.UInt8.ReadFixed(ref row, ref root, this.roomNumber, out byte _);
if (r != Result.Success)
{
return r;
}
return LayoutType.Boolean.ReadFixed(ref row, ref root, this.isAvailable, out bool _);
}
}
private sealed class PostalCodeHybridRowSerializer : HybridRowSerializer
{
private static readonly Utf8String ZipName = Utf8String.TranscodeUtf16("zip");
private static readonly Utf8String Plus4Name = Utf8String.TranscodeUtf16("plus4");
private readonly LayoutColumn zip;
private readonly LayoutColumn plus4;
private readonly StringToken plus4Token;
// ReSharper disable once UnusedParameter.Local
public PostalCodeHybridRowSerializer(Layout layout, LayoutResolver resolver)
{
layout.TryFind(PostalCodeHybridRowSerializer.ZipName, out this.zip);
layout.TryFind(PostalCodeHybridRowSerializer.Plus4Name, out this.plus4);
layout.Tokenizer.TryFindToken(this.plus4.Path, out this.plus4Token);
}
public override Result WriteBuffer(ref RowBuffer row, ref RowCursor root, Dictionary<Utf8String, object> tableValue)
{
foreach ((Utf8String key, object value) in tableValue)
{
Result r;
switch (0)
{
case 0 when key.Equals(PostalCodeHybridRowSerializer.ZipName):
if (value != null)
{
r = LayoutType.Int32.WriteFixed(ref row, ref root, this.zip, (int)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(PostalCodeHybridRowSerializer.Plus4Name):
if (value != null)
{
root.Find(ref row, this.plus4Token);
r = LayoutType.Int16.WriteSparse(ref row, ref root, (short)value);
if (r != Result.Success)
{
return r;
}
}
break;
default:
Contract.Fail($"Unknown property name: {key}");
break;
}
}
return Result.Success;
}
public override Result ReadBuffer(ref RowBuffer row, ref RowCursor root)
{
Result r = LayoutType.Int32.ReadFixed(ref row, ref root, this.zip, out int _);
if (r != Result.Success)
{
return r;
}
root.Find(ref row, this.plus4Token);
return LayoutType.Int16.ReadSparse(ref row, ref root, out short _);
}
}
private sealed class AddressHybridRowSerializer : HybridRowSerializer
{
private static readonly Utf8String StreetName = Utf8String.TranscodeUtf16("street");
private static readonly Utf8String CityName = Utf8String.TranscodeUtf16("city");
private static readonly Utf8String StateName = Utf8String.TranscodeUtf16("state");
private static readonly Utf8String PostalCodeName = Utf8String.TranscodeUtf16("postal_code");
private readonly LayoutColumn street;
private readonly LayoutColumn city;
private readonly LayoutColumn state;
private readonly LayoutColumn postalCode;
private readonly StringToken postalCodeToken;
private readonly PostalCodeHybridRowSerializer postalCodeSerializer;
public AddressHybridRowSerializer(Layout layout, LayoutResolver resolver)
{
layout.TryFind(AddressHybridRowSerializer.StreetName, out this.street);
layout.TryFind(AddressHybridRowSerializer.CityName, out this.city);
layout.TryFind(AddressHybridRowSerializer.StateName, out this.state);
layout.TryFind(AddressHybridRowSerializer.PostalCodeName, out this.postalCode);
layout.Tokenizer.TryFindToken(this.postalCode.Path, out this.postalCodeToken);
this.postalCodeSerializer = new PostalCodeHybridRowSerializer(resolver.Resolve(this.postalCode.TypeArgs.SchemaId), resolver);
}
public override Result WriteBuffer(ref RowBuffer row, ref RowCursor root, Dictionary<Utf8String, object> tableValue)
{
foreach ((Utf8String key, object value) in tableValue)
{
Result r;
switch (0)
{
case 0 when key.Equals(AddressHybridRowSerializer.StreetName):
if (value != null)
{
r = LayoutType.Utf8.WriteVariable(ref row, ref root, this.street, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(AddressHybridRowSerializer.CityName):
if (value != null)
{
r = LayoutType.Utf8.WriteVariable(ref row, ref root, this.city, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(AddressHybridRowSerializer.StateName):
if (value != null)
{
r = LayoutType.Utf8.WriteFixed(ref row, ref root, this.state, (Utf8String)value);
if (r != Result.Success)
{
return r;
}
}
break;
case 0 when key.Equals(AddressHybridRowSerializer.PostalCodeName):
if (value != null)
{
root.Find(ref row, this.postalCodeToken);
r = LayoutType.UDT.WriteScope(ref row, ref root, this.postalCode.TypeArgs, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
r = this.postalCodeSerializer.WriteBuffer(ref row, ref childScope, (Dictionary<Utf8String, object>)value);
if (r != Result.Success)
{
return r;
}
root.Skip(ref row, ref childScope);
}
break;
default:
Contract.Fail($"Unknown property name: {key}");
break;
}
}
return Result.Success;
}
public override Result ReadBuffer(ref RowBuffer row, ref RowCursor root)
{
Result r = LayoutType.Utf8.ReadVariable(ref row, ref root, this.street, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
r = LayoutType.Utf8.ReadVariable(ref row, ref root, this.city, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
r = LayoutType.Utf8.ReadFixed(ref row, ref root, this.state, out Utf8Span _);
if (r != Result.Success)
{
return r;
}
root.Find(ref row, this.postalCodeToken);
r = LayoutType.UDT.ReadScope(ref row, ref root, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
r = this.postalCodeSerializer.ReadBuffer(ref row, ref childScope);
if (r != Result.Success)
{
return r;
}
root.Skip(ref row, ref childScope);
return Result.Success;
}
}
}
}

View File

@@ -0,0 +1,100 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Schemas;
using Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
[DeploymentItem(TestData.SchemaFile, TestData.Target)]
public sealed class GenerateBenchmarkSuite : BenchmarkSuiteBase
{
private string sdl;
[TestInitialize]
public void ParseNamespaceExample()
{
this.sdl = File.ReadAllText(TestData.SchemaFile);
Namespace schema = Namespace.Parse(this.sdl);
this.DefaultResolver = new LayoutResolverNamespace(schema, SystemSchema.LayoutResolver);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task GenerateHotelBenchmarkAsync()
{
await this.GenerateBenchmarkAsync("Hotels", 100, TestData.HotelExpected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task GenerateRoomsBenchmarkAsync()
{
await this.GenerateBenchmarkAsync("Available_Rooms_By_Hotel_Date", 100, TestData.RoomsExpected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task GenerateGuestsBenchmarkAsync()
{
await this.GenerateBenchmarkAsync("Guests", 50, TestData.GuestsExpected);
}
private static List<Dictionary<Utf8String, object>> GenerateBenchmarkInputs(
LayoutResolverNamespace resolver,
string schemaName,
int outerLoopIterations)
{
HybridRowGeneratorConfig generatorConfig = new HybridRowGeneratorConfig();
const int seed = 42;
RandomGenerator rand = new RandomGenerator(new Random(seed));
HybridRowValueGenerator valueGenerator = new HybridRowValueGenerator(rand, generatorConfig);
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
List<Dictionary<Utf8String, object>> rows = new List<Dictionary<Utf8String, object>>(outerLoopIterations);
for (int iteration = 0; iteration != outerLoopIterations; iteration += outerLoopIterations < 0 ? 0 : 1)
{
TypeArgument typeArg = new TypeArgument(LayoutType.UDT, new TypeArgumentList(layout.SchemaId));
Dictionary<Utf8String, object> rowValue = (Dictionary<Utf8String, object>)valueGenerator.GenerateLayoutType(resolver, typeArg);
rows.Add(rowValue);
}
return rows;
}
private async Task GenerateBenchmarkAsync(string schemaName, int outerLoopIterations, string expectedFile)
{
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
List<Dictionary<Utf8String, object>>
rows = GenerateBenchmarkSuite.GenerateBenchmarkInputs(resolver, schemaName, outerLoopIterations);
Schema tableSchema = resolver.Namespace.Schemas.Find(x => x.Name == schemaName);
TypeArgument typeArg = new TypeArgument(LayoutType.UDT, new TypeArgumentList(tableSchema.SchemaId));
bool allMatch = rows.Count == expected.Count;
for (int i = 0; allMatch && i < rows.Count; i++)
{
allMatch |= HybridRowValueGenerator.DynamicTypeArgumentEquals(resolver, expected[i], rows[i], typeArg);
}
if (!allMatch)
{
await BenchmarkSuiteBase.WriteAllRowsAsync(expectedFile, this.sdl, resolver, resolver.Resolve(tableSchema.SchemaId), rows);
Console.WriteLine("Updated expected file at: {0}", Path.GetFullPath(expectedFile));
Assert.IsTrue(allMatch, "Expected output does not match expected file.");
}
}
}
}

View File

@@ -0,0 +1,7 @@
%NugetMachineInstallRoot%\google.protobuf.tools\3.5.1\tools\windows_x64\protoc.exe --csharp_out=CassandraProto TestData\CassandraHotelSchema.proto --proto_path=. --proto_path=%NugetMachineInstallRoot%\google.protobuf.tools\3.5.1\tools
move CassandraProto\CassandraHotelSchema.cs CassandraProto\CassandraHotelSchema.tmp
echo #pragma warning disable DontUseNamespaceAliases // Namespace Aliases should be avoided > CassandraProto\CassandraHotelSchema.cs
echo #pragma warning disable NamespaceMatchesFolderStructure // Namespace Declarations must match folder structure >> CassandraProto\CassandraHotelSchema.cs
cat CassandraProto\CassandraHotelSchema.tmp >> CassandraProto\CassandraHotelSchema.cs
del CassandraProto\CassandraHotelSchema.tmp

View File

@@ -0,0 +1,43 @@
RunId,Model,Operation,Schema,API,Iterations,Size (bytes),Total (ms),Duration (ms),Allocated (bytes),ThreadId,Gen0,Gen1,Gen2,Total Allocated (bytes)
636970252347090152,CodeGen,Write,Guests,Protobuf,50,1328,795.36630000,0.01590733,5084.00000000,8,40,0,0,254200600
636970252347090152,CodeGen,Write,Hotels,Protobuf,100,156,2303.97720000,0.00230398,861.00000000,8,137,0,0,861760000
636970252347090152,CodeGen,Write,Rooms,Protobuf,100,31,629.30810000,0.00062931,343.00000000,12,54,0,0,343440072
636970252347090152,CodeGen,Read,Guests,Protobuf,50,1328,313.95220000,0.00627904,3556.00000000,12,28,0,0,177840000
636970252347090152,CodeGen,Read,Hotels,Protobuf,100,156,795.37780000,0.00079538,485.00000000,13,77,0,0,485760072
636970252347090152,CodeGen,Read,Rooms,Protobuf,100,31,252.23010000,0.00025223,183.00000000,8,29,0,0,183440000
636970252347090152,CodeGen,Write,Guests,HybridRowGen,50,1222,1118.28520000,0.02236570,0.00000000,12,0,0,0,0
636970252347090152,CodeGen,Write,Hotels,HybridRowGen,100,143,2044.81250000,0.00204481,0.00000000,8,0,0,0,0
636970252347090152,CodeGen,Write,Rooms,HybridRowGen,100,23,534.58980000,0.00053459,0.00000000,8,0,0,0,0
636970252347090152,CodeGen,Read,Guests,HybridRowGen,50,1222,207.97910000,0.00415958,168.00000000,12,1,0,0,8400000
636970252347090152,CodeGen,Read,Hotels,HybridRowGen,100,143,731.67050000,0.00073167,0.00000000,12,0,0,0,0
636970252347090152,CodeGen,Read,Rooms,HybridRowGen,100,23,184.29990000,0.00018430,0.00000000,12,0,0,0,0
636970252347090152,Schematized,Write,Hotels,JSON,100,342,926.68970000,0.00926690,1653.00000000,13,26,1,0,165382696
636970252347090152,Schematized,Write,Rooms,JSON,100,129,3728.27760000,0.00372828,871.00000000,12,138,0,0,871474472
636970252347090152,Schematized,Write,Guests,JSON,50,2467,2236.45660000,0.04472913,6252.00000000,13,49,0,0,312604400
636970252347090152,Schematized,Read,Hotels,JSON,100,342,283.91940000,0.00283919,8557.00000000,12,136,0,0,855776000
636970252347090152,Schematized,Read,Rooms,JSON,100,129,1629.21600000,0.00162922,7846.00000000,13,1246,0,0,7846400000
636970252347090152,Schematized,Read,Guests,JSON,50,2467,626.56730000,0.01253135,11421.00000000,12,90,0,0,571096000
636970252347090152,Schematized,Write,Hotels,BSON,100,240,373.35650000,0.00373357,2125.00000000,13,33,0,0,212576000
636970252347090152,Schematized,Write,Rooms,BSON,100,74,1215.03590000,0.00121504,815.00000000,13,129,0,0,815440000
636970252347090152,Schematized,Write,Guests,BSON,50,1782,1018.99680000,0.02037994,9041.00000000,12,71,0,0,452072000
636970252347090152,Schematized,Read,Hotels,BSON,100,240,199.81420000,0.00199814,2688.00000000,13,42,0,0,268800000
636970252347090152,Schematized,Read,Rooms,BSON,100,74,888.62140000,0.00088862,1408.00000000,6,223,0,0,1408000000
636970252347090152,Schematized,Read,Guests,BSON,50,1782,431.03020000,0.00862060,9146.00000000,13,72,0,0,457344000
636970252347090152,Schematized,Write,Hotels,Layout,100,143,264.21190000,0.00264212,0.00000000,6,0,0,0,0
636970252347090152,Schematized,Write,Rooms,Layout,100,23,64.51090000,0.00064511,0.00000000,6,0,0,0,0
636970252347090152,Schematized,Write,Guests,Layout,50,1222,1221.28360000,0.02442567,0.00000000,12,0,0,0,0
636970252347090152,Schematized,Read,Hotels,Layout,100,143,79.88940000,0.00079889,0.00000000,13,0,0,0,0
636970252347090152,Schematized,Read,Rooms,Layout,100,23,21.45470000,0.00021455,0.00000000,12,0,0,0,0
636970252347090152,Schematized,Read,Guests,Layout,50,1222,248.11960000,0.00496239,168.00000000,6,1,0,0,8400000
636970252347090152,Schematized,Write,Hotels,HybridRow,100,143,352.73410000,0.00352734,0.00000000,12,0,0,0,0
636970252347090152,Schematized,Write,Rooms,HybridRow,100,23,1129.17010000,0.00112917,0.00000000,6,0,0,0,0
636970252347090152,Schematized,Write,Guests,HybridRow,50,1222,1418.34740000,0.02836695,0.00000000,12,0,0,0,0
636970252347090152,Schematized,Read,Hotels,HybridRow,100,143,107.00560000,0.00107006,0.00000000,6,0,0,0,0
636970252347090152,Schematized,Read,Rooms,HybridRow,100,23,307.03740000,0.00030704,0.00000000,12,0,0,0,0
636970252347090152,Schematized,Read,Guests,HybridRow,50,1222,356.78610000,0.00713572,168.00000000,6,1,0,0,8400000
636970252347090152,Unschematized,Write,Messages1K,HybridRowSparse,1001,3405,312.43510000,0.03121230,0.00000000,13,0,0,0,0
636970252347090152,Unschematized,Read,Messages1K,HybridRowSparse,1001,3405,153.64990000,0.01534964,0.00000000,12,0,0,0,0
636970252347090152,Unschematized,Write,Messages1K,BSON,1001,3949,301.69900000,0.03013976,30476.00000000,13,48,0,0,305065120
636970252347090152,Unschematized,Read,Messages1K,BSON,1001,3949,177.64080000,0.01774633,23166.00000000,8,36,0,0,231892392
636970252347090152,Unschematized,Write,Messages1K,JSON,1001,5693,999.48440000,0.09984859,16900.00000000,8,26,0,0,169178696
636970252347090152,Unschematized,Read,Messages1K,JSON,1001,5693,291.96320000,0.02916715,32040.00000000,8,49,1,1,320720528
1 RunId Model Operation Schema API Iterations Size (bytes) Total (ms) Duration (ms) Allocated (bytes) ThreadId Gen0 Gen1 Gen2 Total Allocated (bytes)
2 636970252347090152 CodeGen Write Guests Protobuf 50 1328 795.36630000 0.01590733 5084.00000000 8 40 0 0 254200600
3 636970252347090152 CodeGen Write Hotels Protobuf 100 156 2303.97720000 0.00230398 861.00000000 8 137 0 0 861760000
4 636970252347090152 CodeGen Write Rooms Protobuf 100 31 629.30810000 0.00062931 343.00000000 12 54 0 0 343440072
5 636970252347090152 CodeGen Read Guests Protobuf 50 1328 313.95220000 0.00627904 3556.00000000 12 28 0 0 177840000
6 636970252347090152 CodeGen Read Hotels Protobuf 100 156 795.37780000 0.00079538 485.00000000 13 77 0 0 485760072
7 636970252347090152 CodeGen Read Rooms Protobuf 100 31 252.23010000 0.00025223 183.00000000 8 29 0 0 183440000
8 636970252347090152 CodeGen Write Guests HybridRowGen 50 1222 1118.28520000 0.02236570 0.00000000 12 0 0 0 0
9 636970252347090152 CodeGen Write Hotels HybridRowGen 100 143 2044.81250000 0.00204481 0.00000000 8 0 0 0 0
10 636970252347090152 CodeGen Write Rooms HybridRowGen 100 23 534.58980000 0.00053459 0.00000000 8 0 0 0 0
11 636970252347090152 CodeGen Read Guests HybridRowGen 50 1222 207.97910000 0.00415958 168.00000000 12 1 0 0 8400000
12 636970252347090152 CodeGen Read Hotels HybridRowGen 100 143 731.67050000 0.00073167 0.00000000 12 0 0 0 0
13 636970252347090152 CodeGen Read Rooms HybridRowGen 100 23 184.29990000 0.00018430 0.00000000 12 0 0 0 0
14 636970252347090152 Schematized Write Hotels JSON 100 342 926.68970000 0.00926690 1653.00000000 13 26 1 0 165382696
15 636970252347090152 Schematized Write Rooms JSON 100 129 3728.27760000 0.00372828 871.00000000 12 138 0 0 871474472
16 636970252347090152 Schematized Write Guests JSON 50 2467 2236.45660000 0.04472913 6252.00000000 13 49 0 0 312604400
17 636970252347090152 Schematized Read Hotels JSON 100 342 283.91940000 0.00283919 8557.00000000 12 136 0 0 855776000
18 636970252347090152 Schematized Read Rooms JSON 100 129 1629.21600000 0.00162922 7846.00000000 13 1246 0 0 7846400000
19 636970252347090152 Schematized Read Guests JSON 50 2467 626.56730000 0.01253135 11421.00000000 12 90 0 0 571096000
20 636970252347090152 Schematized Write Hotels BSON 100 240 373.35650000 0.00373357 2125.00000000 13 33 0 0 212576000
21 636970252347090152 Schematized Write Rooms BSON 100 74 1215.03590000 0.00121504 815.00000000 13 129 0 0 815440000
22 636970252347090152 Schematized Write Guests BSON 50 1782 1018.99680000 0.02037994 9041.00000000 12 71 0 0 452072000
23 636970252347090152 Schematized Read Hotels BSON 100 240 199.81420000 0.00199814 2688.00000000 13 42 0 0 268800000
24 636970252347090152 Schematized Read Rooms BSON 100 74 888.62140000 0.00088862 1408.00000000 6 223 0 0 1408000000
25 636970252347090152 Schematized Read Guests BSON 50 1782 431.03020000 0.00862060 9146.00000000 13 72 0 0 457344000
26 636970252347090152 Schematized Write Hotels Layout 100 143 264.21190000 0.00264212 0.00000000 6 0 0 0 0
27 636970252347090152 Schematized Write Rooms Layout 100 23 64.51090000 0.00064511 0.00000000 6 0 0 0 0
28 636970252347090152 Schematized Write Guests Layout 50 1222 1221.28360000 0.02442567 0.00000000 12 0 0 0 0
29 636970252347090152 Schematized Read Hotels Layout 100 143 79.88940000 0.00079889 0.00000000 13 0 0 0 0
30 636970252347090152 Schematized Read Rooms Layout 100 23 21.45470000 0.00021455 0.00000000 12 0 0 0 0
31 636970252347090152 Schematized Read Guests Layout 50 1222 248.11960000 0.00496239 168.00000000 6 1 0 0 8400000
32 636970252347090152 Schematized Write Hotels HybridRow 100 143 352.73410000 0.00352734 0.00000000 12 0 0 0 0
33 636970252347090152 Schematized Write Rooms HybridRow 100 23 1129.17010000 0.00112917 0.00000000 6 0 0 0 0
34 636970252347090152 Schematized Write Guests HybridRow 50 1222 1418.34740000 0.02836695 0.00000000 12 0 0 0 0
35 636970252347090152 Schematized Read Hotels HybridRow 100 143 107.00560000 0.00107006 0.00000000 6 0 0 0 0
36 636970252347090152 Schematized Read Rooms HybridRow 100 23 307.03740000 0.00030704 0.00000000 12 0 0 0 0
37 636970252347090152 Schematized Read Guests HybridRow 50 1222 356.78610000 0.00713572 168.00000000 6 1 0 0 8400000
38 636970252347090152 Unschematized Write Messages1K HybridRowSparse 1001 3405 312.43510000 0.03121230 0.00000000 13 0 0 0 0
39 636970252347090152 Unschematized Read Messages1K HybridRowSparse 1001 3405 153.64990000 0.01534964 0.00000000 12 0 0 0 0
40 636970252347090152 Unschematized Write Messages1K BSON 1001 3949 301.69900000 0.03013976 30476.00000000 13 48 0 0 305065120
41 636970252347090152 Unschematized Read Messages1K BSON 1001 3949 177.64080000 0.01774633 23166.00000000 8 36 0 0 231892392
42 636970252347090152 Unschematized Write Messages1K JSON 1001 5693 999.48440000 0.09984859 16900.00000000 8 26 0 0 169178696
43 636970252347090152 Unschematized Read Messages1K JSON 1001 5693 291.96320000 0.02916715 32040.00000000 8 49 1 1 320720528

View File

@@ -0,0 +1,132 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
public ref struct JsonModelRowGenerator
{
private RowBuffer row;
public JsonModelRowGenerator(int capacity, Layout layout, LayoutResolver resolver, ISpanResizer<byte> resizer = default)
{
this.row = new RowBuffer(capacity, resizer);
this.row.InitLayout(HybridRowVersion.V1, layout, resolver);
}
public int Length => this.row.Length;
public byte[] ToArray() => this.row.ToArray();
public void WriteTo(Stream stream)
{
this.row.WriteTo(stream);
}
public bool ReadFrom(Stream stream, int length)
{
return this.row.ReadFrom(stream, length, HybridRowVersion.V1, this.row.Resolver);
}
public void Reset()
{
Layout layout = this.row.Resolver.Resolve(this.row.Header.SchemaId);
this.row.InitLayout(HybridRowVersion.V1, layout, this.row.Resolver);
}
public RowReader GetReader()
{
return new RowReader(ref this.row);
}
public Result WriteBuffer(Dictionary<Utf8String, object> value)
{
return RowWriter.WriteBuffer(
ref this.row,
value,
(ref RowWriter writer, TypeArgument typeArg, Dictionary<Utf8String, object> dict) =>
{
foreach ((Utf8String propPath, object propValue) in dict)
{
Result result = JsonModelRowGenerator.JsonModelSwitch(ref writer, propPath, propValue);
if (result != Result.Success)
{
return result;
}
}
return Result.Success;
});
}
private static Result JsonModelSwitch(ref RowWriter writer, Utf8String path, object value)
{
switch (value)
{
case null:
return writer.WriteNull(path);
case bool x:
return writer.WriteBool(path, x);
case long x:
return writer.WriteInt64(path, x);
case double x:
return writer.WriteFloat64(path, x);
case string x:
return writer.WriteString(path, x);
case Utf8String x:
return writer.WriteString(path, x.Span);
case byte[] x:
return writer.WriteBinary(path, x);
case ReadOnlyMemory<byte> x:
return writer.WriteBinary(path, x.Span);
case Dictionary<Utf8String, object> x:
return writer.WriteScope(
path,
new TypeArgument(LayoutType.Object),
x,
(ref RowWriter writer2, TypeArgument typeArg, Dictionary<Utf8String, object> dict) =>
{
foreach ((Utf8String propPath, object propValue) in dict)
{
Result result = JsonModelRowGenerator.JsonModelSwitch(ref writer2, propPath, propValue);
if (result != Result.Success)
{
return result;
}
}
return Result.Success;
});
case List<object> x:
return writer.WriteScope(
path,
new TypeArgument(LayoutType.Array),
x,
(ref RowWriter writer2, TypeArgument typeArg, List<object> list) =>
{
foreach (object elm in list)
{
Result result = JsonModelRowGenerator.JsonModelSwitch(ref writer2, null, elm);
if (result != Result.Success)
{
return result;
}
}
return Result.Success;
});
default:
Contract.Assert(false, $"Unknown type will be ignored: {value.GetType().Name}");
return Result.Failure;
}
}
}
}

View File

@@ -0,0 +1,89 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.IO;
using System.Text;
internal class Measurements : IDisposable
{
private static readonly long RunId = DateTime.UtcNow.Ticks;
private readonly FileStream file;
private readonly TextWriter writer;
public Measurements(string path)
{
FileInfo info = new FileInfo(path);
if (info.Exists)
{
this.file = new FileStream(path, FileMode.Append);
this.writer = new StreamWriter(this.file, Encoding.ASCII);
}
else
{
this.file = new FileStream(path, FileMode.CreateNew);
this.writer = new StreamWriter(this.file, Encoding.ASCII);
this.writer.WriteLine(
"RunId,Model,Operation,Schema,API,Iterations,Size (bytes),Total (ms),Duration (ms),Allocated (bytes),ThreadId,Gen0,Gen1,Gen2,Total Allocated (bytes)");
}
}
public void Dispose()
{
this.writer.Flush();
this.writer.Dispose();
this.file.Dispose();
}
public void WriteMeasurement(string model, string operation, string schema, string api,
int outerLoopIterations, int innerLoopIterations, long totalSize, double totalDurationMs,
int threadId, int gen0, int gen1, int gen2, long totalAllocatedBytes)
{
Console.WriteLine(
"RunId: {0}, \nModel: {1} \nOperation: {2} \nSchema: {3} \nAPI: {4}",
Measurements.RunId,
model,
operation,
schema,
api);
Console.WriteLine(
"\n\nIterations: {0} \nSize (bytes): {1:F0} \nTotal (ms): {2:F4} \nDuration (ms): {3:F4} \nAllocated (bytes): {4:F4}",
outerLoopIterations,
totalSize / outerLoopIterations,
totalDurationMs,
totalDurationMs / (outerLoopIterations * innerLoopIterations),
totalAllocatedBytes / (outerLoopIterations * innerLoopIterations));
Console.WriteLine(
"\n\nThread: {0} \nCollections: {1}, {2}, {3} \nTotal Allocated: {4:n0} (bytes)",
threadId,
gen0,
gen1,
gen2,
totalAllocatedBytes);
this.writer.WriteLine(
"{0},{1},{2},{3},{4},{5},{6:F0},{7:F8},{8:F8},{9:F8},{10},{11},{12},{13},{14:0}",
Measurements.RunId,
model,
operation,
schema,
api,
outerLoopIterations,
totalSize / outerLoopIterations,
totalDurationMs,
totalDurationMs / (outerLoopIterations * innerLoopIterations),
totalAllocatedBytes / (outerLoopIterations * innerLoopIterations),
threadId,
gen0,
gen1,
gen2,
totalAllocatedBytes);
}
}
}

View File

@@ -0,0 +1,128 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Threading;
using JetBrains.Profiler.Api;
using Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator;
[SuppressMessage("Microsoft.Reliability", "CA2001:Avoid calling problematic methods", Justification = "Perf Benchmark")]
public class MicroBenchmarkSuiteBase : BenchmarkSuiteBase
{
private const int WarmCount = 5;
private const string MetricsResultFile = "HybridRowPerf.csv";
[SuppressMessage("Microsoft.Reliability", "CA2001:Avoid calling problematic methods", Justification = "Perf Benchmark")]
private protected static void Benchmark<TValue>(
string model,
string operation,
string schema,
string api,
int innerLoopIterations,
ref BenchmarkContext context,
BenchmarkBody<TValue> loopBody,
BenchmarkMeasure<TValue> measure,
List<TValue> expected)
{
Stopwatch sw = new Stopwatch();
double durationMs = 0;
long rowSize = 0;
// Warm
int warm = Math.Min(MicroBenchmarkSuiteBase.WarmCount, expected.Count);
for (int i = 0; i < warm; i++)
{
for (int innerLoop = 0; innerLoop < innerLoopIterations; innerLoop++)
{
loopBody(ref context, expected[i]);
}
}
// Execute
GC.Collect();
GC.WaitForPendingFinalizers();
Thread.Sleep(1000);
int gen0 = GC.CollectionCount(0);
int gen1 = GC.CollectionCount(1);
int gen2 = GC.CollectionCount(2);
long allocated = GC.GetAllocatedBytesForCurrentThread();
int threadId = Thread.CurrentThread.ManagedThreadId;
ThreadPriority currentPriority = Thread.CurrentThread.Priority;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
MemoryProfiler.CollectAllocations(true);
MemoryProfiler.GetSnapshot();
try
{
foreach (TValue tableValue in expected)
{
sw.Restart();
MicroBenchmarkSuiteBase.BenchmarkInnerLoop(innerLoopIterations, tableValue, ref context, loopBody);
sw.Stop();
durationMs += sw.Elapsed.TotalMilliseconds;
rowSize += measure(ref context, tableValue);
}
}
finally
{
Thread.CurrentThread.Priority = currentPriority;
gen0 = GC.CollectionCount(0) - gen0;
gen1 = GC.CollectionCount(1) - gen1;
gen2 = GC.CollectionCount(2) - gen2;
allocated = GC.GetAllocatedBytesForCurrentThread() - allocated;
MemoryProfiler.GetSnapshot();
MemoryProfiler.CollectAllocations(false);
}
using (Measurements m = new Measurements(MicroBenchmarkSuiteBase.MetricsResultFile))
{
m.WriteMeasurement(
model: model,
operation: operation,
schema: schema,
api: api,
outerLoopIterations: expected.Count,
innerLoopIterations: innerLoopIterations,
totalSize: rowSize,
totalDurationMs: durationMs,
threadId: threadId,
gen0: gen0,
gen1: gen1,
gen2: gen2,
totalAllocatedBytes: allocated);
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void BenchmarkInnerLoop<TValue>(
int innerLoopIterations,
TValue tableValue,
ref BenchmarkContext context,
BenchmarkBody<TValue> loopBody)
{
for (int innerLoop = 0; innerLoop < innerLoopIterations; innerLoop++)
{
loopBody(ref context, tableValue);
}
}
private protected ref struct BenchmarkContext
{
public CodeGenRowGenerator CodeGenWriter;
public ProtobufRowGenerator ProtobufWriter;
public WriteRowGenerator PatchWriter;
public StreamingRowGenerator StreamingWriter;
public JsonModelRowGenerator JsonModelWriter;
}
private protected delegate void BenchmarkBody<in TValue>(ref BenchmarkContext context, TValue value);
private protected delegate long BenchmarkMeasure<in TValue>(ref BenchmarkContext context, TValue value);
}
}

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<ProjectGuid>{26A73F4A-AC9E-46AF-9445-286EE9EDA3EE}</ProjectGuid>
<OutputType>Library</OutputType>
<SigningType>Test</SigningType>
<RootNamespace>Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf</RootNamespace>
<AssemblyName>Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf</AssemblyName>
<TargetFramework>netcoreapp2.2</TargetFramework>
<EnforceTestOwnership>True</EnforceTestOwnership>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Build.props))\build.props" />
<PropertyGroup>
<QTestType>MsTest_Latest</QTestType>
<QTestDotNetFramework>FrameworkCore20</QTestDotNetFramework>
<QTestDotNetCoreRuntimeArchitecture>X64</QTestDotNetCoreRuntimeArchitecture>
<QTestDirToDeploy>$(OutDir)</QTestDirToDeploy>
</PropertyGroup>
<ItemGroup>
<None Include="TestData\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(DocDBRoot)\Cosmos\Core\Core\Microsoft.Azure.Cosmos.Core.csproj" />
<ProjectReference Include="..\HybridRowGenerator\Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator.csproj" />
<ProjectReference Include="..\HybridRow\Microsoft.Azure.Cosmos.Serialization.HybridRow.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Profiler.Api" />
<PackageReference Include="Google.Protobuf" />
<PackageReference Include="Google.Protobuf.Tools" />
<PackageReference Include="MSTest.TestAdapter" />
<PackageReference Include="MSTest.TestFramework" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Microsoft.VisualStudio.TestPlatform" />
<PackageReference Include="MongoDB.Bson.Signed" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="System.Memory" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" />
<PackageReference Include="System.ValueTuple" />
<PackageReference Include="VisualStudio.UnitTest.Corext" />
</ItemGroup>
<ItemGroup>
<None Update="HybridRowPerf.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,19 @@
// <copyright file="AssemblyInfo.cs" company="Microsoft">
// Copyright (c) Microsoft. All rights reserved.
// </copyright>
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("26A73F4A-AC9E-46AF-9445-286EE9EDA3EE")]

View File

@@ -0,0 +1,315 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using Google.Protobuf;
using Google.Protobuf.Collections;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.VisualStudio.TestTools.UnitTesting;
#pragma warning disable DontUseNamespaceAliases // Namespace Aliases should be avoided
using pb = Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf;
#pragma warning restore DontUseNamespaceAliases // Namespace Aliases should be avoided
internal ref struct ProtobufRowGenerator
{
private readonly string schemaName;
private readonly byte[] buffer;
private ReadOnlySpan<byte> active;
public ProtobufRowGenerator(string schemaName, int capacity)
{
this.schemaName = schemaName;
this.buffer = new byte[capacity];
this.active = this.buffer;
}
public void WriteBuffer(Dictionary<Utf8String, object> tableValue)
{
switch (this.schemaName)
{
case "Hotels":
this.WriteBufferHotel(tableValue);
break;
case "Guests":
this.WriteBufferGuest(tableValue);
break;
case "Available_Rooms_By_Hotel_Date":
this.WriteBufferRoom(tableValue);
break;
default:
Contract.Fail($"Unknown schema will be ignored: {this.schemaName}");
break;
}
}
public void ReadBuffer(byte[] buffer)
{
switch (this.schemaName)
{
case "Hotels":
ProtobufRowGenerator.ReadBufferHotel(buffer);
break;
case "Guests":
ProtobufRowGenerator.ReadBufferGuest(buffer);
break;
case "Available_Rooms_By_Hotel_Date":
ProtobufRowGenerator.ReadBufferRoom(buffer);
break;
default:
Contract.Fail($"Unknown schema will be ignored: {this.schemaName}");
break;
}
}
public int Length => this.active.Length;
public byte[] ToArray()
{
return this.active.ToArray();
}
private void WriteBufferGuest(Dictionary<Utf8String, object> tableValue)
{
pb.Guests room = new pb.Guests();
using (CodedOutputStream stm = new CodedOutputStream(this.buffer))
{
foreach ((Utf8String key, object value) in tableValue)
{
switch (key.ToString())
{
case "guest_id":
room.GuestId = ((Guid?)value)?.ToString();
break;
case "first_name":
room.FirstName = ((Utf8String)value)?.ToString();
break;
case "last_name":
room.LastName = ((Utf8String)value)?.ToString();
break;
case "title":
room.Title = ((Utf8String)value)?.ToString();
break;
case "emails":
if (value != null)
{
ProtobufRowGenerator.PopulateStringList(room.Emails, (List<object>)value);
}
break;
case "phone_numbers":
if (value != null)
{
ProtobufRowGenerator.PopulateStringList(room.PhoneNumbers, (List<object>)value);
}
break;
case "addresses":
if (value != null)
{
ProtobufRowGenerator.PopulateStringAddressMap(room.Addresses, (List<object>)value);
}
break;
case "confirm_number":
room.ConfirmNumber = ((Utf8String)value)?.ToString();
break;
default:
Assert.Fail("should never happen");
break;
}
}
room.WriteTo(stm);
stm.Flush();
this.active = this.buffer.AsSpan(0, (int)stm.Position);
}
}
private void WriteBufferHotel(Dictionary<Utf8String, object> tableValue)
{
pb.Hotels room = new pb.Hotels();
using (CodedOutputStream stm = new CodedOutputStream(this.buffer))
{
foreach ((Utf8String key, object value) in tableValue)
{
switch (key.ToString())
{
case "hotel_id":
room.HotelId = ((Utf8String)value)?.ToString();
break;
case "name":
room.Name = ((Utf8String)value)?.ToString();
break;
case "phone":
room.Phone = ((Utf8String)value)?.ToString();
break;
case "address":
room.Address = value == null ? null : ProtobufRowGenerator.MakeAddress((Dictionary<Utf8String, object>)value);
break;
default:
Assert.Fail("should never happen");
break;
}
}
room.WriteTo(stm);
stm.Flush();
this.active = this.buffer.AsSpan(0, (int)stm.Position);
}
}
private void WriteBufferRoom(Dictionary<Utf8String, object> tableValue)
{
pb.Available_Rooms_By_Hotel_Date room = new pb.Available_Rooms_By_Hotel_Date();
using (CodedOutputStream stm = new CodedOutputStream(this.buffer))
{
foreach ((Utf8String key, object value) in tableValue)
{
switch (key.ToString())
{
case "hotel_id":
room.HotelId = ((Utf8String)value)?.ToString();
break;
case "date":
room.Date = ((DateTime?)value)?.Ticks;
break;
case "room_number":
room.RoomNumber = (byte?)value;
break;
case "is_available":
room.IsAvailable = (bool?)value;
break;
default:
Assert.Fail("should never happen");
break;
}
}
room.WriteTo(stm);
stm.Flush();
this.active = this.buffer.AsSpan(0, (int)stm.Position);
}
}
private static void ReadBufferGuest(byte[] buffer)
{
pb.Guests item = new pb.Guests();
item.MergeFrom(buffer);
}
private static void ReadBufferHotel(byte[] buffer)
{
pb.Hotels item = new pb.Hotels();
item.MergeFrom(buffer);
}
private static void ReadBufferRoom(byte[] buffer)
{
pb.Available_Rooms_By_Hotel_Date item = new pb.Available_Rooms_By_Hotel_Date();
item.MergeFrom(buffer);
}
private static void PopulateStringList(RepeatedField<string> field, List<object> list)
{
foreach (object item in list)
{
field.Add(((Utf8String)item).ToString());
}
}
private static void PopulateStringAddressMap(MapField<string, pb.Address> field, List<object> list)
{
foreach (object item in list)
{
List<object> tuple = (List<object>)item;
string key = ((Utf8String)tuple[0]).ToString();
pb.Address value = ProtobufRowGenerator.MakeAddress((Dictionary<Utf8String, object>)tuple[1]);
field.Add(key, value);
}
}
private static pb.PostalCode MakePostalCode(Dictionary<Utf8String, object> tableValue)
{
pb.PostalCode postalCode = new pb.PostalCode();
foreach ((Utf8String key, object value) in tableValue)
{
switch (key.ToString())
{
case "zip":
postalCode.Zip = (int?)value;
break;
case "plus4":
postalCode.Plus4 = (short?)value;
break;
default:
Assert.Fail("should never happen");
break;
}
}
return postalCode;
}
private static pb.Address MakeAddress(Dictionary<Utf8String, object> tableValue)
{
pb.Address address = new pb.Address();
foreach ((Utf8String key, object value) in tableValue)
{
switch (key.ToString())
{
case "street":
address.Street = ((Utf8String)value)?.ToString();
break;
case "city":
address.City = ((Utf8String)value)?.ToString();
break;
case "state":
address.State = ((Utf8String)value)?.ToString();
break;
case "postal_code":
address.PostalCode = value == null ? null : ProtobufRowGenerator.MakePostalCode((Dictionary<Utf8String, object>)value);
break;
default:
Assert.Fail("should never happen");
break;
}
}
return address;
}
}
}

View File

@@ -0,0 +1,282 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.RecordIO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
[SuppressMessage("Microsoft.Reliability", "CA2001:Avoid calling problematic methods", Justification = "Perf Benchmark")]
[DeploymentItem(@"TestData\*.hr", "TestData")]
public sealed class ReaderBenchmark
{
private const int InitialCapacity = 2 * 1024 * 1024;
private const int WarmCount = 5;
private const int MeasureCount = 10;
private const string CombinedScriptsData = @"TestData\CombinedScriptsData.hr";
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(ReaderBenchmark.CombinedScriptsData, "TestData")]
public async Task RowReaderAsync()
{
using (BenchmarkContext context = new BenchmarkContext(ReaderBenchmark.CombinedScriptsData, true, true))
{
await ReaderBenchmark.BenchmarkAsync(context, ReaderBenchmark.RowReaderBenchmarkAsync);
}
}
[TestMethod]
[Owner("jthunter")]
[Ignore]
public async Task SpecificFileAsync()
{
const string filename = @"E:\TestData\HybridRow\Lastfm.hr";
using (BenchmarkContext context = new BenchmarkContext(filename, true, true))
{
await ReaderBenchmark.BenchmarkAsync(context, ReaderBenchmark.RowReaderBenchmarkAsync);
}
}
[TestMethod]
[Owner("jthunter")]
[Ignore]
public async Task AllAsync()
{
const string dir = @"E:\TestData\HybridRow";
foreach (FileInfo childFile in new DirectoryInfo(dir).EnumerateFiles(@"*.hr"))
{
using (BenchmarkContext context = new BenchmarkContext(childFile.FullName, false, false))
{
await ReaderBenchmark.BenchmarkAsync(context, ReaderBenchmark.RowReaderBenchmarkAsync);
}
}
}
private static async Task RowReaderBenchmarkAsync(object ctx)
{
BenchmarkContext context = (BenchmarkContext)ctx;
MemorySpanResizer<byte> resizer = new MemorySpanResizer<byte>(ReaderBenchmark.InitialCapacity);
Result r = await context.Input.ReadRecordIOAsync(
record =>
{
context.IncrementRecordCount();
r = ReaderBenchmark.VisitOneRow(record, context.Resolver);
Assert.AreEqual(Result.Success, r);
return Result.Success;
},
segment =>
{
r = SegmentSerializer.Read(segment.Span, context.Resolver, out Segment _);
Assert.AreEqual(Result.Success, r);
// TODO: do something with embedded schema.
return Result.Success;
},
resizer);
Assert.AreEqual(Result.Success, r);
}
private static Result VisitOneRow(Memory<byte> buffer, LayoutResolver resolver)
{
RowBuffer row = new RowBuffer(buffer.Span, HybridRowVersion.V1, resolver);
RowReader reader = new RowReader(ref row);
return reader.VisitReader();
}
[SuppressMessage("Microsoft.Reliability", "CA2001:Avoid calling problematic methods", Justification = "Perf Benchmark")]
private static async Task BenchmarkAsync(BenchmarkContext context, Func<object, Task> body)
{
using (SingleThreadedTaskScheduler scheduler = new SingleThreadedTaskScheduler())
{
// Warm
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
for (int i = 0; i < ReaderBenchmark.WarmCount; i++)
{
context.Reset();
sw.Restart();
await Task.Factory.StartNew(body, context, CancellationToken.None, TaskCreationOptions.None, scheduler).Unwrap();
sw.Stop();
if (context.ShowWarmSummary)
{
context.Summarize(sw.Elapsed);
}
}
// Execute
double[] timing = new double[ReaderBenchmark.MeasureCount];
for (int i = 0; i < ReaderBenchmark.MeasureCount; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
context.Reset();
sw.Restart();
await Task.Factory.StartNew(body, context, CancellationToken.None, TaskCreationOptions.None, scheduler).Unwrap();
sw.Stop();
if (context.ShowSummary)
{
context.Summarize(sw.Elapsed);
}
timing[i] = sw.Elapsed.TotalMilliseconds;
}
Array.Sort(timing);
Console.WriteLine(
$"File: {Path.GetFileNameWithoutExtension(context.InputFile)}, Mean: {timing[ReaderBenchmark.MeasureCount / 2]:F4}");
}
}
private sealed class BenchmarkContext : IDisposable
{
private readonly string inputFile;
private readonly bool showSummary;
private readonly bool showWarmSummary;
private long recordCount;
private readonly Stream input;
private readonly LayoutResolver resolver;
public BenchmarkContext(string inputFile, bool showSummary = true, bool showWarmSummary = false)
{
this.inputFile = inputFile;
this.showSummary = showSummary;
this.showWarmSummary = showWarmSummary;
this.input = new FileStream(inputFile, FileMode.Open);
this.resolver = SystemSchema.LayoutResolver;
}
public bool ShowSummary => this.showSummary;
public bool ShowWarmSummary => this.showWarmSummary;
public string InputFile => this.inputFile;
public Stream Input => this.input;
public LayoutResolver Resolver => this.resolver;
public void IncrementRecordCount()
{
this.recordCount++;
}
public void Reset()
{
this.recordCount = 0;
this.input.Seek(0, SeekOrigin.Begin);
}
public void Summarize(TimeSpan duration)
{
Console.Write($"Total Time: {duration.TotalMilliseconds:F4}, ");
Console.WriteLine($"Record Count: {this.recordCount}");
}
public void Dispose()
{
this.input.Dispose();
}
}
private sealed class SingleThreadedTaskScheduler : TaskScheduler, IDisposable
{
private readonly Thread worker;
private readonly EventWaitHandle ready;
private readonly ConcurrentQueue<Task> tasks;
private readonly CancellationTokenSource cancel;
// Creates a new instance with the specified degree of parallelism.
public SingleThreadedTaskScheduler()
{
this.tasks = new ConcurrentQueue<Task>();
this.ready = new ManualResetEvent(false);
this.worker = new Thread(this.DoWork);
this.cancel = new CancellationTokenSource();
this.worker.Start();
}
// Gets the maximum concurrency level supported by this scheduler.
public override int MaximumConcurrencyLevel => 1;
public void Dispose()
{
if (!this.cancel.IsCancellationRequested)
{
this.cancel.Cancel();
this.worker.Join();
this.ready?.Dispose();
this.cancel?.Dispose();
}
}
// Queues a task to the scheduler.
protected override void QueueTask(Task task)
{
lock (this.tasks)
{
this.tasks.Enqueue(task);
if (Thread.CurrentThread != this.worker)
{
this.ready.Set();
}
}
}
// Attempts to execute the specified task on the current thread.
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (Thread.CurrentThread != this.worker)
{
return false;
}
// If the task was previously queued, then skip it.
if (taskWasPreviouslyQueued)
{
return false;
}
return this.TryExecuteTask(task);
}
protected override bool TryDequeue(Task task)
{
return false;
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return null;
}
private void DoWork()
{
while (!this.cancel.IsCancellationRequested)
{
if (this.tasks.TryDequeue(out Task item))
{
this.TryExecuteTask(item);
}
else
{
this.ready.WaitOne(TimeSpan.FromSeconds(1));
}
}
}
}
}
}

View File

@@ -0,0 +1,97 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
internal static class RowReaderExtensions
{
public static Result VisitReader(this ref RowReader reader)
{
while (reader.Read())
{
Utf8Span path = reader.PathSpan;
switch (reader.Type.LayoutCode)
{
case LayoutCode.Null:
case LayoutCode.Boolean:
case LayoutCode.Int8:
case LayoutCode.Int16:
case LayoutCode.Int32:
case LayoutCode.Int64:
case LayoutCode.UInt8:
case LayoutCode.UInt16:
case LayoutCode.UInt32:
case LayoutCode.UInt64:
case LayoutCode.VarInt:
case LayoutCode.VarUInt:
case LayoutCode.Float32:
case LayoutCode.Float64:
case LayoutCode.Float128:
case LayoutCode.Decimal:
case LayoutCode.DateTime:
case LayoutCode.UnixDateTime:
case LayoutCode.Guid:
case LayoutCode.MongoDbObjectId:
case LayoutCode.Utf8:
case LayoutCode.Binary:
break;
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
{
if (!reader.HasValue)
{
break;
}
goto case LayoutCode.TypedTupleScope;
}
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
case LayoutCode.ArrayScope:
case LayoutCode.ImmutableArrayScope:
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
{
Result r = reader.ReadScope(null, (ref RowReader child, object _) => child.VisitReader());
if (r != Result.Success)
{
return r;
}
break;
}
default:
{
Contract.Assert(false, $"Unknown type will be ignored: {reader.Type.LayoutCode}");
break;
}
}
}
return Result.Success;
}
}
}

View File

@@ -0,0 +1,583 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Internal;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Schemas;
using Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MongoDB.Bson.IO;
using Newtonsoft.Json;
[TestClass]
[DeploymentItem(TestData.SchemaFile, TestData.Target)]
public sealed class SchematizedMicroBenchmarkSuite : MicroBenchmarkSuiteBase
{
private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
private string sdl;
[TestInitialize]
public void ParseNamespaceExample()
{
this.sdl = File.ReadAllText(TestData.SchemaFile);
Namespace schema = Namespace.Parse(this.sdl);
this.DefaultResolver = new LayoutResolverNamespace(schema, SystemSchema.LayoutResolver);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task JsonHotelWriteBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.JsonWriteBenchmark("Hotels", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task JsonRoomsWriteBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.JsonWriteBenchmark("Rooms", 10000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task JsonGuestsWriteBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.JsonWriteBenchmark("Guests", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task JsonHotelReadBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.JsonReadBenchmark("Hotels", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task JsonRoomsReadBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.JsonReadBenchmark("Rooms", 10000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task JsonGuestsReadBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace _) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.JsonReadBenchmark("Guests", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task BsonHotelWriteBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.BsonWriteBenchmark(resolver, "Hotels", "Hotels", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task BsonRoomsWriteBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.BsonWriteBenchmark(resolver, "Available_Rooms_By_Hotel_Date", "Rooms", 10000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task BsonGuestsWriteBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.BsonWriteBenchmark(resolver, "Guests", "Guests", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task BsonHotelReadBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.BsonReadBenchmark(resolver, "Hotels", "Hotels", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task BsonRoomsReadBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.BsonReadBenchmark(resolver, "Available_Rooms_By_Hotel_Date", "Rooms", 10000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task BsonGuestsReadBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.BsonReadBenchmark(resolver, "Guests", "Guests", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task LayoutHotelWriteBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.LayoutWriteBenchmark(resolver, "Hotels", "Hotels", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task LayoutRoomsWriteBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.LayoutWriteBenchmark(resolver, "Available_Rooms_By_Hotel_Date", "Rooms", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task LayoutGuestsWriteBenchmarkAsync()
{
#if DEBUG
const int innerLoopIterations = 1;
#else
const int innerLoopIterations = 1000;
#endif
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.LayoutWriteBenchmark(resolver, "Guests", "Guests", innerLoopIterations, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task LayoutHotelReadBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.LayoutReadBenchmark(resolver, "Hotels", "Hotels", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task LayoutRoomsReadBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.LayoutReadBenchmark(resolver, "Available_Rooms_By_Hotel_Date", "Rooms", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task LayoutGuestsReadBenchmarkAsync()
{
#if DEBUG
const int innerLoopIterations = 1;
#else
const int innerLoopIterations = 1000;
#endif
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.LayoutReadBenchmark(resolver, "Guests", "Guests", innerLoopIterations, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task StreamingHotelWriteBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.StreamingWriteBenchmark(resolver, "Hotels", "Hotels", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task StreamingRoomsWriteBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.StreamingWriteBenchmark(resolver, "Available_Rooms_By_Hotel_Date", "Rooms", 10000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task StreamingGuestsWriteBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.StreamingWriteBenchmark(resolver, "Guests", "Guests", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.HotelExpected, TestData.Target)]
public async Task StreamingHotelReadBenchmarkAsync()
{
string expectedFile = TestData.HotelExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.StreamingReadBenchmark(resolver, "Hotels", "Hotels", 1000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.RoomsExpected, TestData.Target)]
public async Task StreamingRoomsReadBenchmarkAsync()
{
string expectedFile = TestData.RoomsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.StreamingReadBenchmark(resolver, "Available_Rooms_By_Hotel_Date", "Rooms", 10000, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.GuestsExpected, TestData.Target)]
public async Task StreamingGuestsReadBenchmarkAsync()
{
string expectedFile = TestData.GuestsExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
SchematizedMicroBenchmarkSuite.StreamingReadBenchmark(resolver, "Guests", "Guests", 1000, expected);
}
private static void JsonWriteBenchmark(
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
Encoding utf8Encoding = new UTF8Encoding();
JsonSerializer jsonSerializer = JsonSerializer.Create(SchematizedMicroBenchmarkSuite.JsonSettings);
using (MemoryStream jsonStream = new MemoryStream(BenchmarkSuiteBase.InitialCapacity))
using (StreamWriter textWriter = new StreamWriter(jsonStream, utf8Encoding))
using (JsonTextWriter jsonWriter = new JsonTextWriter(textWriter))
{
BenchmarkContext ignoredContext = default;
jsonSerializer.Converters.Add(new Utf8StringJsonConverter());
MicroBenchmarkSuiteBase.Benchmark(
"Schematized",
"Write",
dataSetName,
"JSON",
innerLoopIterations,
ref ignoredContext,
(ref BenchmarkContext _, Dictionary<Utf8String, object> tableValue) =>
{
jsonStream.SetLength(0);
jsonSerializer.Serialize(jsonWriter, tableValue);
jsonWriter.Flush();
},
(ref BenchmarkContext _, Dictionary<Utf8String, object> value) => jsonStream.Length,
expected);
}
}
private static void JsonReadBenchmark(string dataSetName, int innerLoopIterations, List<Dictionary<Utf8String, object>> expected)
{
// Serialize input data to sequence of byte buffers.
List<byte[]> expectedSerialized = new List<byte[]>(expected.Count);
Encoding utf8Encoding = new UTF8Encoding();
JsonSerializer jsonSerializer = JsonSerializer.Create(SchematizedMicroBenchmarkSuite.JsonSettings);
using (MemoryStream jsonStream = new MemoryStream(BenchmarkSuiteBase.InitialCapacity))
using (StreamWriter textWriter = new StreamWriter(jsonStream, utf8Encoding))
using (JsonTextWriter jsonWriter = new JsonTextWriter(textWriter))
{
jsonSerializer.Converters.Add(new Utf8StringJsonConverter());
foreach (Dictionary<Utf8String, object> tableValue in expected)
{
jsonSerializer.Serialize(jsonWriter, tableValue);
jsonWriter.Flush();
expectedSerialized.Add(jsonStream.ToArray());
jsonStream.SetLength(0);
}
}
BenchmarkContext ignoredContext = default;
jsonSerializer.Converters.Add(new Utf8StringJsonConverter());
MicroBenchmarkSuiteBase.Benchmark(
"Schematized",
"Read",
dataSetName,
"JSON",
innerLoopIterations,
ref ignoredContext,
(ref BenchmarkContext _, byte[] tableValue) =>
{
using (MemoryStream jsonStream = new MemoryStream(tableValue))
using (StreamReader textReader = new StreamReader(jsonStream, utf8Encoding))
using (JsonTextReader jsonReader = new JsonTextReader(textReader))
{
while (jsonReader.Read())
{
// Just visit the entire structure without materializing any of the values.
}
}
},
(ref BenchmarkContext _, byte[] tableValue) => tableValue.Length,
expectedSerialized);
}
private static void BsonWriteBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
using (BsonRowGenerator writer = new BsonRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver))
{
BenchmarkContext ignoredContext = default;
MicroBenchmarkSuiteBase.Benchmark(
"Schematized",
"Write",
dataSetName,
"BSON",
innerLoopIterations,
ref ignoredContext,
(ref BenchmarkContext _, Dictionary<Utf8String, object> tableValue) =>
{
writer.Reset();
writer.WriteBuffer(tableValue);
},
(ref BenchmarkContext _, Dictionary<Utf8String, object> tableValue) => writer.Length,
expected);
}
}
private static void BsonReadBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
// Serialize input data to sequence of byte buffers.
List<byte[]> expectedSerialized = new List<byte[]>(expected.Count);
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
using (BsonRowGenerator writer = new BsonRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver))
{
foreach (Dictionary<Utf8String, object> tableValue in expected)
{
writer.Reset();
writer.WriteBuffer(tableValue);
expectedSerialized.Add(writer.ToArray());
}
}
BenchmarkContext ignoredContext = default;
MicroBenchmarkSuiteBase.Benchmark(
"Schematized",
"Read",
dataSetName,
"BSON",
innerLoopIterations,
ref ignoredContext,
(ref BenchmarkContext _, byte[] tableValue) =>
{
using (MemoryStream stm = new MemoryStream(tableValue))
using (BsonBinaryReader bsonReader = new BsonBinaryReader(stm))
{
bsonReader.VisitBsonDocument();
}
},
(ref BenchmarkContext _, byte[] tableValue) => tableValue.Length,
expectedSerialized);
}
private static void LayoutWriteBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
BenchmarkContext context = new BenchmarkContext
{
PatchWriter = new WriteRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver)
};
MicroBenchmarkSuiteBase.Benchmark(
"Schematized",
"Write",
dataSetName,
"Layout",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> dict) =>
{
ctx.PatchWriter.Reset();
Result r = ctx.PatchWriter.DispatchLayout(layout, dict);
ResultAssert.IsSuccess(r);
},
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> _) => ctx.PatchWriter.Length,
expected);
}
private static void LayoutReadBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
// Serialize input data to sequence of byte buffers.
List<byte[]> expectedSerialized = new List<byte[]>(expected.Count);
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
BenchmarkContext context = new BenchmarkContext
{
StreamingWriter = new StreamingRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver)
};
foreach (Dictionary<Utf8String, object> tableValue in expected)
{
context.StreamingWriter.Reset();
Result r = context.StreamingWriter.WriteBuffer(tableValue);
ResultAssert.IsSuccess(r);
expectedSerialized.Add(context.StreamingWriter.ToArray());
}
MicroBenchmarkSuiteBase.Benchmark(
"Schematized",
"Read",
dataSetName,
"Layout",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, byte[] tableValue) =>
{
VisitRowGenerator visitor = new VisitRowGenerator(tableValue.AsSpan(), resolver);
Result r = visitor.DispatchLayout(layout);
ResultAssert.IsSuccess(r);
},
(ref BenchmarkContext ctx, byte[] tableValue) => tableValue.Length,
expectedSerialized);
}
private static void StreamingWriteBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
BenchmarkContext context = new BenchmarkContext
{
StreamingWriter = new StreamingRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver)
};
MicroBenchmarkSuiteBase.Benchmark(
"Schematized",
"Write",
dataSetName,
"HybridRow",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> tableValue) =>
{
ctx.StreamingWriter.Reset();
Result r = ctx.StreamingWriter.WriteBuffer(tableValue);
ResultAssert.IsSuccess(r);
},
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> _) => ctx.StreamingWriter.Length,
expected);
}
private static void StreamingReadBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
// Serialize input data to sequence of byte buffers.
List<byte[]> expectedSerialized = new List<byte[]>(expected.Count);
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
BenchmarkContext context = new BenchmarkContext
{
StreamingWriter = new StreamingRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver)
};
foreach (Dictionary<Utf8String, object> tableValue in expected)
{
context.StreamingWriter.Reset();
Result r = context.StreamingWriter.WriteBuffer(tableValue);
ResultAssert.IsSuccess(r);
expectedSerialized.Add(context.StreamingWriter.ToArray());
}
MicroBenchmarkSuiteBase.Benchmark(
"Schematized",
"Read",
dataSetName,
"HybridRow",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, byte[] tableValue) =>
{
RowBuffer row = new RowBuffer(tableValue.AsSpan(), HybridRowVersion.V1, resolver);
RowReader reader = new RowReader(ref row);
reader.VisitReader();
},
(ref BenchmarkContext ctx, byte[] tableValue) => tableValue.Length,
expectedSerialized);
}
}
}

View File

@@ -0,0 +1,22 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
/// <summary>
/// Names of assets in the TestData folder.
/// </summary>
internal class TestData
{
/// <summary>
/// The folder to which TestData assets should be copied during deployment.
/// </summary>
public const string Target = "TestData";
public const string SchemaFile = @"TestData\CassandraHotelSchema.json";
public const string HotelExpected = @"TestData\HotelSchemaExpected.hr";
public const string RoomsExpected = @"TestData\RoomsSchemaExpected.hr";
public const string GuestsExpected = @"TestData\GuestsSchemaExpected.hr";
public const string Messages1KExpected = @"TestData\Messages1KExpected.hr";
}
}

View File

@@ -0,0 +1,76 @@
// Partial implementation of Cassandra Hotel Schema described here::
// https://www.oreilly.com/ideas/cassandra-data-modeling
{
"name": "Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel",
"schemas": [
{
"name": "PostalCode",
"id": 1,
"type": "schema",
"properties": [
{ "path": "zip", "type": { "type": "int32", "storage": "fixed" } },
{ "path": "plus4", "type": { "type": "int16", "storage": "sparse" } }
]
},
{
"name": "Address",
"id": 2,
"type": "schema",
"properties": [
{ "path": "street", "type": { "type": "utf8", "storage": "variable" } },
{ "path": "city", "type": { "type": "utf8", "storage": "variable" } },
{ "path": "state", "type": { "type": "utf8", "storage": "fixed", "length": 2 } },
{ "path": "postal_code", "type": { "type": "schema", "name": "PostalCode" } }
]
},
{
"name": "Hotels",
"id": 3,
"type": "schema",
"partitionkeys": [{ "path": "hotel_id" }],
"properties": [
{ "path": "hotel_id", "type": { "type": "utf8", "storage": "fixed", "length": 8 } },
{ "path": "name", "type": { "type": "utf8", "storage": "variable" } },
{ "path": "phone", "type": { "type": "utf8", "storage": "variable" } },
{ "path": "address", "type": { "type": "schema", "name": "Address", "immutable": true } }
]
},
{
"name": "Available_Rooms_By_Hotel_Date",
"id": 4,
"type": "schema",
"partitionkeys": [{ "path": "hotel_id" }],
"primarykeys": [{ "path": "date" }, { "path": "room_number", "direction": "desc" }],
"properties": [
{ "path": "hotel_id", "type": { "type": "utf8", "storage": "fixed", "length": 8 } },
{ "path": "date", "type": { "type": "datetime", "storage": "fixed" } },
{ "path": "room_number", "type": { "type": "uint8", "storage": "fixed" } },
{ "path": "is_available", "type": { "type": "bool", "storage": "fixed" } }
]
},
{
"name": "Guests",
"id": 5,
"type": "schema",
"partitionkeys": [{ "path": "guest_id" }],
"primarykeys": [{"path": "first_name"}, {"path": "phone_numbers", "direction": "desc"}],
"properties": [
{ "path": "guest_id", "type": { "type": "guid", "storage": "fixed" } },
{ "path": "first_name", "type": { "type": "utf8", "storage": "variable" } },
{ "path": "last_name", "type": { "type": "utf8", "storage": "variable" } },
{ "path": "title", "type": { "type": "utf8", "storage": "variable", "length": 20 } },
{ "path": "emails", "type": { "type": "array", "items": { "type": "utf8", "nullable": false } } },
{ "path": "phone_numbers", "type": { "type": "array", "items": { "type": "utf8", "nullable": false } } },
{
"path": "addresses",
"type": {
"type": "map",
"keys": { "type": "utf8", "nullable": false },
"values": { "type": "schema", "name": "Address", "immutable": true, "nullable": false }
}
},
{ "path": "confirm_number", "type": { "type": "utf8", "storage": "variable" } }
]
}
]
}

View File

@@ -0,0 +1,43 @@
syntax = "proto3";
package Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel;
import "google/protobuf/wrappers.proto";
option csharp_namespace = "Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf";
message PostalCode {
google.protobuf.Int32Value zip = 1;
google.protobuf.Int32Value plus4 = 2;
}
message Address {
google.protobuf.StringValue street = 1;
google.protobuf.StringValue city = 2;
google.protobuf.StringValue state = 3;
PostalCode postal_code = 4;
}
message Hotels {
google.protobuf.StringValue hotel_id = 1;
google.protobuf.StringValue name = 2;
google.protobuf.StringValue phone = 3;
Address address = 4;
}
message Available_Rooms_By_Hotel_Date {
google.protobuf.StringValue hotel_id = 1;
google.protobuf.Int64Value date = 2; // datetime
google.protobuf.Int32Value room_number = 3;
google.protobuf.BoolValue is_available = 4;
}
message Guests {
google.protobuf.StringValue guest_id = 1; // guid
google.protobuf.StringValue first_name = 2;
google.protobuf.StringValue last_name = 3;
google.protobuf.StringValue title = 4;
repeated string emails = 5;
repeated string phone_numbers = 6;
map<string, Address> addresses = 7;
google.protobuf.StringValue confirm_number = 8;
}

View File

@@ -0,0 +1,338 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Internal;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MongoDB.Bson.IO;
using Newtonsoft.Json;
/// <summary>Tests involving fully (or mostly) unschematized test data.</summary>
[TestClass]
public sealed class UnschematizedMicroBenchmarkSuite : MicroBenchmarkSuiteBase
{
private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.Messages1KExpected, TestData.Target)]
public async Task Messages1KWriteBenchmarkAsync()
{
#if DEBUG
const int innerLoopIterations = 1;
#else
const int innerLoopIterations = 10;
#endif
string expectedFile = TestData.Messages1KExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
UnschematizedMicroBenchmarkSuite.JsonModelWriteBenchmark(
resolver,
"TypedJsonHybridRowSchema",
"Messages1K",
innerLoopIterations,
expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.Messages1KExpected, TestData.Target)]
public async Task Messages1KReadBenchmarkAsync()
{
#if DEBUG
const int innerLoopIterations = 1;
#else
const int innerLoopIterations = 10;
#endif
string expectedFile = TestData.Messages1KExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
UnschematizedMicroBenchmarkSuite.JsonModelReadBenchmark(
resolver,
"TypedJsonHybridRowSchema",
"Messages1K",
innerLoopIterations,
expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.Messages1KExpected, TestData.Target)]
public async Task BsonMessages1KWriteBenchmarkAsync()
{
#if DEBUG
const int innerLoopIterations = 1;
#else
const int innerLoopIterations = 10;
#endif
string expectedFile = TestData.Messages1KExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
UnschematizedMicroBenchmarkSuite.BsonWriteBenchmark("Messages1K", innerLoopIterations, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.Messages1KExpected, TestData.Target)]
public async Task BsonMessages1KReadBenchmarkAsync()
{
#if DEBUG
const int innerLoopIterations = 1;
#else
const int innerLoopIterations = 10;
#endif
string expectedFile = TestData.Messages1KExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
UnschematizedMicroBenchmarkSuite.BsonReadBenchmark("Messages1K", innerLoopIterations, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.Messages1KExpected, TestData.Target)]
public async Task JsonMessages1KWriteBenchmarkAsync()
{
#if DEBUG
const int innerLoopIterations = 1;
#else
const int innerLoopIterations = 10;
#endif
string expectedFile = TestData.Messages1KExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
UnschematizedMicroBenchmarkSuite.JsonWriteBenchmark("Messages1K", innerLoopIterations, expected);
}
[TestMethod]
[Owner("jthunter")]
[DeploymentItem(TestData.Messages1KExpected, TestData.Target)]
public async Task JsonMessages1KReadBenchmarkAsync()
{
#if DEBUG
const int innerLoopIterations = 1;
#else
const int innerLoopIterations = 10;
#endif
string expectedFile = TestData.Messages1KExpected;
(List<Dictionary<Utf8String, object>> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile);
UnschematizedMicroBenchmarkSuite.JsonReadBenchmark("Messages1K", innerLoopIterations, expected);
}
private static void JsonModelWriteBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
BenchmarkContext context = new BenchmarkContext
{
JsonModelWriter = new JsonModelRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver)
};
MicroBenchmarkSuiteBase.Benchmark(
"Unschematized",
"Write",
dataSetName,
"HybridRowSparse",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> tableValue) =>
{
ctx.JsonModelWriter.Reset();
Result r = ctx.JsonModelWriter.WriteBuffer(tableValue);
ResultAssert.IsSuccess(r);
},
(ref BenchmarkContext ctx, Dictionary<Utf8String, object> tableValue) => ctx.JsonModelWriter.Length,
expected);
}
private static void JsonModelReadBenchmark(
LayoutResolverNamespace resolver,
string schemaName,
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
// Serialize input data to sequence of byte buffers.
List<byte[]> expectedSerialized = new List<byte[]>(expected.Count);
Layout layout = resolver.Resolve(resolver.Namespace.Schemas.Find(x => x.Name == schemaName).SchemaId);
BenchmarkContext context = new BenchmarkContext
{
JsonModelWriter = new JsonModelRowGenerator(BenchmarkSuiteBase.InitialCapacity, layout, resolver)
};
foreach (Dictionary<Utf8String, object> tableValue in expected)
{
context.JsonModelWriter.Reset();
Result r = context.JsonModelWriter.WriteBuffer(tableValue);
ResultAssert.IsSuccess(r);
expectedSerialized.Add(context.JsonModelWriter.ToArray());
}
MicroBenchmarkSuiteBase.Benchmark(
"Unschematized",
"Read",
dataSetName,
"HybridRowSparse",
innerLoopIterations,
ref context,
(ref BenchmarkContext ctx, byte[] tableValue) =>
{
RowBuffer row = new RowBuffer(tableValue.AsSpan(), HybridRowVersion.V1, resolver);
RowReader reader = new RowReader(ref row);
reader.VisitReader();
},
(ref BenchmarkContext ctx, byte[] tableValue) => tableValue.Length,
expectedSerialized);
}
private static void JsonWriteBenchmark(
string dataSetName,
int innerLoopIterations,
List<Dictionary<Utf8String, object>> expected)
{
Encoding utf8Encoding = new UTF8Encoding();
JsonSerializer jsonSerializer = JsonSerializer.Create(UnschematizedMicroBenchmarkSuite.JsonSettings);
using (MemoryStream jsonStream = new MemoryStream(BenchmarkSuiteBase.InitialCapacity))
using (StreamWriter textWriter = new StreamWriter(jsonStream, utf8Encoding))
using (JsonTextWriter jsonWriter = new JsonTextWriter(textWriter))
{
BenchmarkContext ignoredContext = default;
jsonSerializer.Converters.Add(new Utf8StringJsonConverter());
MicroBenchmarkSuiteBase.Benchmark(
"Unschematized",
"Write",
dataSetName,
"JSON",
innerLoopIterations,
ref ignoredContext,
(ref BenchmarkContext _, Dictionary<Utf8String, object> tableValue) =>
{
jsonStream.SetLength(0);
jsonSerializer.Serialize(jsonWriter, tableValue);
jsonWriter.Flush();
},
(ref BenchmarkContext _, Dictionary<Utf8String, object> value) => jsonStream.Length,
expected);
}
}
private static void JsonReadBenchmark(string dataSetName, int innerLoopIterations, List<Dictionary<Utf8String, object>> expected)
{
// Serialize input data to sequence of byte buffers.
List<byte[]> expectedSerialized = new List<byte[]>(expected.Count);
Encoding utf8Encoding = new UTF8Encoding();
JsonSerializer jsonSerializer = JsonSerializer.Create(UnschematizedMicroBenchmarkSuite.JsonSettings);
using (MemoryStream jsonStream = new MemoryStream(BenchmarkSuiteBase.InitialCapacity))
using (StreamWriter textWriter = new StreamWriter(jsonStream, utf8Encoding))
using (JsonTextWriter jsonWriter = new JsonTextWriter(textWriter))
{
jsonSerializer.Converters.Add(new Utf8StringJsonConverter());
foreach (Dictionary<Utf8String, object> tableValue in expected)
{
jsonSerializer.Serialize(jsonWriter, tableValue);
jsonWriter.Flush();
expectedSerialized.Add(jsonStream.ToArray());
jsonStream.SetLength(0);
}
}
BenchmarkContext ignoredContext = default;
jsonSerializer.Converters.Add(new Utf8StringJsonConverter());
MicroBenchmarkSuiteBase.Benchmark(
"Unschematized",
"Read",
dataSetName,
"JSON",
innerLoopIterations,
ref ignoredContext,
(ref BenchmarkContext _, byte[] tableValue) =>
{
using (MemoryStream jsonStream = new MemoryStream(tableValue))
using (StreamReader textReader = new StreamReader(jsonStream, utf8Encoding))
using (JsonTextReader jsonReader = new JsonTextReader(textReader))
{
while (jsonReader.Read())
{
// Just visit the entire structure without materializing any of the values.
}
}
},
(ref BenchmarkContext _, byte[] tableValue) => tableValue.Length,
expectedSerialized);
}
private static void BsonWriteBenchmark(string dataSetName, int innerLoopIterations, List<Dictionary<Utf8String, object>> expected)
{
using (BsonJsonModelRowGenerator writer = new BsonJsonModelRowGenerator(BenchmarkSuiteBase.InitialCapacity))
{
BenchmarkContext ignoredContext = default;
MicroBenchmarkSuiteBase.Benchmark(
"Unschematized",
"Write",
dataSetName,
"BSON",
innerLoopIterations,
ref ignoredContext,
(ref BenchmarkContext _, Dictionary<Utf8String, object> tableValue) =>
{
writer.Reset();
writer.WriteBuffer(tableValue);
},
(ref BenchmarkContext _, Dictionary<Utf8String, object> tableValue) => writer.Length,
expected);
}
}
private static void BsonReadBenchmark(string dataSetName, int innerLoopIterations, List<Dictionary<Utf8String, object>> expected)
{
// Serialize input data to sequence of byte buffers.
List<byte[]> expectedSerialized = new List<byte[]>(expected.Count);
using (BsonJsonModelRowGenerator writer = new BsonJsonModelRowGenerator(BenchmarkSuiteBase.InitialCapacity))
{
foreach (Dictionary<Utf8String, object> tableValue in expected)
{
writer.Reset();
writer.WriteBuffer(tableValue);
expectedSerialized.Add(writer.ToArray());
}
}
BenchmarkContext ignoredContext = default;
MicroBenchmarkSuiteBase.Benchmark(
"Unschematized",
"Read",
dataSetName,
"BSON",
innerLoopIterations,
ref ignoredContext,
(ref BenchmarkContext _, byte[] tableValue) =>
{
using (MemoryStream stm = new MemoryStream(tableValue))
using (BsonBinaryReader bsonReader = new BsonBinaryReader(stm))
{
bsonReader.VisitBsonDocument();
}
},
(ref BenchmarkContext _, byte[] tableValue) => tableValue.Length,
expectedSerialized);
}
}
}