From 31f3bc828b090929bd95f6b8deead465a2442782 Mon Sep 17 00:00:00 2001 From: David Noble Date: Tue, 20 Aug 2019 11:58:29 -0700 Subject: [PATCH] Copied dotnet code from CosmosDB repository --- Hybrid Row Whitepaper.docx | Bin 0 -> 283536 bytes ...Cosmos.Serialization.HybridRow.Json.csproj | 21 + .../HybridRow.Json/Properties/AssemblyInfo.cs | 20 + .../HybridRow.Json/RowReaderJsonExtensions.cs | 453 +++ .../HybridRow.Json/RowReaderJsonSettings.cs | 24 + ...mos.Serialization.HybridRow.Package.nuproj | 26 + ...mos.Serialization.HybridRow.Package.nuspec | 29 + dotnet/src/HybridRow.Tests.Perf/App.config | 11 + .../BenchmarkSuiteBase.cs | 119 + .../BsonJsonModelRowGenerator.cs | 110 + .../BsonReaderExtensions.cs | 63 + .../HybridRow.Tests.Perf/BsonRowGenerator.cs | 303 ++ .../CassandraProto/CassandraHotelSchema.cs | 1283 +++++++ .../CodeGenMicroBenchmarkSuite.cs | 293 ++ .../CodeGenRowGenerator.cs | 872 +++++ .../GenerateBenchmarkSuite.cs | 100 + .../HybridRow.Tests.Perf/GenerateProtoBuf.cmd | 7 + .../HybridRow.Tests.Perf/HybridRowPerf.csv | 43 + .../JsonModelRowGenerator.cs | 132 + .../src/HybridRow.Tests.Perf/Measurements.cs | 89 + .../MicroBenchmarkSuiteBase.cs | 128 + ....Serialization.HybridRow.Tests.Perf.csproj | 52 + .../Properties/AssemblyInfo.cs | 19 + .../ProtobufRowGenerator.cs | 315 ++ .../HybridRow.Tests.Perf/ReaderBenchmark.cs | 282 ++ .../RowReaderExtensions.cs | 97 + .../SchematizedMicroBenchmarkSuite.cs | 583 +++ dotnet/src/HybridRow.Tests.Perf/TestData.cs | 22 + .../TestData/CassandraHotelSchema.json | 76 + .../TestData/CassandraHotelSchema.proto | 43 + .../TestData/CombinedScriptsData.hr | Bin 0 -> 827289 bytes .../TestData/GuestsSchemaExpected.hr | Bin 0 -> 65144 bytes .../TestData/HotelSchemaExpected.hr | Bin 0 -> 19067 bytes .../TestData/Messages1KExpected.hr | Bin 0 -> 3422803 bytes .../TestData/RoomsSchemaExpected.hr | Bin 0 -> 6968 bytes .../UnschematizedMicroBenchmarkSuite.cs | 338 ++ dotnet/src/HybridRow.Tests.Unit/App.config | 11 + .../src/HybridRow.Tests.Unit/ArrayAssert.cs | 59 + .../AssertThrowsException.cs | 137 + .../CrossVersioningUnitTests.cs | 494 +++ .../CustomerExampleUnitTests.cs | 481 +++ .../CustomerSchema/Address.cs | 51 + .../CustomerSchema/AddressSerializer.cs | 104 + .../CustomerSchema/Guest.cs | 103 + .../CustomerSchema/Hotel.cs | 51 + .../CustomerSchema/PostalCode.cs | 42 + .../CustomerSchema/PostalCodeSerializer.cs | 68 + .../GlobalSuppressions.cs | 9 + .../Internal/MurmurHash3UnitTests.cs | 102 + .../LayoutCompilerUnitTests.cs | 1999 ++++++++++ .../LayoutTypeUnitTests.cs | 53 + ....Serialization.HybridRow.Tests.Unit.csproj | 82 + .../HybridRow.Tests.Unit/NullableUnitTests.cs | 437 +++ .../HybridRow.Tests.Unit/PermuteExtensions.cs | 38 + .../Properties/AssemblyInfo.cs | 19 + .../RandomGeneratorUnitTests.cs | 66 + .../HybridRow.Tests.Unit/RecordIOUnitTests.cs | 198 + .../src/HybridRow.Tests.Unit/ResultAssert.cs | 101 + .../RowBufferUnitTests.cs | 60 + .../RowOperationDispatcher.cs | 890 +++++ .../RowReaderUnitTests.cs | 353 ++ .../RowWriterUnitTests.cs | 553 +++ .../SchemaHashUnitTests.cs | 249 ++ .../HybridRow.Tests.Unit/SchemaIdUnitTests.cs | 47 + .../HybridRow.Tests.Unit/SchemaUnitTests.cs | 603 +++ .../SerializerUnitTest.cs | 317 ++ .../HybridRow.Tests.Unit/TaggedUnitTests.cs | 145 + .../TestData/BatchApiSchema.json | 96 + .../TestData/CoverageSchema.json | 211 ++ .../TestData/CrossVersioningExpected.json | 8 + .../TestData/CrossVersioningSchema.json | 127 + .../TestData/CustomerSchema.json | 76 + .../TestData/MovieSchema.json | 40 + .../TestData/NullableSchema.json | 26 + .../TestData/PerfCounterSchema.json | 72 + .../TestData/ReaderSchema.json | 143 + .../TestData/SchemaHashCoverageSchema.json | 171 + .../TestData/TagSchema.json | 28 + .../TestData/TaggedApiSchema.json | 28 + .../TestData/TodoSchema.json | 75 + .../HybridRow.Tests.Unit/TupleUnitTests.cs | 546 +++ .../TypedArrayUnitTests.cs | 413 ++ .../HybridRow.Tests.Unit/TypedMapUnitTests.cs | 704 ++++ .../HybridRow.Tests.Unit/TypedSetUnitTests.cs | 877 +++++ .../UpdateOptionsUnitTests.cs | 24 + dotnet/src/HybridRow/DefaultSpanResizer.cs | 31 + dotnet/src/HybridRow/Docs/Glossary.md | 71 + dotnet/src/HybridRow/Docs/Grammar.md | 109 + dotnet/src/HybridRow/Docs/RecordIO.md | 38 + dotnet/src/HybridRow/Docs/SchemaHash.md | 53 + dotnet/src/HybridRow/Docs/SchemaId.md | 49 + dotnet/src/HybridRow/Docs/SystemSchema.md | 27 + dotnet/src/HybridRow/Float128.cs | 57 + dotnet/src/HybridRow/GlobalSuppressions.cs | 9 + dotnet/src/HybridRow/HybridRowHeader.cs | 33 + dotnet/src/HybridRow/HybridRowVersion.cs | 18 + dotnet/src/HybridRow/IO/IRowSerializable.cs | 22 + dotnet/src/HybridRow/IO/RowReader.cs | 1027 +++++ .../src/HybridRow/IO/RowReaderExtensions.cs | 79 + dotnet/src/HybridRow/IO/RowWriter.cs | 810 ++++ dotnet/src/HybridRow/ISpanResizer.cs | 24 + dotnet/src/HybridRow/Internal/MurmurHash3.cs | 229 ++ .../Internal/Utf8StringJsonConverter.cs | 29 + dotnet/src/HybridRow/Layouts/Layout.cs | 175 + dotnet/src/HybridRow/Layouts/LayoutBit.cs | 123 + dotnet/src/HybridRow/Layouts/LayoutBuilder.cs | 230 ++ dotnet/src/HybridRow/Layouts/LayoutCode.cs | 74 + .../src/HybridRow/Layouts/LayoutCodeTraits.cs | 43 + dotnet/src/HybridRow/Layouts/LayoutColumn.cs | 228 ++ .../Layouts/LayoutCompilationException.cs | 34 + .../src/HybridRow/Layouts/LayoutCompiler.cs | 335 ++ .../src/HybridRow/Layouts/LayoutResolver.cs | 11 + .../Layouts/LayoutResolverNamespace.cs | 64 + .../HybridRow/Layouts/LayoutResolverSimple.cs | 23 + dotnet/src/HybridRow/Layouts/LayoutType.cs | 3337 +++++++++++++++++ .../Layouts/SamplingStringComparer.cs | 53 + .../Layouts/SamplingUtf8StringComparer.cs | 54 + .../src/HybridRow/Layouts/StringTokenizer.cs | 154 + dotnet/src/HybridRow/Layouts/SystemSchema.cs | 61 + dotnet/src/HybridRow/Layouts/TypeArgument.cs | 100 + .../src/HybridRow/Layouts/TypeArgumentList.cs | 154 + dotnet/src/HybridRow/Layouts/UpdateOptions.cs | 42 + dotnet/src/HybridRow/MemorySpanResizer.cs | 40 + ...zure.Cosmos.Serialization.HybridRow.csproj | 31 + dotnet/src/HybridRow/MongoDBObjectId.cs | 152 + dotnet/src/HybridRow/NullValue.cs | 57 + .../src/HybridRow/Properties/AssemblyInfo.cs | 26 + dotnet/src/HybridRow/RecordIO/Record.cs | 20 + .../HybridRow/RecordIO/RecordIOFormatter.cs | 55 + .../src/HybridRow/RecordIO/RecordIOParser.cs | 229 ++ .../src/HybridRow/RecordIO/RecordIOStream.cs | 304 ++ .../HybridRow/RecordIO/RecordSerializer.cs | 62 + dotnet/src/HybridRow/RecordIO/Segment.cs | 22 + .../HybridRow/RecordIO/SegmentSerializer.cs | 103 + dotnet/src/HybridRow/Result.cs | 45 + dotnet/src/HybridRow/RowBuffer.cs | 2957 +++++++++++++++ dotnet/src/HybridRow/RowCursor.cs | 332 ++ dotnet/src/HybridRow/RowOptions.cs | 49 + dotnet/src/HybridRow/SchemaId.cs | 98 + .../HybridRow/Schemas/ArrayPropertyType.cs | 22 + .../src/HybridRow/Schemas/MapPropertyType.cs | 30 + dotnet/src/HybridRow/Schemas/Namespace.cs | 65 + .../HybridRow/Schemas/ObjectPropertyType.cs | 43 + dotnet/src/HybridRow/Schemas/PartitionKey.cs | 17 + .../src/HybridRow/Schemas/PrimarySortKey.cs | 25 + .../Schemas/PrimitivePropertyType.cs | 29 + dotnet/src/HybridRow/Schemas/Property.cs | 37 + .../Schemas/PropertySchemaConverter.cs | 82 + dotnet/src/HybridRow/Schemas/PropertyType.cs | 34 + dotnet/src/HybridRow/Schemas/Schema.cs | 171 + .../src/HybridRow/Schemas/SchemaException.cs | 34 + dotnet/src/HybridRow/Schemas/SchemaHash.cs | 222 ++ .../Schemas/SchemaLanguageVersion.cs | 19 + dotnet/src/HybridRow/Schemas/SchemaOptions.cs | 43 + .../src/HybridRow/Schemas/SchemaValidator.cs | 251 ++ .../HybridRow/Schemas/ScopePropertyType.cs | 17 + .../src/HybridRow/Schemas/SetPropertyType.cs | 25 + dotnet/src/HybridRow/Schemas/SortDirection.cs | 23 + dotnet/src/HybridRow/Schemas/StaticKey.cs | 19 + dotnet/src/HybridRow/Schemas/StorageKind.cs | 43 + .../Schemas/StrictBooleanConverter.cs | 36 + .../Schemas/StrictIntegerConverter.cs | 55 + .../HybridRow/Schemas/TaggedPropertyType.cs | 44 + .../HybridRow/Schemas/TuplePropertyType.cs | 37 + dotnet/src/HybridRow/Schemas/TypeKind.cs | 133 + .../src/HybridRow/Schemas/UdtPropertyType.cs | 41 + .../HybridRow/SystemSchemas/SystemSchema.json | 54 + dotnet/src/HybridRow/UnixDateTime.cs | 77 + dotnet/src/HybridRowCLI/App.config | 11 + dotnet/src/HybridRowCLI/CompileCommand.cs | 82 + dotnet/src/HybridRowCLI/ConsoleNative.cs | 78 + .../src/HybridRowCLI/Csv2HybridRowCommand.cs | 474 +++ .../src/HybridRowCLI/HybridRowCLIProgram.cs | 41 + .../src/HybridRowCLI/Json2HybridRowCommand.cs | 710 ++++ ...e.Cosmos.Serialization.HybridRowCLI.csproj | 30 + dotnet/src/HybridRowCLI/PrintCommand.cs | 441 +++ .../HybridRowCLI/Properties/AssemblyInfo.cs | 19 + dotnet/src/HybridRowCLI/SchemaUtil.cs | 72 + dotnet/src/HybridRowCLI/StringExtensions.cs | 19 + .../src/HybridRowGenerator/ByteConverter.cs | 114 + .../HybridRowGenerator/CharDistribution.cs | 36 + .../HybridRowGenerator/DiagnosticConverter.cs | 749 ++++ .../HybridRowGenerator/DistributionType.cs | 11 + .../HybridRowGeneratorConfig.cs | 105 + .../HybridRowValueGenerator.cs | 635 ++++ .../src/HybridRowGenerator/IntDistribution.cs | 36 + ...os.Serialization.HybridRowGenerator.csproj | 23 + .../Properties/AssemblyInfo.cs | 20 + .../src/HybridRowGenerator/RandomGenerator.cs | 131 + .../src/HybridRowGenerator/SchemaGenerator.cs | 277 ++ .../StreamingRowGenerator.cs | 330 ++ .../HybridRowGenerator/VisitRowGenerator.cs | 260 ++ .../HybridRowGenerator/WriteRowGenerator.cs | 502 +++ .../HybridRowStress/HybridRowStressConfig.cs | 157 + .../HybridRowStress/HybridRowStressProgram.cs | 317 ++ ...osmos.Serialization.HybridRowStress.csproj | 25 + .../Properties/AssemblyInfo.cs | 19 + dotnet/src/HybridRowStress/StressContext.cs | 142 + dotnet/src/HybridRowStress/SyntaxException.cs | 34 + dotnet/src/build.props | 20 + dotnet/src/dirs.proj | 23 + 201 files changed, 37803 insertions(+) create mode 100644 Hybrid Row Whitepaper.docx create mode 100644 dotnet/src/HybridRow.Json/Microsoft.Azure.Cosmos.Serialization.HybridRow.Json.csproj create mode 100644 dotnet/src/HybridRow.Json/Properties/AssemblyInfo.cs create mode 100644 dotnet/src/HybridRow.Json/RowReaderJsonExtensions.cs create mode 100644 dotnet/src/HybridRow.Json/RowReaderJsonSettings.cs create mode 100644 dotnet/src/HybridRow.Package/Microsoft.Azure.Cosmos.Serialization.HybridRow.Package.nuproj create mode 100644 dotnet/src/HybridRow.Package/Microsoft.Azure.Cosmos.Serialization.HybridRow.Package.nuspec create mode 100644 dotnet/src/HybridRow.Tests.Perf/App.config create mode 100644 dotnet/src/HybridRow.Tests.Perf/BenchmarkSuiteBase.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/BsonJsonModelRowGenerator.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/BsonReaderExtensions.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/BsonRowGenerator.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/CassandraProto/CassandraHotelSchema.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/CodeGenMicroBenchmarkSuite.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/CodeGenRowGenerator.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/GenerateBenchmarkSuite.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/GenerateProtoBuf.cmd create mode 100644 dotnet/src/HybridRow.Tests.Perf/HybridRowPerf.csv create mode 100644 dotnet/src/HybridRow.Tests.Perf/JsonModelRowGenerator.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/Measurements.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/MicroBenchmarkSuiteBase.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.csproj create mode 100644 dotnet/src/HybridRow.Tests.Perf/Properties/AssemblyInfo.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/ProtobufRowGenerator.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/ReaderBenchmark.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/RowReaderExtensions.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/SchematizedMicroBenchmarkSuite.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/TestData.cs create mode 100644 dotnet/src/HybridRow.Tests.Perf/TestData/CassandraHotelSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Perf/TestData/CassandraHotelSchema.proto create mode 100644 dotnet/src/HybridRow.Tests.Perf/TestData/CombinedScriptsData.hr create mode 100644 dotnet/src/HybridRow.Tests.Perf/TestData/GuestsSchemaExpected.hr create mode 100644 dotnet/src/HybridRow.Tests.Perf/TestData/HotelSchemaExpected.hr create mode 100644 dotnet/src/HybridRow.Tests.Perf/TestData/Messages1KExpected.hr create mode 100644 dotnet/src/HybridRow.Tests.Perf/TestData/RoomsSchemaExpected.hr create mode 100644 dotnet/src/HybridRow.Tests.Perf/UnschematizedMicroBenchmarkSuite.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/App.config create mode 100644 dotnet/src/HybridRow.Tests.Unit/ArrayAssert.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/AssertThrowsException.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/CrossVersioningUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/CustomerExampleUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/CustomerSchema/Address.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/CustomerSchema/AddressSerializer.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/CustomerSchema/Guest.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/CustomerSchema/Hotel.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/CustomerSchema/PostalCode.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/CustomerSchema/PostalCodeSerializer.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/GlobalSuppressions.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/Internal/MurmurHash3UnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/LayoutCompilerUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/LayoutTypeUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Unit.csproj create mode 100644 dotnet/src/HybridRow.Tests.Unit/NullableUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/PermuteExtensions.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/Properties/AssemblyInfo.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/RandomGeneratorUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/RecordIOUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/ResultAssert.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/RowBufferUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/RowOperationDispatcher.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/RowReaderUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/RowWriterUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/SchemaHashUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/SchemaIdUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/SchemaUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/SerializerUnitTest.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/TaggedUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/BatchApiSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/CoverageSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/CrossVersioningExpected.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/CrossVersioningSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/CustomerSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/MovieSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/NullableSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/PerfCounterSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/ReaderSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/SchemaHashCoverageSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/TagSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/TaggedApiSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TestData/TodoSchema.json create mode 100644 dotnet/src/HybridRow.Tests.Unit/TupleUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/TypedArrayUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/TypedMapUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/TypedSetUnitTests.cs create mode 100644 dotnet/src/HybridRow.Tests.Unit/UpdateOptionsUnitTests.cs create mode 100644 dotnet/src/HybridRow/DefaultSpanResizer.cs create mode 100644 dotnet/src/HybridRow/Docs/Glossary.md create mode 100644 dotnet/src/HybridRow/Docs/Grammar.md create mode 100644 dotnet/src/HybridRow/Docs/RecordIO.md create mode 100644 dotnet/src/HybridRow/Docs/SchemaHash.md create mode 100644 dotnet/src/HybridRow/Docs/SchemaId.md create mode 100644 dotnet/src/HybridRow/Docs/SystemSchema.md create mode 100644 dotnet/src/HybridRow/Float128.cs create mode 100644 dotnet/src/HybridRow/GlobalSuppressions.cs create mode 100644 dotnet/src/HybridRow/HybridRowHeader.cs create mode 100644 dotnet/src/HybridRow/HybridRowVersion.cs create mode 100644 dotnet/src/HybridRow/IO/IRowSerializable.cs create mode 100644 dotnet/src/HybridRow/IO/RowReader.cs create mode 100644 dotnet/src/HybridRow/IO/RowReaderExtensions.cs create mode 100644 dotnet/src/HybridRow/IO/RowWriter.cs create mode 100644 dotnet/src/HybridRow/ISpanResizer.cs create mode 100644 dotnet/src/HybridRow/Internal/MurmurHash3.cs create mode 100644 dotnet/src/HybridRow/Internal/Utf8StringJsonConverter.cs create mode 100644 dotnet/src/HybridRow/Layouts/Layout.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutBit.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutBuilder.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutCode.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutCodeTraits.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutColumn.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutCompilationException.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutCompiler.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutResolver.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutResolverNamespace.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutResolverSimple.cs create mode 100644 dotnet/src/HybridRow/Layouts/LayoutType.cs create mode 100644 dotnet/src/HybridRow/Layouts/SamplingStringComparer.cs create mode 100644 dotnet/src/HybridRow/Layouts/SamplingUtf8StringComparer.cs create mode 100644 dotnet/src/HybridRow/Layouts/StringTokenizer.cs create mode 100644 dotnet/src/HybridRow/Layouts/SystemSchema.cs create mode 100644 dotnet/src/HybridRow/Layouts/TypeArgument.cs create mode 100644 dotnet/src/HybridRow/Layouts/TypeArgumentList.cs create mode 100644 dotnet/src/HybridRow/Layouts/UpdateOptions.cs create mode 100644 dotnet/src/HybridRow/MemorySpanResizer.cs create mode 100644 dotnet/src/HybridRow/Microsoft.Azure.Cosmos.Serialization.HybridRow.csproj create mode 100644 dotnet/src/HybridRow/MongoDBObjectId.cs create mode 100644 dotnet/src/HybridRow/NullValue.cs create mode 100644 dotnet/src/HybridRow/Properties/AssemblyInfo.cs create mode 100644 dotnet/src/HybridRow/RecordIO/Record.cs create mode 100644 dotnet/src/HybridRow/RecordIO/RecordIOFormatter.cs create mode 100644 dotnet/src/HybridRow/RecordIO/RecordIOParser.cs create mode 100644 dotnet/src/HybridRow/RecordIO/RecordIOStream.cs create mode 100644 dotnet/src/HybridRow/RecordIO/RecordSerializer.cs create mode 100644 dotnet/src/HybridRow/RecordIO/Segment.cs create mode 100644 dotnet/src/HybridRow/RecordIO/SegmentSerializer.cs create mode 100644 dotnet/src/HybridRow/Result.cs create mode 100644 dotnet/src/HybridRow/RowBuffer.cs create mode 100644 dotnet/src/HybridRow/RowCursor.cs create mode 100644 dotnet/src/HybridRow/RowOptions.cs create mode 100644 dotnet/src/HybridRow/SchemaId.cs create mode 100644 dotnet/src/HybridRow/Schemas/ArrayPropertyType.cs create mode 100644 dotnet/src/HybridRow/Schemas/MapPropertyType.cs create mode 100644 dotnet/src/HybridRow/Schemas/Namespace.cs create mode 100644 dotnet/src/HybridRow/Schemas/ObjectPropertyType.cs create mode 100644 dotnet/src/HybridRow/Schemas/PartitionKey.cs create mode 100644 dotnet/src/HybridRow/Schemas/PrimarySortKey.cs create mode 100644 dotnet/src/HybridRow/Schemas/PrimitivePropertyType.cs create mode 100644 dotnet/src/HybridRow/Schemas/Property.cs create mode 100644 dotnet/src/HybridRow/Schemas/PropertySchemaConverter.cs create mode 100644 dotnet/src/HybridRow/Schemas/PropertyType.cs create mode 100644 dotnet/src/HybridRow/Schemas/Schema.cs create mode 100644 dotnet/src/HybridRow/Schemas/SchemaException.cs create mode 100644 dotnet/src/HybridRow/Schemas/SchemaHash.cs create mode 100644 dotnet/src/HybridRow/Schemas/SchemaLanguageVersion.cs create mode 100644 dotnet/src/HybridRow/Schemas/SchemaOptions.cs create mode 100644 dotnet/src/HybridRow/Schemas/SchemaValidator.cs create mode 100644 dotnet/src/HybridRow/Schemas/ScopePropertyType.cs create mode 100644 dotnet/src/HybridRow/Schemas/SetPropertyType.cs create mode 100644 dotnet/src/HybridRow/Schemas/SortDirection.cs create mode 100644 dotnet/src/HybridRow/Schemas/StaticKey.cs create mode 100644 dotnet/src/HybridRow/Schemas/StorageKind.cs create mode 100644 dotnet/src/HybridRow/Schemas/StrictBooleanConverter.cs create mode 100644 dotnet/src/HybridRow/Schemas/StrictIntegerConverter.cs create mode 100644 dotnet/src/HybridRow/Schemas/TaggedPropertyType.cs create mode 100644 dotnet/src/HybridRow/Schemas/TuplePropertyType.cs create mode 100644 dotnet/src/HybridRow/Schemas/TypeKind.cs create mode 100644 dotnet/src/HybridRow/Schemas/UdtPropertyType.cs create mode 100644 dotnet/src/HybridRow/SystemSchemas/SystemSchema.json create mode 100644 dotnet/src/HybridRow/UnixDateTime.cs create mode 100644 dotnet/src/HybridRowCLI/App.config create mode 100644 dotnet/src/HybridRowCLI/CompileCommand.cs create mode 100644 dotnet/src/HybridRowCLI/ConsoleNative.cs create mode 100644 dotnet/src/HybridRowCLI/Csv2HybridRowCommand.cs create mode 100644 dotnet/src/HybridRowCLI/HybridRowCLIProgram.cs create mode 100644 dotnet/src/HybridRowCLI/Json2HybridRowCommand.cs create mode 100644 dotnet/src/HybridRowCLI/Microsoft.Azure.Cosmos.Serialization.HybridRowCLI.csproj create mode 100644 dotnet/src/HybridRowCLI/PrintCommand.cs create mode 100644 dotnet/src/HybridRowCLI/Properties/AssemblyInfo.cs create mode 100644 dotnet/src/HybridRowCLI/SchemaUtil.cs create mode 100644 dotnet/src/HybridRowCLI/StringExtensions.cs create mode 100644 dotnet/src/HybridRowGenerator/ByteConverter.cs create mode 100644 dotnet/src/HybridRowGenerator/CharDistribution.cs create mode 100644 dotnet/src/HybridRowGenerator/DiagnosticConverter.cs create mode 100644 dotnet/src/HybridRowGenerator/DistributionType.cs create mode 100644 dotnet/src/HybridRowGenerator/HybridRowGeneratorConfig.cs create mode 100644 dotnet/src/HybridRowGenerator/HybridRowValueGenerator.cs create mode 100644 dotnet/src/HybridRowGenerator/IntDistribution.cs create mode 100644 dotnet/src/HybridRowGenerator/Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator.csproj create mode 100644 dotnet/src/HybridRowGenerator/Properties/AssemblyInfo.cs create mode 100644 dotnet/src/HybridRowGenerator/RandomGenerator.cs create mode 100644 dotnet/src/HybridRowGenerator/SchemaGenerator.cs create mode 100644 dotnet/src/HybridRowGenerator/StreamingRowGenerator.cs create mode 100644 dotnet/src/HybridRowGenerator/VisitRowGenerator.cs create mode 100644 dotnet/src/HybridRowGenerator/WriteRowGenerator.cs create mode 100644 dotnet/src/HybridRowStress/HybridRowStressConfig.cs create mode 100644 dotnet/src/HybridRowStress/HybridRowStressProgram.cs create mode 100644 dotnet/src/HybridRowStress/Microsoft.Azure.Cosmos.Serialization.HybridRowStress.csproj create mode 100644 dotnet/src/HybridRowStress/Properties/AssemblyInfo.cs create mode 100644 dotnet/src/HybridRowStress/StressContext.cs create mode 100644 dotnet/src/HybridRowStress/SyntaxException.cs create mode 100644 dotnet/src/build.props create mode 100644 dotnet/src/dirs.proj diff --git a/Hybrid Row Whitepaper.docx b/Hybrid Row Whitepaper.docx new file mode 100644 index 0000000000000000000000000000000000000000..9635cd47ba0e3b313271aae92843010905f17d3a GIT binary patch literal 283536 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 z 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 vigSHEY-?1WqQ@h0;Xd8Nl_FVm-KoAusT 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≥mB$5MID`|X|j9D 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∓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 literal 0 HcmV?d00001 diff --git a/dotnet/src/HybridRow.Json/Microsoft.Azure.Cosmos.Serialization.HybridRow.Json.csproj b/dotnet/src/HybridRow.Json/Microsoft.Azure.Cosmos.Serialization.HybridRow.Json.csproj new file mode 100644 index 0000000..d4b4d50 --- /dev/null +++ b/dotnet/src/HybridRow.Json/Microsoft.Azure.Cosmos.Serialization.HybridRow.Json.csproj @@ -0,0 +1,21 @@ + + + + true + {CE1C4987-FC19-4887-9EB6-13508F8DA644} + Library + Microsoft.Azure.Cosmos.Serialization.HybridRow.Json + Microsoft.Azure.Cosmos.Serialization.HybridRow.Json + netstandard2.0 + AnyCPU + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/src/HybridRow.Json/Properties/AssemblyInfo.cs b/dotnet/src/HybridRow.Json/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..be43fcc --- /dev/null +++ b/dotnet/src/HybridRow.Json/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +using System.Reflection; +using System.Runtime.CompilerServices; +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.Json")] + +// 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("CE1C4987-FC19-4887-9EB6-13508F8DA644")] diff --git a/dotnet/src/HybridRow.Json/RowReaderJsonExtensions.cs b/dotnet/src/HybridRow.Json/RowReaderJsonExtensions.cs new file mode 100644 index 0000000..3341c9b --- /dev/null +++ b/dotnet/src/HybridRow.Json/RowReaderJsonExtensions.cs @@ -0,0 +1,453 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Json +{ + using System; + using System.Text; + 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 static class RowReaderJsonExtensions + { + ///

+ /// Project a JSON document from a HybridRow . + /// + /// The reader to project to JSON. + /// If successful, the JSON document that corresponds to the . + /// The result. + public static Result ToJson(this ref RowReader reader, out string str) + { + return reader.ToJson(new RowReaderJsonSettings(" "), out str); + } + + /// + /// Project a JSON document from a HybridRow . + /// + /// The reader to project to JSON. + /// Settings that control how the JSON document is formatted. + /// If successful, the JSON document that corresponds to the . + /// The result. + public static Result ToJson(this ref RowReader reader, RowReaderJsonSettings settings, out string str) + { + ReaderStringContext ctx = new ReaderStringContext( + new StringBuilder(), + new RowReaderJsonSettings(settings.IndentChars, settings.QuoteChar == '\'' ? '\'' : '"'), + 1); + + ctx.Builder.Append("{"); + Result result = RowReaderJsonExtensions.ToJson(ref reader, ctx); + if (result != Result.Success) + { + str = null; + return result; + } + + ctx.Builder.Append(ctx.NewLine); + ctx.Builder.Append("}"); + str = ctx.Builder.ToString(); + return Result.Success; + } + + private static Result ToJson(ref RowReader reader, ReaderStringContext ctx) + { + int index = 0; + while (reader.Read()) + { + string path = !reader.Path.IsNull ? $"{ctx.Settings.QuoteChar}{reader.Path}{ctx.Settings.QuoteChar}:" : null; + if (index != 0) + { + ctx.Builder.Append(','); + } + + index++; + ctx.Builder.Append(ctx.NewLine); + ctx.WriteIndent(); + if (path != null) + { + ctx.Builder.Append(path); + ctx.Builder.Append(ctx.Separator); + } + + Result r; + char scopeBracket = default; + char scopeCloseBracket = default; + switch (reader.Type.LayoutCode) + { + case LayoutCode.Null: + { + r = reader.ReadNull(out NullValue _); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append("null"); + break; + } + + case LayoutCode.Boolean: + { + r = reader.ReadBool(out bool value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.Int8: + { + r = reader.ReadInt8(out sbyte value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.Int16: + { + r = reader.ReadInt16(out short value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.Int32: + { + r = reader.ReadInt32(out int value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.Int64: + { + r = reader.ReadInt64(out long value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.UInt8: + { + r = reader.ReadUInt8(out byte value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.UInt16: + { + r = reader.ReadUInt16(out ushort value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.UInt32: + { + r = reader.ReadUInt32(out uint value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.UInt64: + { + r = reader.ReadUInt64(out ulong value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.VarInt: + { + r = reader.ReadVarInt(out long value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.VarUInt: + { + r = reader.ReadVarUInt(out ulong value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.Float32: + { + r = reader.ReadFloat32(out float value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.Float64: + { + r = reader.ReadFloat64(out double value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.Float128: + { + r = reader.ReadFloat128(out Float128 _); + if (r != Result.Success) + { + return r; + } + + // ctx.Builder.AppendFormat("High: {0}, Low: {1}\n", value.High, value.Low); + Contract.Assert(false, "Float128 are not supported."); + break; + } + + case LayoutCode.Decimal: + { + r = reader.ReadDecimal(out decimal value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value); + break; + } + + case LayoutCode.DateTime: + { + r = reader.ReadDateTime(out DateTime value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(ctx.Settings.QuoteChar); + ctx.Builder.Append(value); + ctx.Builder.Append(ctx.Settings.QuoteChar); + break; + } + + case LayoutCode.UnixDateTime: + { + r = reader.ReadUnixDateTime(out UnixDateTime value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(value.Milliseconds); + break; + } + + case LayoutCode.Guid: + { + r = reader.ReadGuid(out Guid value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(ctx.Settings.QuoteChar); + ctx.Builder.Append(value.ToString()); + ctx.Builder.Append(ctx.Settings.QuoteChar); + break; + } + + case LayoutCode.MongoDbObjectId: + { + r = reader.ReadMongoDbObjectId(out MongoDbObjectId value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(ctx.Settings.QuoteChar); + ReadOnlyMemory bytes = value.ToByteArray(); + ctx.Builder.Append(bytes.Span.ToHexString()); + ctx.Builder.Append(ctx.Settings.QuoteChar); + break; + } + + case LayoutCode.Utf8: + { + r = reader.ReadString(out Utf8Span value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(ctx.Settings.QuoteChar); + ctx.Builder.Append(value.ToString()); + ctx.Builder.Append(ctx.Settings.QuoteChar); + break; + } + + case LayoutCode.Binary: + { + r = reader.ReadBinary(out ReadOnlySpan value); + if (r != Result.Success) + { + return r; + } + + ctx.Builder.Append(ctx.Settings.QuoteChar); + ctx.Builder.Append(value.ToHexString()); + ctx.Builder.Append(ctx.Settings.QuoteChar); + break; + } + + case LayoutCode.NullableScope: + case LayoutCode.ImmutableNullableScope: + { + if (!reader.HasValue) + { + ctx.Builder.Append("null"); + break; + } + + goto case LayoutCode.TypedTupleScope; + } + + 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: + scopeBracket = '['; + scopeCloseBracket = ']'; + goto case LayoutCode.EndScope; + case LayoutCode.ObjectScope: + case LayoutCode.ImmutableObjectScope: + case LayoutCode.Schema: + case LayoutCode.ImmutableSchema: + scopeBracket = '{'; + scopeCloseBracket = '}'; + goto case LayoutCode.EndScope; + + case LayoutCode.EndScope: + { + ctx.Builder.Append(scopeBracket); + int snapshot = ctx.Builder.Length; + r = reader.ReadScope(new ReaderStringContext(ctx.Builder, ctx.Settings, ctx.Indent + 1), RowReaderJsonExtensions.ToJson); + if (r != Result.Success) + { + return r; + } + + if (ctx.Builder.Length != snapshot) + { + ctx.Builder.Append(ctx.NewLine); + ctx.WriteIndent(); + } + + ctx.Builder.Append(scopeCloseBracket); + break; + } + + default: + { + Contract.Assert(false, $"Unknown type will be ignored: {reader.Type.LayoutCode}"); + break; + } + } + } + + return Result.Success; + } + + private readonly struct ReaderStringContext + { + public readonly int Indent; + public readonly StringBuilder Builder; + public readonly RowReaderJsonSettings Settings; + public readonly string Separator; + public readonly string NewLine; + + public ReaderStringContext(StringBuilder builder, RowReaderJsonSettings settings, int indent) + { + this.Settings = settings; + this.Separator = settings.IndentChars == null ? "" : " "; + this.NewLine = settings.IndentChars == null ? "" : "\n"; + this.Indent = indent; + this.Builder = builder; + } + + public void WriteIndent() + { + string indentChars = this.Settings.IndentChars ?? ""; + for (int i = 0; i < this.Indent; i++) + { + this.Builder.Append(indentChars); + } + } + } + } +} diff --git a/dotnet/src/HybridRow.Json/RowReaderJsonSettings.cs b/dotnet/src/HybridRow.Json/RowReaderJsonSettings.cs new file mode 100644 index 0000000..4955da9 --- /dev/null +++ b/dotnet/src/HybridRow.Json/RowReaderJsonSettings.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +#pragma warning disable CA1051 // Do not declare visible instance fields + +namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Json +{ + public readonly struct RowReaderJsonSettings + { + /// If non-null then child objects are indented by one copy of this string per level. + public readonly string IndentChars; + + /// The quote character to use. + /// May be or . + public readonly char QuoteChar; + + public RowReaderJsonSettings(string indentChars = " ", char quoteChar = '"') + { + this.IndentChars = indentChars; + this.QuoteChar = quoteChar; + } + } +} diff --git a/dotnet/src/HybridRow.Package/Microsoft.Azure.Cosmos.Serialization.HybridRow.Package.nuproj b/dotnet/src/HybridRow.Package/Microsoft.Azure.Cosmos.Serialization.HybridRow.Package.nuproj new file mode 100644 index 0000000..91fda2c --- /dev/null +++ b/dotnet/src/HybridRow.Package/Microsoft.Azure.Cosmos.Serialization.HybridRow.Package.nuproj @@ -0,0 +1,26 @@ + + + + {7047DD2A-14FA-445E-93B2-67EB98282C4D} + External + true + + + + $(OutputRootDir) + $(NugetPackProperties);ProductSemanticVersion=$(ProductSemanticVersion);VersionPrereleaseExtension=$(VersionPrereleaseExtension) + + + + false + {B7621117-AEF3-4E10-928D-533AE893F379} + Microsoft.Azure.Cosmos.Core + + + false + {490D42EE-1FEF-47CC-97E4-782A353B4D58} + Microsoft.Azure.Cosmos.Serialization.HybridRow + + + + diff --git a/dotnet/src/HybridRow.Package/Microsoft.Azure.Cosmos.Serialization.HybridRow.Package.nuspec b/dotnet/src/HybridRow.Package/Microsoft.Azure.Cosmos.Serialization.HybridRow.Package.nuspec new file mode 100644 index 0000000..b557e87 --- /dev/null +++ b/dotnet/src/HybridRow.Package/Microsoft.Azure.Cosmos.Serialization.HybridRow.Package.nuspec @@ -0,0 +1,29 @@ + + + + Microsoft.Azure.Cosmos.Serialization.HybridRow + 1.0.0-preview + Microsoft.Azure.Cosmos.Serialization.HybridRow + Microsoft + Microsoft + https://aka.ms/netcoregaeula + https://github.com/Azure/azure-cosmos-dotnet-v3 + http://go.microsoft.com/fwlink/?LinkID=288890 + false + Microsoft.Azure.Cosmos.Serialization.HybridRow + This package supports the Microsoft Azure Cosmos Client and is not intended to be used directly from your code. + Copyright © Microsoft Corporation + microsoft azure cosmos dotnetcore netcore netstandard + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/src/HybridRow.Tests.Perf/App.config b/dotnet/src/HybridRow.Tests.Perf/App.config new file mode 100644 index 0000000..02249c3 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/App.config @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/dotnet/src/HybridRow.Tests.Perf/BenchmarkSuiteBase.cs b/dotnet/src/HybridRow.Tests.Perf/BenchmarkSuiteBase.cs new file mode 100644 index 0000000..ba2740d --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/BenchmarkSuiteBase.cs @@ -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>, LayoutResolverNamespace)> LoadExpectedAsync(string expectedFile) + { + LayoutResolverNamespace resolver = this.DefaultResolver; + List> expected = new List>(); + using (Stream stm = new FileStream(expectedFile, FileMode.Open)) + { + // Read a RecordIO stream. + MemorySpanResizer resizer = new MemorySpanResizer(BenchmarkSuiteBase.InitialCapacity); + Result r = await stm.ReadRecordIOAsync( + record => + { + r = BenchmarkSuiteBase.LoadOneRow(record, resolver, out Dictionary 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> rows) + { + using (Stream stm = new FileStream(file, FileMode.Truncate)) + { + // Create a reusable, resizable buffer. + MemorySpanResizer resizer = new MemorySpanResizer(BenchmarkSuiteBase.InitialCapacity); + + // Write a RecordIO stream. + Result r = await stm.WriteRecordIOAsync( + new Segment("HybridRow.Tests.Perf Expected Results", sdl), + (long index, out ReadOnlyMemory 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 buffer, LayoutResolver resolver, out Dictionary 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); + } + } + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/BsonJsonModelRowGenerator.cs b/dotnet/src/HybridRow.Tests.Perf/BsonJsonModelRowGenerator.cs new file mode 100644 index 0000000..8342aa9 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/BsonJsonModelRowGenerator.cs @@ -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 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 x: + this.writer.WriteStartDocument(); + foreach ((Utf8String propPath, object propValue) in x) + { + this.JsonModelSwitch(propPath, propValue); + } + + this.writer.WriteEndDocument(); + return; + case List 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; + } + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/BsonReaderExtensions.cs b/dotnet/src/HybridRow.Tests.Perf/BsonReaderExtensions.cs new file mode 100644 index 0000000..c0b50d0 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/BsonReaderExtensions.cs @@ -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(); + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/BsonRowGenerator.cs b/dotnet/src/HybridRow.Tests.Perf/BsonRowGenerator.cs new file mode 100644 index 0000000..b5bdd68 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/BsonRowGenerator.cs @@ -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 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)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 items = (List)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)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)value) + { + this.DispatchTuple(typeArg, item); + } + + this.writer.WriteEndArray(); + } + + private void DispatchUDT(TypeArgument typeArg, object value) + { + this.writer.WriteStartDocument(); + + Dictionary dict = (Dictionary)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(); + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/CassandraProto/CassandraHotelSchema.cs b/dotnet/src/HybridRow.Tests.Perf/CassandraProto/CassandraHotelSchema.cs new file mode 100644 index 0000000..f8d73de --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/CassandraProto/CassandraHotelSchema.cs @@ -0,0 +1,1283 @@ +#pragma warning disable DontUseNamespaceAliases // Namespace Aliases should be avoided +#pragma warning disable NamespaceMatchesFolderStructure // Namespace Declarations must match folder structure +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: TestData/CassandraHotelSchema.proto +// +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf { + + /// Holder for reflection information generated from TestData/CassandraHotelSchema.proto + public static partial class CassandraHotelSchemaReflection { + + #region Descriptor + /// File descriptor for TestData/CassandraHotelSchema.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static CassandraHotelSchemaReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "CiNUZXN0RGF0YS9DYXNzYW5kcmFIb3RlbFNjaGVtYS5wcm90bxJITWljcm9z", + "b2Z0LkF6dXJlLkNvc21vcy5TZXJpYWxpemF0aW9uLkh5YnJpZFJvdy5UZXN0", + "cy5QZXJmLkNhc3NhbmRyYUhvdGVsGh5nb29nbGUvcHJvdG9idWYvd3JhcHBl", + "cnMucHJvdG8iYgoKUG9zdGFsQ29kZRIoCgN6aXAYASABKAsyGy5nb29nbGUu", + "cHJvdG9idWYuSW50MzJWYWx1ZRIqCgVwbHVzNBgCIAEoCzIbLmdvb2dsZS5w", + "cm90b2J1Zi5JbnQzMlZhbHVlIvsBCgdBZGRyZXNzEiwKBnN0cmVldBgBIAEo", + "CzIcLmdvb2dsZS5wcm90b2J1Zi5TdHJpbmdWYWx1ZRIqCgRjaXR5GAIgASgL", + "MhwuZ29vZ2xlLnByb3RvYnVmLlN0cmluZ1ZhbHVlEisKBXN0YXRlGAMgASgL", + "MhwuZ29vZ2xlLnByb3RvYnVmLlN0cmluZ1ZhbHVlEmkKC3Bvc3RhbF9jb2Rl", + "GAQgASgLMlQuTWljcm9zb2Z0LkF6dXJlLkNvc21vcy5TZXJpYWxpemF0aW9u", + "Lkh5YnJpZFJvdy5UZXN0cy5QZXJmLkNhc3NhbmRyYUhvdGVsLlBvc3RhbENv", + "ZGUi9QEKBkhvdGVscxIuCghob3RlbF9pZBgBIAEoCzIcLmdvb2dsZS5wcm90", + "b2J1Zi5TdHJpbmdWYWx1ZRIqCgRuYW1lGAIgASgLMhwuZ29vZ2xlLnByb3Rv", + "YnVmLlN0cmluZ1ZhbHVlEisKBXBob25lGAMgASgLMhwuZ29vZ2xlLnByb3Rv", + "YnVmLlN0cmluZ1ZhbHVlEmIKB2FkZHJlc3MYBCABKAsyUS5NaWNyb3NvZnQu", + "QXp1cmUuQ29zbW9zLlNlcmlhbGl6YXRpb24uSHlicmlkUm93LlRlc3RzLlBl", + "cmYuQ2Fzc2FuZHJhSG90ZWwuQWRkcmVzcyLeAQodQXZhaWxhYmxlX1Jvb21z", + "X0J5X0hvdGVsX0RhdGUSLgoIaG90ZWxfaWQYASABKAsyHC5nb29nbGUucHJv", + "dG9idWYuU3RyaW5nVmFsdWUSKQoEZGF0ZRgCIAEoCzIbLmdvb2dsZS5wcm90", + "b2J1Zi5JbnQ2NFZhbHVlEjAKC3Jvb21fbnVtYmVyGAMgASgLMhsuZ29vZ2xl", + "LnByb3RvYnVmLkludDMyVmFsdWUSMAoMaXNfYXZhaWxhYmxlGAQgASgLMhou", + "Z29vZ2xlLnByb3RvYnVmLkJvb2xWYWx1ZSKfBAoGR3Vlc3RzEi4KCGd1ZXN0", + "X2lkGAEgASgLMhwuZ29vZ2xlLnByb3RvYnVmLlN0cmluZ1ZhbHVlEjAKCmZp", + "cnN0X25hbWUYAiABKAsyHC5nb29nbGUucHJvdG9idWYuU3RyaW5nVmFsdWUS", + "LwoJbGFzdF9uYW1lGAMgASgLMhwuZ29vZ2xlLnByb3RvYnVmLlN0cmluZ1Zh", + "bHVlEisKBXRpdGxlGAQgASgLMhwuZ29vZ2xlLnByb3RvYnVmLlN0cmluZ1Zh", + "bHVlEg4KBmVtYWlscxgFIAMoCRIVCg1waG9uZV9udW1iZXJzGAYgAygJEnIK", + "CWFkZHJlc3NlcxgHIAMoCzJfLk1pY3Jvc29mdC5BenVyZS5Db3Ntb3MuU2Vy", + "aWFsaXphdGlvbi5IeWJyaWRSb3cuVGVzdHMuUGVyZi5DYXNzYW5kcmFIb3Rl", + "bC5HdWVzdHMuQWRkcmVzc2VzRW50cnkSNAoOY29uZmlybV9udW1iZXIYCCAB", + "KAsyHC5nb29nbGUucHJvdG9idWYuU3RyaW5nVmFsdWUagwEKDkFkZHJlc3Nl", + "c0VudHJ5EgsKA2tleRgBIAEoCRJgCgV2YWx1ZRgCIAEoCzJRLk1pY3Jvc29m", + "dC5BenVyZS5Db3Ntb3MuU2VyaWFsaXphdGlvbi5IeWJyaWRSb3cuVGVzdHMu", + "UGVyZi5DYXNzYW5kcmFIb3RlbC5BZGRyZXNzOgI4AUJUqgJRTWljcm9zb2Z0", + "LkF6dXJlLkNvc21vcy5TZXJpYWxpemF0aW9uLkh5YnJpZFJvdy5UZXN0cy5Q", + "ZXJmLkNhc3NhbmRyYUhvdGVsLlByb3RvYnVmYgZwcm90bzM=")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, }, + new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.PostalCode), global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.PostalCode.Parser, new[]{ "Zip", "Plus4" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Address), global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Address.Parser, new[]{ "Street", "City", "State", "PostalCode" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Hotels), global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Hotels.Parser, new[]{ "HotelId", "Name", "Phone", "Address" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Available_Rooms_By_Hotel_Date), global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Available_Rooms_By_Hotel_Date.Parser, new[]{ "HotelId", "Date", "RoomNumber", "IsAvailable" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Guests), global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Guests.Parser, new[]{ "GuestId", "FirstName", "LastName", "Title", "Emails", "PhoneNumbers", "Addresses", "ConfirmNumber" }, null, null, new pbr::GeneratedClrTypeInfo[] { null, }) + })); + } + #endregion + + } + #region Messages + public sealed partial class PostalCode : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new PostalCode()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.CassandraHotelSchemaReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public PostalCode() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public PostalCode(PostalCode other) : this() { + Zip = other.Zip; + Plus4 = other.Plus4; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public PostalCode Clone() { + return new PostalCode(this); + } + + /// Field number for the "zip" field. + public const int ZipFieldNumber = 1; + private static readonly pb::FieldCodec _single_zip_codec = pb::FieldCodec.ForStructWrapper(10); + private int? zip_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int? Zip { + get { return zip_; } + set { + zip_ = value; + } + } + + /// Field number for the "plus4" field. + public const int Plus4FieldNumber = 2; + private static readonly pb::FieldCodec _single_plus4_codec = pb::FieldCodec.ForStructWrapper(18); + private int? plus4_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int? Plus4 { + get { return plus4_; } + set { + plus4_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as PostalCode); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(PostalCode other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Zip != other.Zip) return false; + if (Plus4 != other.Plus4) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (zip_ != null) hash ^= Zip.GetHashCode(); + if (plus4_ != null) hash ^= Plus4.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (zip_ != null) { + _single_zip_codec.WriteTagAndValue(output, Zip); + } + if (plus4_ != null) { + _single_plus4_codec.WriteTagAndValue(output, Plus4); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (zip_ != null) { + size += _single_zip_codec.CalculateSizeWithTag(Zip); + } + if (plus4_ != null) { + size += _single_plus4_codec.CalculateSizeWithTag(Plus4); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(PostalCode other) { + if (other == null) { + return; + } + if (other.zip_ != null) { + if (zip_ == null || other.Zip != 0) { + Zip = other.Zip; + } + } + if (other.plus4_ != null) { + if (plus4_ == null || other.Plus4 != 0) { + Plus4 = other.Plus4; + } + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + int? value = _single_zip_codec.Read(input); + if (zip_ == null || value != 0) { + Zip = value; + } + break; + } + case 18: { + int? value = _single_plus4_codec.Read(input); + if (plus4_ == null || value != 0) { + Plus4 = value; + } + break; + } + } + } + } + + } + + public sealed partial class Address : pb::IMessage
{ + private static readonly pb::MessageParser
_parser = new pb::MessageParser
(() => new Address()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser
Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.CassandraHotelSchemaReflection.Descriptor.MessageTypes[1]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Address() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Address(Address other) : this() { + Street = other.Street; + City = other.City; + State = other.State; + PostalCode = other.postalCode_ != null ? other.PostalCode.Clone() : null; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Address Clone() { + return new Address(this); + } + + /// Field number for the "street" field. + public const int StreetFieldNumber = 1; + private static readonly pb::FieldCodec _single_street_codec = pb::FieldCodec.ForClassWrapper(10); + private string street_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Street { + get { return street_; } + set { + street_ = value; + } + } + + /// Field number for the "city" field. + public const int CityFieldNumber = 2; + private static readonly pb::FieldCodec _single_city_codec = pb::FieldCodec.ForClassWrapper(18); + private string city_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string City { + get { return city_; } + set { + city_ = value; + } + } + + /// Field number for the "state" field. + public const int StateFieldNumber = 3; + private static readonly pb::FieldCodec _single_state_codec = pb::FieldCodec.ForClassWrapper(26); + private string state_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string State { + get { return state_; } + set { + state_ = value; + } + } + + /// Field number for the "postal_code" field. + public const int PostalCodeFieldNumber = 4; + private global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.PostalCode postalCode_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.PostalCode PostalCode { + get { return postalCode_; } + set { + postalCode_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Address); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Address other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Street != other.Street) return false; + if (City != other.City) return false; + if (State != other.State) return false; + if (!object.Equals(PostalCode, other.PostalCode)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (street_ != null) hash ^= Street.GetHashCode(); + if (city_ != null) hash ^= City.GetHashCode(); + if (state_ != null) hash ^= State.GetHashCode(); + if (postalCode_ != null) hash ^= PostalCode.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (street_ != null) { + _single_street_codec.WriteTagAndValue(output, Street); + } + if (city_ != null) { + _single_city_codec.WriteTagAndValue(output, City); + } + if (state_ != null) { + _single_state_codec.WriteTagAndValue(output, State); + } + if (postalCode_ != null) { + output.WriteRawTag(34); + output.WriteMessage(PostalCode); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (street_ != null) { + size += _single_street_codec.CalculateSizeWithTag(Street); + } + if (city_ != null) { + size += _single_city_codec.CalculateSizeWithTag(City); + } + if (state_ != null) { + size += _single_state_codec.CalculateSizeWithTag(State); + } + if (postalCode_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(PostalCode); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Address other) { + if (other == null) { + return; + } + if (other.street_ != null) { + if (street_ == null || other.Street != "") { + Street = other.Street; + } + } + if (other.city_ != null) { + if (city_ == null || other.City != "") { + City = other.City; + } + } + if (other.state_ != null) { + if (state_ == null || other.State != "") { + State = other.State; + } + } + if (other.postalCode_ != null) { + if (postalCode_ == null) { + postalCode_ = new global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.PostalCode(); + } + PostalCode.MergeFrom(other.PostalCode); + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + string value = _single_street_codec.Read(input); + if (street_ == null || value != "") { + Street = value; + } + break; + } + case 18: { + string value = _single_city_codec.Read(input); + if (city_ == null || value != "") { + City = value; + } + break; + } + case 26: { + string value = _single_state_codec.Read(input); + if (state_ == null || value != "") { + State = value; + } + break; + } + case 34: { + if (postalCode_ == null) { + postalCode_ = new global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.PostalCode(); + } + input.ReadMessage(postalCode_); + break; + } + } + } + } + + } + + public sealed partial class Hotels : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Hotels()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.CassandraHotelSchemaReflection.Descriptor.MessageTypes[2]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Hotels() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Hotels(Hotels other) : this() { + HotelId = other.HotelId; + Name = other.Name; + Phone = other.Phone; + Address = other.address_ != null ? other.Address.Clone() : null; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Hotels Clone() { + return new Hotels(this); + } + + /// Field number for the "hotel_id" field. + public const int HotelIdFieldNumber = 1; + private static readonly pb::FieldCodec _single_hotelId_codec = pb::FieldCodec.ForClassWrapper(10); + private string hotelId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string HotelId { + get { return hotelId_; } + set { + hotelId_ = value; + } + } + + /// Field number for the "name" field. + public const int NameFieldNumber = 2; + private static readonly pb::FieldCodec _single_name_codec = pb::FieldCodec.ForClassWrapper(18); + private string name_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Name { + get { return name_; } + set { + name_ = value; + } + } + + /// Field number for the "phone" field. + public const int PhoneFieldNumber = 3; + private static readonly pb::FieldCodec _single_phone_codec = pb::FieldCodec.ForClassWrapper(26); + private string phone_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Phone { + get { return phone_; } + set { + phone_ = value; + } + } + + /// Field number for the "address" field. + public const int AddressFieldNumber = 4; + private global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Address address_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Address Address { + get { return address_; } + set { + address_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Hotels); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Hotels other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (HotelId != other.HotelId) return false; + if (Name != other.Name) return false; + if (Phone != other.Phone) return false; + if (!object.Equals(Address, other.Address)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (hotelId_ != null) hash ^= HotelId.GetHashCode(); + if (name_ != null) hash ^= Name.GetHashCode(); + if (phone_ != null) hash ^= Phone.GetHashCode(); + if (address_ != null) hash ^= Address.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (hotelId_ != null) { + _single_hotelId_codec.WriteTagAndValue(output, HotelId); + } + if (name_ != null) { + _single_name_codec.WriteTagAndValue(output, Name); + } + if (phone_ != null) { + _single_phone_codec.WriteTagAndValue(output, Phone); + } + if (address_ != null) { + output.WriteRawTag(34); + output.WriteMessage(Address); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (hotelId_ != null) { + size += _single_hotelId_codec.CalculateSizeWithTag(HotelId); + } + if (name_ != null) { + size += _single_name_codec.CalculateSizeWithTag(Name); + } + if (phone_ != null) { + size += _single_phone_codec.CalculateSizeWithTag(Phone); + } + if (address_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Address); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Hotels other) { + if (other == null) { + return; + } + if (other.hotelId_ != null) { + if (hotelId_ == null || other.HotelId != "") { + HotelId = other.HotelId; + } + } + if (other.name_ != null) { + if (name_ == null || other.Name != "") { + Name = other.Name; + } + } + if (other.phone_ != null) { + if (phone_ == null || other.Phone != "") { + Phone = other.Phone; + } + } + if (other.address_ != null) { + if (address_ == null) { + address_ = new global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Address(); + } + Address.MergeFrom(other.Address); + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + string value = _single_hotelId_codec.Read(input); + if (hotelId_ == null || value != "") { + HotelId = value; + } + break; + } + case 18: { + string value = _single_name_codec.Read(input); + if (name_ == null || value != "") { + Name = value; + } + break; + } + case 26: { + string value = _single_phone_codec.Read(input); + if (phone_ == null || value != "") { + Phone = value; + } + break; + } + case 34: { + if (address_ == null) { + address_ = new global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Address(); + } + input.ReadMessage(address_); + break; + } + } + } + } + + } + + public sealed partial class Available_Rooms_By_Hotel_Date : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Available_Rooms_By_Hotel_Date()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.CassandraHotelSchemaReflection.Descriptor.MessageTypes[3]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Available_Rooms_By_Hotel_Date() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Available_Rooms_By_Hotel_Date(Available_Rooms_By_Hotel_Date other) : this() { + HotelId = other.HotelId; + Date = other.Date; + RoomNumber = other.RoomNumber; + IsAvailable = other.IsAvailable; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Available_Rooms_By_Hotel_Date Clone() { + return new Available_Rooms_By_Hotel_Date(this); + } + + /// Field number for the "hotel_id" field. + public const int HotelIdFieldNumber = 1; + private static readonly pb::FieldCodec _single_hotelId_codec = pb::FieldCodec.ForClassWrapper(10); + private string hotelId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string HotelId { + get { return hotelId_; } + set { + hotelId_ = value; + } + } + + /// Field number for the "date" field. + public const int DateFieldNumber = 2; + private static readonly pb::FieldCodec _single_date_codec = pb::FieldCodec.ForStructWrapper(18); + private long? date_; + /// + /// datetime + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public long? Date { + get { return date_; } + set { + date_ = value; + } + } + + /// Field number for the "room_number" field. + public const int RoomNumberFieldNumber = 3; + private static readonly pb::FieldCodec _single_roomNumber_codec = pb::FieldCodec.ForStructWrapper(26); + private int? roomNumber_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int? RoomNumber { + get { return roomNumber_; } + set { + roomNumber_ = value; + } + } + + /// Field number for the "is_available" field. + public const int IsAvailableFieldNumber = 4; + private static readonly pb::FieldCodec _single_isAvailable_codec = pb::FieldCodec.ForStructWrapper(34); + private bool? isAvailable_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool? IsAvailable { + get { return isAvailable_; } + set { + isAvailable_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Available_Rooms_By_Hotel_Date); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Available_Rooms_By_Hotel_Date other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (HotelId != other.HotelId) return false; + if (Date != other.Date) return false; + if (RoomNumber != other.RoomNumber) return false; + if (IsAvailable != other.IsAvailable) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (hotelId_ != null) hash ^= HotelId.GetHashCode(); + if (date_ != null) hash ^= Date.GetHashCode(); + if (roomNumber_ != null) hash ^= RoomNumber.GetHashCode(); + if (isAvailable_ != null) hash ^= IsAvailable.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (hotelId_ != null) { + _single_hotelId_codec.WriteTagAndValue(output, HotelId); + } + if (date_ != null) { + _single_date_codec.WriteTagAndValue(output, Date); + } + if (roomNumber_ != null) { + _single_roomNumber_codec.WriteTagAndValue(output, RoomNumber); + } + if (isAvailable_ != null) { + _single_isAvailable_codec.WriteTagAndValue(output, IsAvailable); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (hotelId_ != null) { + size += _single_hotelId_codec.CalculateSizeWithTag(HotelId); + } + if (date_ != null) { + size += _single_date_codec.CalculateSizeWithTag(Date); + } + if (roomNumber_ != null) { + size += _single_roomNumber_codec.CalculateSizeWithTag(RoomNumber); + } + if (isAvailable_ != null) { + size += _single_isAvailable_codec.CalculateSizeWithTag(IsAvailable); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Available_Rooms_By_Hotel_Date other) { + if (other == null) { + return; + } + if (other.hotelId_ != null) { + if (hotelId_ == null || other.HotelId != "") { + HotelId = other.HotelId; + } + } + if (other.date_ != null) { + if (date_ == null || other.Date != 0L) { + Date = other.Date; + } + } + if (other.roomNumber_ != null) { + if (roomNumber_ == null || other.RoomNumber != 0) { + RoomNumber = other.RoomNumber; + } + } + if (other.isAvailable_ != null) { + if (isAvailable_ == null || other.IsAvailable != false) { + IsAvailable = other.IsAvailable; + } + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + string value = _single_hotelId_codec.Read(input); + if (hotelId_ == null || value != "") { + HotelId = value; + } + break; + } + case 18: { + long? value = _single_date_codec.Read(input); + if (date_ == null || value != 0L) { + Date = value; + } + break; + } + case 26: { + int? value = _single_roomNumber_codec.Read(input); + if (roomNumber_ == null || value != 0) { + RoomNumber = value; + } + break; + } + case 34: { + bool? value = _single_isAvailable_codec.Read(input); + if (isAvailable_ == null || value != false) { + IsAvailable = value; + } + break; + } + } + } + } + + } + + public sealed partial class Guests : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Guests()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.CassandraHotelSchemaReflection.Descriptor.MessageTypes[4]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Guests() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Guests(Guests other) : this() { + GuestId = other.GuestId; + FirstName = other.FirstName; + LastName = other.LastName; + Title = other.Title; + emails_ = other.emails_.Clone(); + phoneNumbers_ = other.phoneNumbers_.Clone(); + addresses_ = other.addresses_.Clone(); + ConfirmNumber = other.ConfirmNumber; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public Guests Clone() { + return new Guests(this); + } + + /// Field number for the "guest_id" field. + public const int GuestIdFieldNumber = 1; + private static readonly pb::FieldCodec _single_guestId_codec = pb::FieldCodec.ForClassWrapper(10); + private string guestId_; + /// + /// guid + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string GuestId { + get { return guestId_; } + set { + guestId_ = value; + } + } + + /// Field number for the "first_name" field. + public const int FirstNameFieldNumber = 2; + private static readonly pb::FieldCodec _single_firstName_codec = pb::FieldCodec.ForClassWrapper(18); + private string firstName_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string FirstName { + get { return firstName_; } + set { + firstName_ = value; + } + } + + /// Field number for the "last_name" field. + public const int LastNameFieldNumber = 3; + private static readonly pb::FieldCodec _single_lastName_codec = pb::FieldCodec.ForClassWrapper(26); + private string lastName_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string LastName { + get { return lastName_; } + set { + lastName_ = value; + } + } + + /// Field number for the "title" field. + public const int TitleFieldNumber = 4; + private static readonly pb::FieldCodec _single_title_codec = pb::FieldCodec.ForClassWrapper(34); + private string title_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string Title { + get { return title_; } + set { + title_ = value; + } + } + + /// Field number for the "emails" field. + public const int EmailsFieldNumber = 5; + private static readonly pb::FieldCodec _repeated_emails_codec + = pb::FieldCodec.ForString(42); + private readonly pbc::RepeatedField emails_ = new pbc::RepeatedField(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public pbc::RepeatedField Emails { + get { return emails_; } + } + + /// Field number for the "phone_numbers" field. + public const int PhoneNumbersFieldNumber = 6; + private static readonly pb::FieldCodec _repeated_phoneNumbers_codec + = pb::FieldCodec.ForString(50); + private readonly pbc::RepeatedField phoneNumbers_ = new pbc::RepeatedField(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public pbc::RepeatedField PhoneNumbers { + get { return phoneNumbers_; } + } + + /// Field number for the "addresses" field. + public const int AddressesFieldNumber = 7; + private static readonly pbc::MapField.Codec _map_addresses_codec + = new pbc::MapField.Codec(pb::FieldCodec.ForString(10), pb::FieldCodec.ForMessage(18, global::Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.CassandraHotel.Protobuf.Address.Parser), 58); + private readonly pbc::MapField addresses_ = new pbc::MapField(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public pbc::MapField Addresses { + get { return addresses_; } + } + + /// Field number for the "confirm_number" field. + public const int ConfirmNumberFieldNumber = 8; + private static readonly pb::FieldCodec _single_confirmNumber_codec = pb::FieldCodec.ForClassWrapper(66); + private string confirmNumber_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string ConfirmNumber { + get { return confirmNumber_; } + set { + confirmNumber_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as Guests); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(Guests other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (GuestId != other.GuestId) return false; + if (FirstName != other.FirstName) return false; + if (LastName != other.LastName) return false; + if (Title != other.Title) return false; + if(!emails_.Equals(other.emails_)) return false; + if(!phoneNumbers_.Equals(other.phoneNumbers_)) return false; + if (!Addresses.Equals(other.Addresses)) return false; + if (ConfirmNumber != other.ConfirmNumber) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (guestId_ != null) hash ^= GuestId.GetHashCode(); + if (firstName_ != null) hash ^= FirstName.GetHashCode(); + if (lastName_ != null) hash ^= LastName.GetHashCode(); + if (title_ != null) hash ^= Title.GetHashCode(); + hash ^= emails_.GetHashCode(); + hash ^= phoneNumbers_.GetHashCode(); + hash ^= Addresses.GetHashCode(); + if (confirmNumber_ != null) hash ^= ConfirmNumber.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (guestId_ != null) { + _single_guestId_codec.WriteTagAndValue(output, GuestId); + } + if (firstName_ != null) { + _single_firstName_codec.WriteTagAndValue(output, FirstName); + } + if (lastName_ != null) { + _single_lastName_codec.WriteTagAndValue(output, LastName); + } + if (title_ != null) { + _single_title_codec.WriteTagAndValue(output, Title); + } + emails_.WriteTo(output, _repeated_emails_codec); + phoneNumbers_.WriteTo(output, _repeated_phoneNumbers_codec); + addresses_.WriteTo(output, _map_addresses_codec); + if (confirmNumber_ != null) { + _single_confirmNumber_codec.WriteTagAndValue(output, ConfirmNumber); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (guestId_ != null) { + size += _single_guestId_codec.CalculateSizeWithTag(GuestId); + } + if (firstName_ != null) { + size += _single_firstName_codec.CalculateSizeWithTag(FirstName); + } + if (lastName_ != null) { + size += _single_lastName_codec.CalculateSizeWithTag(LastName); + } + if (title_ != null) { + size += _single_title_codec.CalculateSizeWithTag(Title); + } + size += emails_.CalculateSize(_repeated_emails_codec); + size += phoneNumbers_.CalculateSize(_repeated_phoneNumbers_codec); + size += addresses_.CalculateSize(_map_addresses_codec); + if (confirmNumber_ != null) { + size += _single_confirmNumber_codec.CalculateSizeWithTag(ConfirmNumber); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(Guests other) { + if (other == null) { + return; + } + if (other.guestId_ != null) { + if (guestId_ == null || other.GuestId != "") { + GuestId = other.GuestId; + } + } + if (other.firstName_ != null) { + if (firstName_ == null || other.FirstName != "") { + FirstName = other.FirstName; + } + } + if (other.lastName_ != null) { + if (lastName_ == null || other.LastName != "") { + LastName = other.LastName; + } + } + if (other.title_ != null) { + if (title_ == null || other.Title != "") { + Title = other.Title; + } + } + emails_.Add(other.emails_); + phoneNumbers_.Add(other.phoneNumbers_); + addresses_.Add(other.addresses_); + if (other.confirmNumber_ != null) { + if (confirmNumber_ == null || other.ConfirmNumber != "") { + ConfirmNumber = other.ConfirmNumber; + } + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + string value = _single_guestId_codec.Read(input); + if (guestId_ == null || value != "") { + GuestId = value; + } + break; + } + case 18: { + string value = _single_firstName_codec.Read(input); + if (firstName_ == null || value != "") { + FirstName = value; + } + break; + } + case 26: { + string value = _single_lastName_codec.Read(input); + if (lastName_ == null || value != "") { + LastName = value; + } + break; + } + case 34: { + string value = _single_title_codec.Read(input); + if (title_ == null || value != "") { + Title = value; + } + break; + } + case 42: { + emails_.AddEntriesFrom(input, _repeated_emails_codec); + break; + } + case 50: { + phoneNumbers_.AddEntriesFrom(input, _repeated_phoneNumbers_codec); + break; + } + case 58: { + addresses_.AddEntriesFrom(input, _map_addresses_codec); + break; + } + case 66: { + string value = _single_confirmNumber_codec.Read(input); + if (confirmNumber_ == null || value != "") { + ConfirmNumber = value; + } + break; + } + } + } + } + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/dotnet/src/HybridRow.Tests.Perf/CodeGenMicroBenchmarkSuite.cs b/dotnet/src/HybridRow.Tests.Perf/CodeGenMicroBenchmarkSuite.cs new file mode 100644 index 0000000..670045a --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/CodeGenMicroBenchmarkSuite.cs @@ -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; + + /// + /// 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 . + /// + /// The tests here differ from 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. + /// + /// + [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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> expected) + { + BenchmarkContext context = new BenchmarkContext + { + ProtobufWriter = new ProtobufRowGenerator(schemaName, BenchmarkSuiteBase.InitialCapacity) + }; + + MicroBenchmarkSuiteBase.Benchmark( + "CodeGen", + "Write", + dataSetName, + "Protobuf", + innerLoopIterations, + ref context, + (ref BenchmarkContext ctx, Dictionary tableValue) => { ctx.ProtobufWriter.WriteBuffer(tableValue); }, + (ref BenchmarkContext ctx, Dictionary tableValue) => ctx.ProtobufWriter.Length, + expected); + } + + private static void ProtobufReadBenchmark( + string schemaName, + string dataSetName, + int innerLoopIterations, + List> expected) + { + // Serialize input data to sequence of byte buffers. + List expectedSerialized = new List(expected.Count); + BenchmarkContext context = new BenchmarkContext + { + ProtobufWriter = new ProtobufRowGenerator(schemaName, BenchmarkSuiteBase.InitialCapacity) + }; + + foreach (Dictionary 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> 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 tableValue) => + { + ctx.CodeGenWriter.Reset(); + + Result r = ctx.CodeGenWriter.WriteBuffer(tableValue); + ResultAssert.IsSuccess(r); + }, + (ref BenchmarkContext ctx, Dictionary tableValue) => ctx.CodeGenWriter.Length, + expected); + } + + private static void CodeGenReadBenchmark( + LayoutResolverNamespace resolver, + string schemaName, + string dataSetName, + int innerLoopIterations, + List> expected) + { + // Serialize input data to sequence of byte buffers. + List expectedSerialized = new List(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 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); + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/CodeGenRowGenerator.cs b/dotnet/src/HybridRow.Tests.Perf/CodeGenRowGenerator.cs new file mode 100644 index 0000000..40923ad --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/CodeGenRowGenerator.cs @@ -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 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 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 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> 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 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)value, + (ref RowBuffer row2, ref RowCursor childScope, List 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)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)value), + (ref RowBuffer row2, ref RowCursor childScope, (GuestsHybridRowSerializer _this, List value) ctx) => + { + foreach (object item in ctx.value) + { + Result r2 = LayoutType.TypedTuple.WriteScope( + ref row2, + ref childScope, + ctx._this.addresses.TypeArgs, + (ctx._this, (List)item), + (ref RowBuffer row3, ref RowCursor tupleScope, (GuestsHybridRowSerializer _this, List 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)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 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)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 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 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 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)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; + } + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/GenerateBenchmarkSuite.cs b/dotnet/src/HybridRow.Tests.Perf/GenerateBenchmarkSuite.cs new file mode 100644 index 0000000..fb34dd7 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/GenerateBenchmarkSuite.cs @@ -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> 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> rows = new List>(outerLoopIterations); + for (int iteration = 0; iteration != outerLoopIterations; iteration += outerLoopIterations < 0 ? 0 : 1) + { + TypeArgument typeArg = new TypeArgument(LayoutType.UDT, new TypeArgumentList(layout.SchemaId)); + Dictionary rowValue = (Dictionary)valueGenerator.GenerateLayoutType(resolver, typeArg); + rows.Add(rowValue); + } + + return rows; + } + + private async Task GenerateBenchmarkAsync(string schemaName, int outerLoopIterations, string expectedFile) + { + (List> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile); + List> + 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."); + } + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/GenerateProtoBuf.cmd b/dotnet/src/HybridRow.Tests.Perf/GenerateProtoBuf.cmd new file mode 100644 index 0000000..7e2cdcd --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/GenerateProtoBuf.cmd @@ -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 diff --git a/dotnet/src/HybridRow.Tests.Perf/HybridRowPerf.csv b/dotnet/src/HybridRow.Tests.Perf/HybridRowPerf.csv new file mode 100644 index 0000000..41041e2 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/HybridRowPerf.csv @@ -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 diff --git a/dotnet/src/HybridRow.Tests.Perf/JsonModelRowGenerator.cs b/dotnet/src/HybridRow.Tests.Perf/JsonModelRowGenerator.cs new file mode 100644 index 0000000..0c99a59 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/JsonModelRowGenerator.cs @@ -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 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 value) + { + return RowWriter.WriteBuffer( + ref this.row, + value, + (ref RowWriter writer, TypeArgument typeArg, Dictionary 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 x: + return writer.WriteBinary(path, x.Span); + case Dictionary x: + return writer.WriteScope( + path, + new TypeArgument(LayoutType.Object), + x, + (ref RowWriter writer2, TypeArgument typeArg, Dictionary 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 x: + return writer.WriteScope( + path, + new TypeArgument(LayoutType.Array), + x, + (ref RowWriter writer2, TypeArgument typeArg, List 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; + } + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/Measurements.cs b/dotnet/src/HybridRow.Tests.Perf/Measurements.cs new file mode 100644 index 0000000..e0fb8b3 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/Measurements.cs @@ -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); + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/MicroBenchmarkSuiteBase.cs b/dotnet/src/HybridRow.Tests.Perf/MicroBenchmarkSuiteBase.cs new file mode 100644 index 0000000..56f69cd --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/MicroBenchmarkSuiteBase.cs @@ -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( + string model, + string operation, + string schema, + string api, + int innerLoopIterations, + ref BenchmarkContext context, + BenchmarkBody loopBody, + BenchmarkMeasure measure, + List 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( + int innerLoopIterations, + TValue tableValue, + ref BenchmarkContext context, + BenchmarkBody 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(ref BenchmarkContext context, TValue value); + + private protected delegate long BenchmarkMeasure(ref BenchmarkContext context, TValue value); + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.csproj b/dotnet/src/HybridRow.Tests.Perf/Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.csproj new file mode 100644 index 0000000..34e4c7d --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf.csproj @@ -0,0 +1,52 @@ + + + + true + true + {26A73F4A-AC9E-46AF-9445-286EE9EDA3EE} + Library + Test + Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf + Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf + netcoreapp2.2 + True + AnyCPU + + + + MsTest_Latest + FrameworkCore20 + X64 + $(OutDir) + + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + \ No newline at end of file diff --git a/dotnet/src/HybridRow.Tests.Perf/Properties/AssemblyInfo.cs b/dotnet/src/HybridRow.Tests.Perf/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9421396 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// + +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")] diff --git a/dotnet/src/HybridRow.Tests.Perf/ProtobufRowGenerator.cs b/dotnet/src/HybridRow.Tests.Perf/ProtobufRowGenerator.cs new file mode 100644 index 0000000..e6aa1ea --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/ProtobufRowGenerator.cs @@ -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 active; + + public ProtobufRowGenerator(string schemaName, int capacity) + { + this.schemaName = schemaName; + this.buffer = new byte[capacity]; + this.active = this.buffer; + } + + public void WriteBuffer(Dictionary 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 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)value); + } + + break; + + case "phone_numbers": + if (value != null) + { + ProtobufRowGenerator.PopulateStringList(room.PhoneNumbers, (List)value); + } + + break; + + case "addresses": + if (value != null) + { + ProtobufRowGenerator.PopulateStringAddressMap(room.Addresses, (List)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 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)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 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 field, List list) + { + foreach (object item in list) + { + field.Add(((Utf8String)item).ToString()); + } + } + + private static void PopulateStringAddressMap(MapField field, List list) + { + foreach (object item in list) + { + List tuple = (List)item; + string key = ((Utf8String)tuple[0]).ToString(); + pb.Address value = ProtobufRowGenerator.MakeAddress((Dictionary)tuple[1]); + field.Add(key, value); + } + } + + private static pb.PostalCode MakePostalCode(Dictionary 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 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)value); + break; + + default: + Assert.Fail("should never happen"); + break; + } + } + + return address; + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/ReaderBenchmark.cs b/dotnet/src/HybridRow.Tests.Perf/ReaderBenchmark.cs new file mode 100644 index 0000000..108a82e --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/ReaderBenchmark.cs @@ -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 resizer = new MemorySpanResizer(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 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 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 tasks; + private readonly CancellationTokenSource cancel; + + // Creates a new instance with the specified degree of parallelism. + public SingleThreadedTaskScheduler() + { + this.tasks = new ConcurrentQueue(); + 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 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)); + } + } + } + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/RowReaderExtensions.cs b/dotnet/src/HybridRow.Tests.Perf/RowReaderExtensions.cs new file mode 100644 index 0000000..9a34447 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/RowReaderExtensions.cs @@ -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; + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/SchematizedMicroBenchmarkSuite.cs b/dotnet/src/HybridRow.Tests.Perf/SchematizedMicroBenchmarkSuite.cs new file mode 100644 index 0000000..e372b28 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/SchematizedMicroBenchmarkSuite.cs @@ -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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> expected, LayoutResolverNamespace resolver) = await this.LoadExpectedAsync(expectedFile); + SchematizedMicroBenchmarkSuite.StreamingReadBenchmark(resolver, "Guests", "Guests", 1000, expected); + } + + private static void JsonWriteBenchmark( + string dataSetName, + int innerLoopIterations, + List> 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 tableValue) => + { + jsonStream.SetLength(0); + jsonSerializer.Serialize(jsonWriter, tableValue); + jsonWriter.Flush(); + }, + (ref BenchmarkContext _, Dictionary value) => jsonStream.Length, + expected); + } + } + + private static void JsonReadBenchmark(string dataSetName, int innerLoopIterations, List> expected) + { + // Serialize input data to sequence of byte buffers. + List expectedSerialized = new List(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 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> 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 tableValue) => + { + writer.Reset(); + writer.WriteBuffer(tableValue); + }, + (ref BenchmarkContext _, Dictionary tableValue) => writer.Length, + expected); + } + } + + private static void BsonReadBenchmark( + LayoutResolverNamespace resolver, + string schemaName, + string dataSetName, + int innerLoopIterations, + List> expected) + { + // Serialize input data to sequence of byte buffers. + List expectedSerialized = new List(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 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> 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 dict) => + { + ctx.PatchWriter.Reset(); + Result r = ctx.PatchWriter.DispatchLayout(layout, dict); + ResultAssert.IsSuccess(r); + }, + (ref BenchmarkContext ctx, Dictionary _) => ctx.PatchWriter.Length, + expected); + } + + private static void LayoutReadBenchmark( + LayoutResolverNamespace resolver, + string schemaName, + string dataSetName, + int innerLoopIterations, + List> expected) + { + // Serialize input data to sequence of byte buffers. + List expectedSerialized = new List(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 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> 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 tableValue) => + { + ctx.StreamingWriter.Reset(); + + Result r = ctx.StreamingWriter.WriteBuffer(tableValue); + ResultAssert.IsSuccess(r); + }, + (ref BenchmarkContext ctx, Dictionary _) => ctx.StreamingWriter.Length, + expected); + } + + private static void StreamingReadBenchmark( + LayoutResolverNamespace resolver, + string schemaName, + string dataSetName, + int innerLoopIterations, + List> expected) + { + // Serialize input data to sequence of byte buffers. + List expectedSerialized = new List(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 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); + } + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/TestData.cs b/dotnet/src/HybridRow.Tests.Perf/TestData.cs new file mode 100644 index 0000000..9e65909 --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/TestData.cs @@ -0,0 +1,22 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos.Serialization.HybridRow.Tests.Perf +{ + /// + /// Names of assets in the TestData folder. + /// + internal class TestData + { + /// + /// The folder to which TestData assets should be copied during deployment. + /// + 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"; + } +} diff --git a/dotnet/src/HybridRow.Tests.Perf/TestData/CassandraHotelSchema.json b/dotnet/src/HybridRow.Tests.Perf/TestData/CassandraHotelSchema.json new file mode 100644 index 0000000..c27a3bc --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/TestData/CassandraHotelSchema.json @@ -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" } } + ] + } + ] +} diff --git a/dotnet/src/HybridRow.Tests.Perf/TestData/CassandraHotelSchema.proto b/dotnet/src/HybridRow.Tests.Perf/TestData/CassandraHotelSchema.proto new file mode 100644 index 0000000..abb1b3e --- /dev/null +++ b/dotnet/src/HybridRow.Tests.Perf/TestData/CassandraHotelSchema.proto @@ -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 addresses = 7; + google.protobuf.StringValue confirm_number = 8; +} diff --git a/dotnet/src/HybridRow.Tests.Perf/TestData/CombinedScriptsData.hr b/dotnet/src/HybridRow.Tests.Perf/TestData/CombinedScriptsData.hr new file mode 100644 index 0000000000000000000000000000000000000000..cf62dc77fe53ee5afb84963f56a1d3e57e717138 GIT binary patch literal 827289 zcmeFa3DhP@RVLgBO*aU*(4x3t0gm8|&|TGARSyHinvoG1kr^2o5g9p*Saugp(cM*T zS3%q0iYU&wf;xk{A})*y>LBy~Zn)qm`j6wNkcY@!2_V(K^Ui--NcYF+N zxcbAdyY`-QmoA;X^W5{j_ezu+mMZE(%8M2wgNSq~YjI3n>+;x?uFR513Q-xkU_ms# zF6un5XgYE2+1oF}Q(t;YzvDIJ;yw4kLtS_E-RCdd^^6x^zI^X(*I$4C{r5lnMQ3k6 z_h}a|-u3L;FWz%~d*;lU>+ijI>GGxP6Z|8;!dE}4IDgl{h3Gf|HMyRbMK2UUO0E@x<{Tob>nr9yy@m!_;pube&F77Pq_EK^DjGl z`P`nuUibLh?>h&7z2k+K&);+I$)|2OdBd}AIQ^^}PBkZQy6x1hx7~Epvu?Qcwi|A^ z?(r|Z_m2OAfA88y-*NHw{EkQMKhV{e&cFQJi}wE7tMuyMxZ#GI9<;vy1kd|~Uw!ho zkFBqAG}R%Zf%QG1I`_b3Flc@D!iDn}?p#@*+}mf(0$J6SERQm?Udqms)?3x(hT&2p zU6ewG%;MB$T@=(ypIX;t8S@c)Q-v7)*aGLsWzYQPEZtd=f}ecw&$SPV^r;`z|7~5Q zH$EuRcf&8g_^#Ld%dtos3SV6+iUtmqoW19SId?yZbtTS!vbV>YIGs1d>l|~e%1nuZ zb{Y$r@uo(3+tk4o9{eW}jrK)ehO$b6mO05yE{X1#-m*9sXu|TP%N){HR`0C(KYC9! zd-Z?c2KB$?LG}N0u#<~Vy{8?kKhHxIS|0Se`ad3>y$>8(eesSvAy`>iz^6kHGcf)S-w@86dC>TO1j3mQjjwxPZ2U!1P0?mntsDPihi4z1T0Yjb zB!$ut4V6S8+vL(4U3%1syb%`uhP_4@%L-@UBc#f-z%?t|C@CdQY~S){Nc7Q{E}y;p z(o2UFm|i`Q9~$;}?#TLpogb z-P5eA`Y7bB8={aK9#r2yhF|{bzx?5sjMcY>goKq4P_H|p4fm>is=|u0Qp+r4s!;{O zqN4B@ix_Jsi!S`kUgn#(G~`N`+Q+zOMJ9Y~DvsbzX`{tl-+9iZLo^qD@3~ zUP8VuWPv0m5N%`Dm@3ax(RD}`WvO_ba)|cf{~w^vAH5PBh}T{A*xOvaHG|#eOK)@U zIUjuI9MxVxxpzSS!iJl|$p;cgjwsxpdERjXXl z7^$oc4ta72wWY}NEc3pzWf3`Ym1te)8r;S@>$29U3~m+#j~&$&pa(~j2!HJG;F{hy zPWA!Yr-MHH;$wg3wy{2VUdLcTtPi0N`?u4ztZ7y9LY5}D2I0!K!th3V!n2x%B2QWo z+8JhD*ND@0>T@aVDjwT6~uQrfI*-{Z;I`=Ee&qB?u`J?HLQB`wcP9a7mrW|D@2@wn1LX(+d) z;oxyAPAW4SSffap?=mA?po~Yq@y@E# zeuk(o6P=mf`%dwqzx6~D&RZ{ z*BhWE@zz`l@y7S=Z1tMCe z(sMSR;pPsv3Vz+5+v(lgPB$L>uz;zB8_WdN%@EvhNvP`@0^dM`V3~zH7EcgTx1lY0X=Jfafp#V&K4SIRwuR3$(-~TSG#Q=8548Wi8^ZX$<>DuytoZNztKL1<{*qx^UWKI!F@UI2Jmw)ebQ@RIhLo%B;iFy2XV`^9QV(r@UPGX%~XYDg`VYp?f5w2 zoppv!7#Ppxl!K z`^*hC*Y~~r9OMyy>CbQc%(2boDUY^vd{9sxm6WRKX$2{13EdDaYlTgsBu3Q`VxZgy z-DG8|8ei5PY!OP#Im)5?MFXtKxnNQ$p^?5CpH>U(ZNbuIio0Or1xuGb^Rm?o9(y}W zkK^kud*+#|ch0YU$iS~%Jxw5QrAp{T(>M-uaGaJZ~&em6=+?{|yeR4Z&eKl`0p$5nd~u zwS|M2FG8}2FrcpRxyU`B9DRXetzxBHZG1o#vC=Dtm5;jMc+w*>Q#4z>qipS&muaIcxGDz1Wx|{x3m|(FjH{(Wl?~7+*-kw8GCkPl>N$$E zRTs73ftP*fW3}-NTR)lu{>cNIf6NA(?<2OqgQFj}U-}JWn{Q-vs;b396oQ^UJ==i; zu29l=QRWiWw8Why#6x{I2~=|#AzMltK^@Y04)iP-s!cibnQSC0u%Toeu^l%}nxPJd zn~?aZfg1eDhB&WJ0{%QG$gAG+qpumO0Rog;!dClr*FPfS_}Hl)0D!eBv8$RWC9MI- z&RIgiW?8FwW81E4^8oIjHWFg>!ct+ftSUrqwX91W>A~v3GA-KX>IJ^=*e$*CzVos- zK4JmyoNN2&for>Vnv*!y!}nhZuI<&Ye%`fX*Os?j*3#Gkzuqt>abkmK3uR`A&5-gs z0C_=Xn<|znt02LO+Dcleap**g0-*e+kcoI`8?K59Xg?xgg%8-XEzp5urF;icj@;TC zFWY#Vs~70koTW2-%+W;C`&1`AfbvT>MAJR6%na{kl{^H{Xv5DH?FZw$0X8=U)SAR5r?ATwR3~-VS zW_(lfDucmq{n zAapim82gKvqS5BKiB&$izmS}&WTAy4&K;Ew_IyzsWiPq^wt=IFw4EzL;z0SOv1=S~ZRRbsumY=pjhQK!=$I%RO17$K6)^S{(05jB zQsY_RS{5vwsR##hS}xmrYcG8qH(v0}bC&L$pV-ge^?rhEIIQ~S#}!xLGoJZVj~pLX z7SMcIZi!q!tfna2=S|&&6_x6MlU^IglQS(B08awuZk9tQB(k#5RiIrkv?Xn7xCJ6j zkFkKmEwc^?1b61FT4t@wR?pGF<)`zqXI{4Y%6sEw-)rXTopV%A8AR0^xB_qN+Zg{4 z>eyfLmzV!^?5Jn~iMWufK^=Qk%t%u&14@)O2#Wqgs4^90;^!*54woGK>26t!GXjx$Mmtc`N_7j_s$vpiGk5Sb{d`B*n?Mo z3~HNC`0eLTDESo0v!@-36+90S@pVSfBTnBO%mN8i}P4ljTQfA!-Y_2pv^ z&U4q|yfE#0s5$cB8}RWEWJ-(cHdEaSR9I8Zq5nJu+~r--L_+gQlnK(SOvEgBRn{;T zg7bg?Pv5u$C0jdsB{<%(-qya?%#CN5+|iUUo;onSSAl`8{;Y98+}M{e{s=tb`)=zV zH8wpZa0%R_>YVLRN(0!0vpWOJ%X%h6t9tZcWU=aW1 z(ck*BW3|Ud5J6BjyfX~v*F2-QrS;gM;0ihrA`L)wB0B&=(X0l7ae%qojAxi~4K3i9 zWvqnOVUuWOG8o{9jn0C?Ra@>%X5$J|`|Z5$f?bc!PLJl8`X_s~)Z6RZHXKuZ(PCxE zFa1xy_c3F8mC)Y^gLK@mYk5lSw@-<*R%thdbw$>&szxL@uzFzfOI%Q$v;^w4(nV3Q z26|9JOY#=jxU&iujmW9aOrz?Z0;?arMs547J32!++ym=r0|WT`4F^__*!^m_3BU4} z{@MQ+8vyNy;Grr8!0k~B@qWgCP5E<5Qar ztln9h|HOgKUpuw=8+(kvyC7b<>k;Ie#x^e)?z+IDT~FK&c?Dmvx5b*tI}4on89)P| z^IH)_Qv&KOAQCOB2n0g~4yC9yMmAxcz|7lJLxU0Jc_&*W0X;W1`DHTQjutQ2^;o^` z&Z_<=3{?MbHw5H;pUKyPQG6G=%Z*jvG&R88LK@sihkYg+VBZ<=V?n|Q=8g%V#?ml? z7FQ_mk}8YL0YiAiU5cp$wxA|u(brk-Xj_p4^CB9f)$UMeT`Br|AfdrBi(R(*I11|M z-*{);)P54WPn@m#c~^hd>pOeYV^DN`3i+$kV>jiSPRdkd12;9TlmFBHzq_96=GFuI zSPe6(l5)O`uC`LQO=>iPb`>8ok{cZxn0c-0AO$e@IG`N^Ce2Ve$W|OM;(Vq`?5tcr zY43yga{Z6flKD-2$^0Wh;=lX9-k6W&S|Al$374Vu$ZIOseX7;9=t}{qb8NUSft$lZ z&7y5#P#MtK`a0GfFFcGSG7ZUaw3RB9uL6gfdZC!k2y2a=3^1Q5ndSM0hdtYE*I$4C z{rCSLO?g=Gv9`1H=+$b5uQ=SC@jiiZ?<1Z!_5C;Xwf$$pFTZi?t1gdygi|fRUaA<^ zeZ&Y>vk%=_a}XG5glP_?O%6TKBKQOY;|(wCtah$xrK(Wb`bw%!RXPCIYx1pjo$+ba zwx}MS?QrfmTFf$gyW&>Qa27|C#@%PJ@0~`q!D;k`rr(5Lp7xlF=f+M02LL@cT{mzV zQ&zAIvrkRa0WR8?DZtPd8W2o{DXe5rbJDst0R|%?KwXwFR-5~R>zF&HiY(ERz_3)& zPTD>eSkduS$1C!smx*h^CtI*|*)t!*)iXTa;f6>1ch=t1edLrW=B6G({$99TeL}kS z6ULsdVks|5-aHiQ*iY}pRbI2!dyjQv>Q<7RbPPsc1=5j{w1P`+f&VlC%Nm4Fbp~TY z+8X|6=*Py8$aa&NAjeGycTNxXCl~d4@TVKD(tQ!*d647%(=YfB-#pfXT9vFSq3pOG zCU0Qz*gsjSAFDImuzeU^o^8a zpi9-c2QFW{a6+HGaN+!gJ6F@THVlx?wP8ea2i#SqHkoshm!V)4*SRSmsKNkJ3neLh zE?Ku4Fw}rrCEJSBL2JF3N$_Svxc{C5WN$Bc#hgqkWHl!>^3pDUr&W z7};RJz$k--rV2%PmhPevzTfOYXFZ_wI_CjI<8q=YxeBCEa zYc%^TbZc>?X+>4TTwTeD0(K`AVd5Z66!a8YPgJSvh((|@hw@ui6wXu#W-AcW)_|Vz zosoX5z(;@397jbw#?L&*?zR&+?X0z*8d&=yr-{>>d;IHd;0L;={_OXUt-a0*ph$;- zHS5;Cf!yus)AI^wAZ=}QE+It>_d zt-2OW*a37hk@7?;+!+W;mRq$!^~| za6Bty`|8gcnDplULi-PpsCoUXE>4ncMT?kGT;=N_?hx~S;=%vqRqH^~jw%@P7bFla z3FzQ9FfB~f5c0+%TXBdW=*@1Lm^XDJTUSY-0n&gd0{Tek`LdX%@nv>v<}+vGoz;b# z_5{@@T>oO4n!CBLo_;1ourIj!3GW)msl0(^9Sc)C*L7jk?zhjEy>?hTPq=O2QZ?4aD?lX<_wIappbCG!!6)_A z*I$Q|>-5vFr(;zJHrF&2crdp@~fFTqklfg)K3m`V2!P*_*+R=NJWj<+n#^9K~@zy-WG5ub19(!jGy#14? zkG!5Z#jV}aM_zYAS?M=k@)_Sg_Hvyrt#pzNBClbFa06K;D^+WQRfc4d3za&^1hk&9 zy!I7}FrOQjWm^DNDY6oxaGAUk9ynkmuYeC#O=ytjh~z-L#g%6}3qJJ`FWp(2J2QwZ zK5Gguxw(gz{0zh#?d{k6+1TcARcV(in+yuwwPQ|R)pysvUqbe&NL8)440oShZweh)(L zNZ+y{TudtwoEW_uq1FCwQR+Hab(H%j2%CZM?OQb@Xq%vv&8-2hQ~K zHaOEB)%8sH?*93>V%*FT9lfFL}V$JkW%PbNRF7C-y#FIjl6y~*3Ucx9= zz`qb-L>^OC_(}yr6HrQ3Dh95x(jiO7{>HxCa2MFmc9zbxyBWrFxVeqz42-K(qwYPFm>Km9?q`p%fVMfx#gF zWgC(~xB_?p3|Nx0C2eAfD9J?*bZ*Br0y$h^{)>Syzk3>a+|q+Qehm^;fAD>O_S0iw z)+IqWrE)M^YgkCxza_4DKT+n$LX$w~I3PHH4J$#f3AC9oXsYxH__8!^0KyzvA+n;5 z&}4!v44ka;03#8IXvebLjzKTjb*3-afotbBpM1dw-_FuyA8WxgFWY$L>KP~1(Nv4^ zz{!31hCEfDjbD{)p7BLr{?@URLufTuqUr|l=Mi{+17A0^(gmclwjnt1S}+B(xjf{f z2u4+t`z}x$0tv{G#&)dZ#IjrdzWz z;g-HW_)0j1-~1tW{l!=fMFe)qs_6!8zSH{PhHdU3pXL+I0k*M1m&`Ok0q z`saAsA_HdgiggjV{2NLn zFLIo(O13`Z020`|>_-}(4_Rg<*!v~E8S&9U=1h{0c z3vx;nvfl!>Cd@qRGFFLHEokKiF22tV337nT;hqAoziG>SF_Up7RM7z!A5*5ws|)gn(F zPo6>Iq^wG5u%uZmGdp!x3ruG_OCR>Hhix}UbD=}M!R&=@WJ3zFhyUINznu8+&$ie>P-qh+OK z)~aZ8UZRMytghWSb@i}H{{K?Rz0+@j`&jnD=WIOV_&FLgA_Fs$8_cN35_~^gRbT&~ zt7nbPh`J`>)kLO;4xB$?Z<{qMs(^k1c=QB7DGk+_2$Gf zY3rsgL}fg#n>?XZCp5OAIbu1GK&~FC~jJlTF0xB!Td4S zmKkmKfJ-rCpB9}W8Of;WGZ80&*H(5(!UFbDAG zkv=?Wdj6PLP+%vwrG=5N4M_=xk=MAUC?lnDMFL=S)6isAFbUfEWF!36lm!z6`ipjC zoW-&uXlGr78mPqIZcvH-VtOmY+E4m~pZ@)cN-$tb%7YlVhz%Fhy%OenbE;--jdB2Q z)+Nj}0p<*tN){_2T=YQ!vNOkxOe#chB3*DEA{1x_BfO0^v66^v98%A;)@6d-TfN}8 z+|JT%eyo{$@2m~!ferELt@PIZR{B~vfM568XMNGwhK!QAM$rGUek&bS>+#gB2FYVI zLR*osPS<6qE8`feyfH9NSeZ~-p9qwRR-=bA8GIph2NV4O?w)tVRAc*DpolXidS#m4 zg73VYrOTwdoo8OK>&(~PS@*QhS=HYH{?mqAK#$`1ONjcu@yF_?j@=V;3I@!WcHo{i z9P#@t#A{hIIKt`bh}W_0A`CLqhICbCSmuz2fmb2sISeoZ?zYqdKf7@p1KkZ;Ij5Kl zQh%Tv@iR4Qd2F$4JuQSV8)p3CmZvkXcI~ZR8P+1?8r@`9zyo_X2o1&_U*rORG-o2%!1ubp#KMempPYQ!7njo#XW ze*Xp>*dM?5ws(%t>4)8c0>cUipzPysZ7+1ssLzj^l(WiWEHS% zD<>!gC@_KwbO)Im2i)EoCTqyD%{mL5?c-a-Hr3$x?mb6=7la2hbpL2F*8A)UeXRP3 z=~eo4k2CrlumN||3qO8r0~LUQ>kOc$>sRS1;u!Cl+L|3yO1jdQ9&sgVNPNTf8F*$| zi@lGwX`52R$Pd>7i(^oLjqfP1rwNa(an^S=n}n)IEYTe};b&Uk&g$YmEWOvo&)N_+ z_SM?2g0S&DKcb&8)-+iTtJs9@Pndt2Q>^BpD^`JB=c1hfGQNZz<@B+ zuoZ7;3vu8TSbf8kYLBHq;+IQ_J10Y#`RmsJUaUQj#;-?Ve0Xa=$S9n&(}>Aupg zKu9l9fASS$QP|40+)6%}DL?E=e&$pZ4ko%X3oYgtz-Pz{;5_j~qD|W&jB`#_Oj9gZ z3O6`!VjGJbrtKA7Q{)w^8a~M`93NXe5NEsKb(ftH$8qBY&wPBA?wnt-1HbaX)Wx6f z5gV(ah}VA8uTJhmtEyr{tr_^0>3wK}n>R4v$mamvXbEa8&t>PbXhnrJ)i@Jb=pb_d zOmz&pEn2+l2L#fkMnGdG&pEUYAB3BK*vhY-gEe2~oVK!h=M1kN7~Y1Vh^PCS!7qbJ z{>Ha|-LuCBIvmDzM_A(PvDJp6**^YeEfb-LQ*~-ICmbfow3RhgfXSq*`G-71q~f6< zIOU>-0YoLUaK$VO%Vb*sHA!KzsOCnP%|9c(PUcPz`;FX zN-=V}M+H0^9Nh2y?dN~g*uh2G=(@vtFuHr%gC3^07D57raTX~{SfrQSKb!Jt|L@6ZdaL#_1I_xBY(`)ZzLl`F521=UzgA>KqM`*yi+t|L0Ex3%PUg@ z1FWqeY@)8D;5a;t+KUVmXc<4i8_CuzU|G0uOdosMOK)@af)74t=?sr?xEbnp5MZk>u0;Ck39uIr*y_rHsJo=aEp4{)LS$J+8RrqnMQOTS_-lzH@wA=skSkr zMkge!OCn*(9m%AJ@~=snL_{mn=4iscIY%DbKB#A^{?4h+IdXjuU#RfSSl*n>m`%oOJG&}o6@FIc*@*WKF23qIC@rN{AgXFl@`zjict z@i;gpAHU%)-Zv1v0kZtxea~YiJc}Hbk0B6scx` zGwASS4TFlV2&l;Fl&l6kJ^|uSRlx8-r=U|eCuJvsmd$Pi)er0cA&^kunMLR@=)_Sdf%2P6$nFp8D510s)UK=Sx$SgSf4vF43(qyQRJsepaP zb*O@=c~MAhkp~z-gtA@KU2UOyz<{i`RVpEXEy`lTLo?Hr9!O-nZ11DD`JGkA7Yxpi z&z&L_&-BHrGw{o=z2FHyI95k!BFzHiOx{m?{M4yB3JTgrq^~6+HB9t@sY`7x8TWvp zZ4*@uaKNgz${T1#)jUvzh07Y$c12l2``1ApA6I}j9qwg44YcUwREtjcaI${~x2(rK z>nA5#go6##c;nX7dK;pU>!)gD7_iV$;2g?Rj@p!hif|FnI!#n%2rz~KnFt~t7(_y# zx=Ot4NN|O0Tbeh3cpXO}%XDGc>IHsj!P4XS%*XL{mwo4LuAcL?XPkJ4>!beFz(@Vu zG@*E=uUdW?ATVF|q#x?WCmjG%6Up1mz8@d8&z!V&Qz9_7Csj>KK!l!ZVKkGtz$C%| zgsv2D%o*RnP<68!PzOWQMF?=%!NB5J0q>fZlPR7vbzz1`j^+~E4McN!3V}V-x9+?U zg!bFNbo(u1(WqET82N1m!vcm7*bTglP#C5#H3_2#JX0lgdBa4jTB3w5wL&m>)i{Z2 z+jKxFLoAFJH%$|TYqg6R&;^fAjyYDc{Bkr?7j_^DKfX`C9Z7Fz*O^ka0(1MA4V1E{Z_=aBfz&JNCh1eFyNwr5p642taGK} zWeGzOS%Cjy0h8jWfl4&ysld(_t|J86XhYD`Q5ZADvcTWWkm%ta=r0^d@{Jo#jlQ&V z2jp1(=#xMFTgH-f1dd@2{cnQbx3GZ|$SN(ys9pvu1P_NNK-4HJ+G%&k|>6S&4zsRlmMCIN{% zHB^>DB*2N~RaQsrx~hR;<4!wcfUc@Sm|FO^!T~Dc9El#SD>@ow__TpCyn90o(4&M_ zgy~=R3BT~7u}dPFWOb&qAwSbJ2H01+wHN@9h-FiRnz)h~j!NxqnGjYSbW0lv(FInd z!wyO)t%0W2+M%LWFzMCiIZVzv$m`=W$Fj}U3-oyAW6$twha1M;K2YHw+~Cjp-1+n2 zm&g81_xiC4H_77)=0Fd4ONPmh{hH&N_Ig0EIY_w|bz_W!5{qCh%7A0JN=Y}WX!C*= za9Obdg-(gwms#cVwgf;OVP$^6qg{?(4VFcsGnD9P{NWt~WjZ}krjs}HnY34fr@Zkw zAO0Oz_MZVxDhc1tnljiQ3=i*g8+r7b5)fQW;n*Ex;iIy%{r=Lkujj^+r(@+6!4Rv|PEoq2pVT=tAjJeFwsK6IhG_Nslk;+SND^k<3OPU#OEIVfw zgzN{h@v@g*V0v5Ic+NA=u)f0$DPKIWy3d{@YEItpkJFz2;3vi7#wSioQefCrgUJj- zcAxzo(6t~SmMK6hCV|O-fZg#{AZV%*3bLS0Re{L~$Lmg83G>Y&056@Km=)E zCZ{=&AYhr+@0=jd4Foxz_I&b&9xV9d5LCSF?cYB^_qNmq)wOt#>)3#++2{FQlV*~L z^Oy=EZOM}{5hgo_HioQGObby~kXs8?UQ;RqaDPTj74KdKhl#MX5!ZBRBvfHJP=MD^@vf^VRSi zox!9W7zDv!P`gQiK!k{F6EfWji8=&bP<6K>^@?SO!m`zK6mXd{didWg>WdG5YT*FN(69UIUOm9UxB zN%IcGSTb5O;h~8El0&R^B}Pd?qC8WWc~Xf&(V(h4YbAmC>}{SkS9&61rXn85`Lk^A zW95yH_lY}GP&@0b?iqNi|F+=>`p3FR_LjT;`Pf?t6!X-0+^yGnhnLv>=E=1a2s)&i z1ckaHFG`0vG-kWnsmcmjIN$<7jst&)sevsOiKv-qxc3=nMFCgesxgz^|Cv%brmDEJ z%5-s{OdIMVC;N+Z2Ws|n<+~mjD-+j{6KrMv5E+ptPQw|NI*~U9lcYg)GEw6K(1WSV z8QjAN#kDFR_tDmQp&~T(!LKgMnUM%}v_gKTI_?&D@&!w`WxF@v`J(BWb+RwNeg~vvKkZpxn2+5I%nZSmicYVeSsO@eHq<+sRXCm33g;*k5N4CA z%~I!yDHF4)h6=39v~;OLT^qf{PBgT%)H|Tdle&4h-f`jVJ?9o!{liP2{i3tCpZm0n z7w>xZ?HBL4zCCm1%=PzPyma}}^$Grw&#={_Np;*ku-Tv3P%P;oWOsr!|LYIY|7L8n z9V9JM3;g&4o2|}0a2c+}`s{@Z=P%s3x)wiuLwumxvY@;qSK!Xaxq72+X-p%K%sZFeP zDLY`fBhW;jwZwL+Ox^)Bu*^$YB7g~2-cuAxeo(l-ot4a|403xRf#1-xhg~+qja+Dl%@@jPCAsFh10-BDfntK zOOPQ?3i1;c;9)M$XmOBZ6XqD(@|4AbU6(oY1sg9|I`f%ln9ku|dhQ#T%+1pP>ST{L zc_;kx$shOYA2%@>V-oc6v;&aB6yJE;)R{pIn-sWX7~#SL^>-5xFFAxqE#MfvB(CxR zF*PtS8Q8z!#slC5j8>r2qBEumVvO-xAj}0zXX?Rndt0{g@m+naWVLhVeraItkJ=Es z^eC2h1K9IhUeW!@*xVr($}&ZeSU(}BvCD==K!`>F!R%42D-VH5L$w!#!faKj5y~c0 zu>uYa<3%b^B^?YSwjFfkKo^d-Rc#I$(z!s3j@{B_wm#=|XQ=(rqzhg)Q2+N$5zQz2 zbU_V<@zLM;xo;e+zm_#Lh-YlrVL{H`bHbdvA5sRZQ~{qJb>6k4WE#PM#KO0X2jsAl zu~SlLFBPy{Ntb!nJ@D$&A((!-HLsqymjc6RoU9uZDyPBpek=dl^)pD)#k`|+ zRuPv3)GbaF)^QWI@7}NEW)h@Y>P(B0|U2Hs{Vi>xmad>TUouc%KXxSGQWL8 zq}szhe;y8*|M4+T{G+ilH>Ml-7}K)OeU*k0dr7D zDJq&}P8nzt0UCH`tSbael@?a}j{2hEZONpFtSB=8z(Ch9U}2Kq9T3$5*SMXfTd{y+ z9O^FnUfYrJf?a36?hF@qxKZ*01J`!VWa`DqQ$3vTGr;L(r{4CQ^?dfL#(9~niNPJapACr;RR?A5;Xqh6;Ts=ds zhwD^cF_7n*r={Ohed+fCL_A;os=xbhV|h}fpn!wAV*| z;+lJ3eDT7$OV>T}oE`tk$!o;w;>`k4dMd)4&beX1`Ze-m6)p7FFy zk4-NMTryvo0kh_JuouyZv;ce}WmuEzUnguzG_2j^r;@> zeJo<<2pWK=|tphHL-R%K2?W~D2O*yTx9OyM9g z%%aT|3=eHC<}y?o)@1noYE>P93D9m?_8w2gkM}gJo~L&D9G$&4Q)N-uUP@ z-Z>xh%7Kshf~k+WvA+bo4Kfbj_;a`az}Uwy57R1GMF*U|!%ILp^)X!9tPDcX)<=^H z7)w?dW;5Ps$wdYO%2ZAR$vsRng8`RJHNNdyVJli_m^X*oaWJw>4Q8(1Iaz+rK$hRN zA@AGQ$)5sJ{;7ZUL;q_m%L-u0p|*C=v^|t%GnHi&oi@3TWz!&Kvphh{f{3e8%Q@pU za4DA7YZ%NfxmE#^_YiCn7);k?6bl3#eq&iKFv?@Mbf)HQ`+(cpV=oZ!vS*&TdWMTQ zn%vLl4qOGFy8Tl<_}joQFMIt5f6&-fbOZwsrcHys&7rH^Mo#A2*_tyIP1K0A3scX5hhqU}#_~o~6?Isarp|cW-Q$up#QT2I4UKuOk zTr6uKq=NR5T$g1@fO;_y?Wji4W|^c0nyQ?zCP3;ri%8MJCSMW({M|Gghap?jpJf{_ zTRlesmwVG?&wPAWZ|7?-*mdTo-Z>Zbc>@>qvr`v!sz=Aa4t{yfA72dP=qGC}0w8lEa&upxfD4vx?RA%JylnM?@3oz!NB_DroYT=<(Y|2dr2e0&le)1-@vWH4FFxbr zCrqD_xh{~Vs@G9`(-t1Qx6K+nIAkHf>)D6t#H}?3j--p}tYd`nSe>*;T98ipt z8e_gL)KPGJ%T%JuHgLbLr524vR%NR|bx}cE7W8o0 zh5_<>!p}BSEX&W1Wg9PBee`O4yua77uRU}149h><>+q`v7JtnKH{OHmzX>ez?<&^_pnTtj_>M+KiOT+c=6@S_uh8>_4nU@|3kCb@5Py0 z5%vtvbGUc3e?9OxU%BCq_K)-AUijFr|M;=T!7A^pwOPAob#9#n&*BZ1j+@5Q5TF>UBFe2~ZD=~yLbnXV&RdJ7C4q-c;D}A&n11jDkA3Wx z&h&9JywuS|z5iz5tv1Y)yRpY^{Tqm!zUCd5ethh$V1i|XDxkuDXm3?ay;be=CKRqI zoYfHgl!zs`hH@ko4A53}y_)KbG@_(yTVQQK4jBtG!9Va z7&+S#5=8D($QVgLzvddR$}IDhN)~usnzov-9d5_GXWHnpC}xH#9c~=*r2`du_LPX_ zrXD)=YY?G)!!La9gtf1(8wGr1dJu;U8-6x0@;aMrgkcPlCgc^DK8w6m7-kZyPsKGKN+G@gf+4tI-ptiR074*GkIJct- zU%qVM%SFA}ouY zi#QmV;@v?X5{$)d+K`G^rz~mFuDOunodb^)Qz@AO-_4JNvIr9sGEmdPwYU+c$Z=L5L>RsN*qW&e$N02eV0Lqs?yS{(#lULLOv^4e z^&qC-hF>20@6SJH?7?*lotz902bxd$0yiKUicp(2l^xt}e771frd<(Y#XH5I(OqCF z5P;B%Dzzp80X#v;mRW5oUcmemWy#obmg(>up+0PP7mheXcdTSQ!{!h7O8VM?tv_O# z8o#M8vHmdRJAUP=g1}?x$~M9iPg|=T$GX%z5{s0ofG6&4g~p~8`4vIH4bpv`orGyxUnE{ z8Gy+@&~81{86i(MbcdID$Dz9cQcz8FbrHEQV^L$5glk>oysZ#|Nu8pWD~c~lstO2* ziy~&32w0Pe$3Biecfm7n&3PTujb}dg&ROeg2G;r`8$zreiS~tXW%;H*dgo7#t+lRX z20hkfh#IY5dg;=+yYD^`&)*HB4^}e|s;TcSDD_>HL9bGZ9w;4nCu*8WUbnc0QC!hA zrUPDZr*H?9&y{AJ(!^`#TT05xPm=sIMRFkbgk!k(3{^auyvACRB zqdxi%KV__vm8>!%Dl-H}%8Tbt@Vn36e*49hI^H@}N0Xu08l1zZ7(;{T3K)%;QBgJ! z1pqS)YY>4#2eVxp1c|zUNiwbUIS}8{$`sR@(=wGfR<3+!Rq3k-sth6eqY8*qD@;t^j#mu2Ug(#VRerfPa39nP6nSw~vLn`B`m zA>0-_Uf39choY;44iLwp!nuRQ+Fn7Lxct_;HN#sbrJ1XD)^h)CZ>m>ad-bLBFF*H= zXnlt)A#mS>q=A^Z7T+ZQUE+irmG2aHeXG4 zg5jWTVX%Y0;|^Q9x(134*~m~UV8$-$GB_G0S<{)K*^VmjtU~;|fkOQC^jdjyU$TAx z6ymvG|2JZ+5YiF?bdoY(zgAA6bI+OTgUmf3CYYijAr!pi22ssf#&PRKXL2c9qp_-q zvZ$~m%Wa4P(g?JSypoiSbAbyy%&}WKQ}=gP3tuwaR#|C?weHb40H$D#6lnea-SMT4^pzi;O&MQ zEpUX(G;k}cXQ=SuChNa$pt|3`L3R7A(YHa|_P?I@zHc6@ZXW8Yt?F*T>bIfSctbxF zm6TH$;*NrhEz3ITTB`un4q&nt=6MMZ7bsqs&XWuvWWK0@oU2VN?+O)((cM^ow<6F3 z$vJJ!1{Q3*VCgbJE!cR@(w(!|uOC?Kk8ik7_2so61zWxYf6jZx77L`iLBpxgtcTAV z0+&ymo;aa`{4T1au%e66LKmbYiMCA!v!H6hxpy!x%5<#(Rz2Y%5z;0lJ>)h3O{=7pP|A>lOO)tfhzyRhQsF{scqiyw6}iESe0W@hLY>z{T)7MrmE~E5EcQU zc!*JFsmyrkq?615uPh8LWg^tdmYgT(;A~Nsw&Z|^$B^!ye%*eLz2`Hmu?rT+`rYs9UOg0+26e=cxQL*t(F|G5u$acioXX?UM zjdp?ITuDo>6w&UiU;3tjUwYdVvv%`8(!2iF|9s2ej{TCfCNtXB1HUwaI&BE3fxd@9 z-kD^OgRV+#3DAoamG5wZDM~B@-aVLoXgXV27?NDH4rmjjYZFXYr%1bc2S95Fa{t|$ z`fqLHhn?He&`r$w&O7U*zH#8BHqg7?+{0;p3EbD2*L>v@#!d=}%377X0S00Uq9>bf zOK{Bx2v>}aEo9d)ufYU^UX`>^l=4}ArgXI2~-NZjMlo zYZSCK54CLLWvdqmcxxLkc;+Ksy0iZ6n+N{w@zXZXTY97jf<)1ue94pV82h&da9No$ zRIOhQM?u&Nrp~S9QNl2xj9S~~7CJsHbgwJF>KOp)WB4z!t}Se7Z~-IcA^=zwa46=r zQ5IXM(@t)O%T#citLG@<&T7-Q47BOYG`V<74>NfYWY9m6KIa$5+EgaO6Ofvdfi?}v zH9l&pO%ZVvOQJQfrL>rMK^j#O0#HCLLz&7-m8pO+4XS3Ok&H&{WR7c}a#Rpk@6J)V zOcCd((hl4uw)WCjx(HnbXqLEj^~@li`==JmQl+cx-Y&0YqUOq#2Z^ zhB$}~b!5I8BS|3ytzinBk9#ee1w*p0mMldH~VS!_Cg$_PFcDhEpW$+N`JsXVnxE z`MBwEmB#?bT+<>UI~BZ1K+@wy8M-E5AmXyLVLUG~1iaClbU>5pOwo`W=Ri(NYH^SR z7sp4BmTT`aV_mj-!FOJ^@v_wm9(%#ktv&P1jc2&H!;J~vFmP?xZn)I-HU0pu?R(#M z$KQ-y8z5jQu+t60@C|U46I0(NLThSZe4vOMgP_0%I7k7gGbcF>o|SdvLT5sSjJ%;r& zAO}FN{jgicE=F`D$O6F9>o@)3wPWwUxjvcNRw&fCF`PnUX;g*97(;id$$>euwSjsc zifE9}8l1s=+0>!R)gJmyw28j7)xlmn7AVnzrOWhh&c-v8?`T5!Z|}8kug&eH8`4cZ zEc5B0O60aDJ!!0j6pO$JOvYPR!fA2l#NIY*U2Z%gqLGzcb*!s_)@RXpLfb?-U`g5#7hf}V7)&B)s^zXy)_7kx1(ljb%y#l8DdVJzEFeri&8e35$ zX(`WL1_NzZ)2A5>8|ti9ZOIC@;yq7@$%rU)hXbiwNLEl@w0Rf@2HVl11-s5vq2;-Q z1%q84ax`F}~v0wx&WI|EsR?QsH5akV1YOMFm)M(l2IqI-n ze`h}P&g$WJ5A<-wd$szrR=~Km$4$KlVniH1<0Hm;h^jmltqjAV#MQY6E?>NGLZ7{G z;rxX=S6rDJ;EFJbf>oJ|QUk!X1q#=Wau`w!gxEkB(+Jn-C~6Wsq?&SwG!d}2h6ZRk zb=8%!#gi(=b`)aHt~0dZa4#8e>czfSLHOgn`@p&u^p)~A!u8`Le*c&Me5?hjBrMM( zAM~J2PvQ+AAee_hE1eXSt$=r>=pHcemz3xAvW9SnJWm5N{q>?N4n;4fMt5_kve{?sH!C`mxo*WL`^} zaySoYTK9X*-ZpD>KY*yzDC($?@c?^;dBUo(Dl!;DUSJq5TpG^1tOmXc0nP3()%ksBxIY5epP?n^t>#_nz~?ch(NRXJ7}9+F%Dgg!SEE2k*M~7v40s1KnYo8e`ac zEIqY@4Kv!L(E>NPYGKYS%w#jAB`s-Twuh43MhVj-P+O#=iY8E?et@KN*J5U%9V68$ zbi30;Jx3pwp9BkbU1oZ7Hr`qF|K5T6zi&eb-@`vX3Npm+{Kb!X#aR7o7}JI z@GAL?=@9_jMWHT@MFLr<KAj;WkSBTxmsYi$ywfLgCFg> z68bSmnp=w|mRrrytNxYw%yZOgXPw0N4V=XDr&ZWnd(6jI!Y|))<`vH!I|<)fn&7Y- z=32?w`(QN7iR$d#_nfU`D( z;6Q5~9FZXxq>TyuN(Dpl4su}g9EmR1{bRT5G6%D4^@5LeOgCQk*fUqp@KcAIf%^V| zfBNYS{;AK0zX5*v!ry=HgpaYRI^br5>hnX#@XwzHGBAoSQ0p@a1EMsc9+Ht2iWHSD zA>9z;s7JwZR(41OVtl;J8f1BZpq-UsnieesSv&)s)v z6;SOZGuELw&?II8x7Dg6RyfOn-bk0n$XoMh zSKh`8)MLTYW#4(5tLMD*4DWU{r`B5sUhaug$kpk--2dH>Mf}*eeN`HJxw3&2UdQ;L z+&|*E+AyURintCy+&OnCqbA_I=DCnIz^uZ;WI0eILn$diSd9$@Ac}KsG<7NssZkB% zm?vn`IWk%9q_(r`R&;-c0w3=2`~w5U{oZMk`*e>LTupYr{jXp9mt)1X0=U*-;2K&F zw}we>IX#*y6`H(}B2SXxtn>s*MI!1};v8sk1865J%cdq7alq9bQtpU@ahy$RfxFGQ zIL1pY(4d)Wuyca^j{`w&nCyPKFV?Lt`k#20`GK(@3F6YXo(=Hq(_$T(3Nzy}kCN7c z6_u3*3=WBT*%dL@4HWG}6*G#kf(fb!qH9%kGRwShL^K9T%gf0{e}OO;ES;$bJCI)3 z+DmV3<7KisrjI@IrDr&*qY0V*uYtqbaM3^AW8>ZqC(liP_1;P4r1d7&Mz8_8V0iL; z^we1ysiEPufDsYQB?~-RvZO}9jo!INmSv_W@x)Zrr6O~XMt}mVx30+qLKr&P4sycY z2bw_t?}0$Scf(D;hfO^nf{fq$bN;yT31o7rJaFX?M*j>2nok7^^c#$DUjgpJU}IF* zK_?YV`p9VN_zE|b@sybeWM~%V(2+KSf>~udls9e4!UqiW`U6dwerTXfr#HlD!;A->j2c`!SkZkRUT(I$ir8A9Z`OSCc#yfka|KPxC-#$eQ zp6(%2j{tlA)YHHEhOyO}nges2Aj4|oB-y)nw5%uZ8{%zoCzX^&I?iS$X6$TA#EKIo?b#`O6EYUZ|2@JwBm4c3qL&2hIelW2>ROd z=YTeR+&g~ed&k-U11*u@Wg4dbC)k(`IBK71SBeU#|6;HavidD#y9k8|JVHBJVPBG_ zf;oY}qoi_}znxbVFnU2(OhE_&K=s3J{OEM>Xl@%nGSJ1(+z?OnWyMv3_FKQ?^u#k! z)LLkrs`+}JYm}gU{4}0`+XQ5{gaXoCA1zO$lo(nB3zU;CNgsiwmqTl8@~Vs~!yRcX zjiQ0!@h}Zp6xq%w;)2t4J4=sFfe+V-{^&q)Hza7!^!3wc!eRRlAN$|4u@fy7R5csy zvvtLt=JotkcX?AIpp9}lR3<pc14khqYAeb4{bU808xOz33 zR4T6z8iYSno(FOuZ%2cV&#pTcdHmSGXiiV#&@+8G?#m!1G1q=YJ~o;r7JwNLL*~W} z+(>+CH53!4QJKNqVUa9MAkIn|fz2i6vaG0qLBmo&3VhXP?tH0ruw|)%NQn|a%XRjk zP$V-%ax@3=+XkY!JPiuZ^aZEqz(M>gcfH~d#-gc&QZNaE3?~8&XSi%&rr>#uu7d$E zxn?XH=$eBe<}e?Ys|--nxNr%2D`4t*fw`eHGoiKKBJHcL7L6<>MT%{@qpj>cL!l1$ zVEplcBHc8N=+5*7uRnrc{{Hu$_fN-)6eJ>@pBC9Au0eaMY(*ngB28=$mVJiZR<=zNr;v5Hc%j9`{j)~)V7Vn(e|HMG; z|8j%c_hplFaOm-q#T)-Od+!2vOH!7HiijdI0*Z*JxD^3`!0g#`*)!a`tE;-Iy1S~Y z`chSWMA*P)KxSr-GegEf<)$bqN24Uh#2htxP#=w#6FD9=csYWik_02h8we&|xFl#u zM2H5=yXeis>Weel{r_vNe`f#BgU|e~zjpQN`o6EKzVH3scWm|!68ogs(ZCX}mwsU2fz!S=xi<~D(9!Fku5iUDuar-is618y|D) z<>N4e6kC$n)ZRts*BA8v?soCmmL}`kxQc0NqZ;_t+~V4zjBAS$cw}ZmtO+Z0+~rUo z71E^{s>^W^cM5E#WKu5Nry;GIdxaUU7<%cMpLqG@x8HvI6P|bOIp?2!;lc}_@SF=b zUhc2E?z+owzHsYpw_YCbpKRx+irt*3f90Y--uFJYzTm~@??2=AJxbuS@yTa=JH3AF z_M%05c`ezURc5c7`n)R@+B~WDXhu&`>-^@Ffex9Zt$^rCp)80#PaL{KCo4xZ=!rM< z#c}MK;L6ZWu2<`9c-`oe=z4qm#w%Xdi5t4`B_G_;;8zDR;%So@aprVi1b;coN$-FC{}aqgzg4dhFvA%e0>#4*@&Lksdbr&{!a>(Y4-(#6h) z!lh1H(VAAKaU7%wxR2m)pkYW&(@HLjnO@Xnm>OL#BECqMF<7ysmV zH7r-IsLL=w?vJoEbIp!{)y(E5DKgQyAWX-)sIIFx%R61?Hr2jJnE`tytpe393pm7cbl^k_^SyT_9C zSa&8mlF=(3dp|=LhM^;QS$moL;GFi&1E+n%q@r-jEUHLUNX;i`>xHXvB;jd^5gnWv{M&&U{MVV&X%ANU5^QwUysw?Pm-ax94UO;z9{8YdAA4S7Gbnx_IXCdU zL7Fv8J+A=FEsC->wnYVGsWeiuE~$uWa))+JAxtQAYnm?SRCFlMmX%TH?9F|YYW&=N zuB1!C_>s85iiuC$(32SCPR>hS5ZjIyy!IP|n0Bw3n`mEN-iYCT__6JTW(Zi2CT@8> zC>>3&AY>ZX+APgnO=($2wDBP{Qu(A{Ht}^3gm*0I1d7+OM5meK-X~tkh=O5Q^AM%3 zING>&a!lxAL_0YTyujvnbCUiK1KXd9;hs6&BdQ*b{XBZb_kPECKQDAy_||oU>-R7V z^SG(qYrxb?1H%70Cbno>k)oYl*V+Uq-sT<6bFWF*RAe(0kbFcUjl4_}K?m11j*zk4 z@5d1KwoRPSiTC!{C(1kyHdOrPAXKpFrTdB=VR1Pw1@cip^t5rPNNCj3$}{n|AN^;5 zmmXO+NMu)>X!KLIoR*+saUxAkXC*6@3dTjQVWM@FmQsqWE@@N6aSry-PE10@IwM>+ zdOvo!V%BwEcireEkA1Md^VWgyJZ*YyzM{v&JQH4Z=BBGB+iX{NW#zaT_|B)>7u|a6 z`5SIHh@!`grz?_ZI(ly=wq)W3G3R)%v%fWnHUH<#KHcNJ zz7U@z&;9pTkN4@yM@mCibkHO*108wPG}fq^)pc7kl+YqjVyLFdWWuaQz7AJhV#<_T zuM5cJD7hj1PIv_DWGaXuz(eK-)g`gS7LFv&t(bVl(0#_5d(dMaT(tO)gJ>aUqD2oh zdpkaPc=woJ8%K+-M%z=-C4*=&YF)p0+}TEkb~??Wdn^ewmg-pArWOHwI#%*jb|H!}m%<;x2k@#k4QwiJlFmokOr^jUUL~tMtBe2GM=QP-bYsXyz z79_^9qS0XaBTd^$o^V!J6Iq{G8s(*kKn(`viKE^8b{`}5eXrizH$G|3ySJ#Z-a?kF z@xjKb-yOuNx6T~Wd%W9Iv3GsbtAD>8$Et=wS{u~7!v^RfBj!veNJC{)t3*)9WKcm= zouyGGt&@PqHo->9Hc3-DlS3|0#u*J|S%pmIg+gmctS1QBb>_chMfYaF2Wv{dH!!7Z zCQLnNuIK?=zl}@VzxbZ3K5J}B71c&NR~Fmb-l#M4>~zZu36ou%2x)i{<3G9Ukb4dw z(G`&3^`O-#0!TB2qKV8Rl`*G5SY|mBzO8sWVF%osDXp-=6+`#-b@w*$ikG$Hp$lC7 zcoV?CKXCC6%@mt@Jku@s