mirror of
https://github.com/microsoft/HybridRow.git
synced 2026-01-24 11:53:15 +00:00
Copied dotnet code from CosmosDB repository
This commit is contained in:
11
dotnet/src/HybridRow.Tests.Perf/App.config
Normal file
11
dotnet/src/HybridRow.Tests.Perf/App.config
Normal 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>
|
||||
119
dotnet/src/HybridRow.Tests.Perf/BenchmarkSuiteBase.cs
Normal file
119
dotnet/src/HybridRow.Tests.Perf/BenchmarkSuiteBase.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
110
dotnet/src/HybridRow.Tests.Perf/BsonJsonModelRowGenerator.cs
Normal file
110
dotnet/src/HybridRow.Tests.Perf/BsonJsonModelRowGenerator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
63
dotnet/src/HybridRow.Tests.Perf/BsonReaderExtensions.cs
Normal file
63
dotnet/src/HybridRow.Tests.Perf/BsonReaderExtensions.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
303
dotnet/src/HybridRow.Tests.Perf/BsonRowGenerator.cs
Normal file
303
dotnet/src/HybridRow.Tests.Perf/BsonRowGenerator.cs
Normal 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
293
dotnet/src/HybridRow.Tests.Perf/CodeGenMicroBenchmarkSuite.cs
Normal file
293
dotnet/src/HybridRow.Tests.Perf/CodeGenMicroBenchmarkSuite.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
872
dotnet/src/HybridRow.Tests.Perf/CodeGenRowGenerator.cs
Normal file
872
dotnet/src/HybridRow.Tests.Perf/CodeGenRowGenerator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100
dotnet/src/HybridRow.Tests.Perf/GenerateBenchmarkSuite.cs
Normal file
100
dotnet/src/HybridRow.Tests.Perf/GenerateBenchmarkSuite.cs
Normal 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
dotnet/src/HybridRow.Tests.Perf/GenerateProtoBuf.cmd
Normal file
7
dotnet/src/HybridRow.Tests.Perf/GenerateProtoBuf.cmd
Normal 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
|
||||
43
dotnet/src/HybridRow.Tests.Perf/HybridRowPerf.csv
Normal file
43
dotnet/src/HybridRow.Tests.Perf/HybridRowPerf.csv
Normal 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
|
||||
|
132
dotnet/src/HybridRow.Tests.Perf/JsonModelRowGenerator.cs
Normal file
132
dotnet/src/HybridRow.Tests.Perf/JsonModelRowGenerator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
dotnet/src/HybridRow.Tests.Perf/Measurements.cs
Normal file
89
dotnet/src/HybridRow.Tests.Perf/Measurements.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
128
dotnet/src/HybridRow.Tests.Perf/MicroBenchmarkSuiteBase.cs
Normal file
128
dotnet/src/HybridRow.Tests.Perf/MicroBenchmarkSuiteBase.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
19
dotnet/src/HybridRow.Tests.Perf/Properties/AssemblyInfo.cs
Normal file
19
dotnet/src/HybridRow.Tests.Perf/Properties/AssemblyInfo.cs
Normal 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")]
|
||||
315
dotnet/src/HybridRow.Tests.Perf/ProtobufRowGenerator.cs
Normal file
315
dotnet/src/HybridRow.Tests.Perf/ProtobufRowGenerator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
282
dotnet/src/HybridRow.Tests.Perf/ReaderBenchmark.cs
Normal file
282
dotnet/src/HybridRow.Tests.Perf/ReaderBenchmark.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
dotnet/src/HybridRow.Tests.Perf/RowReaderExtensions.cs
Normal file
97
dotnet/src/HybridRow.Tests.Perf/RowReaderExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
22
dotnet/src/HybridRow.Tests.Perf/TestData.cs
Normal file
22
dotnet/src/HybridRow.Tests.Perf/TestData.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
@@ -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" } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/CombinedScriptsData.hr
Normal file
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/CombinedScriptsData.hr
Normal file
Binary file not shown.
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/GuestsSchemaExpected.hr
Normal file
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/GuestsSchemaExpected.hr
Normal file
Binary file not shown.
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/HotelSchemaExpected.hr
Normal file
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/HotelSchemaExpected.hr
Normal file
Binary file not shown.
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/Messages1KExpected.hr
Normal file
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/Messages1KExpected.hr
Normal file
Binary file not shown.
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/RoomsSchemaExpected.hr
Normal file
BIN
dotnet/src/HybridRow.Tests.Perf/TestData/RoomsSchemaExpected.hr
Normal file
Binary file not shown.
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user