Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Internal] Binary Encoding: Adds performance tests with binary encoding enabled. #4911

Closed
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Benchmarks
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Exporters.Csv;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Jobs;
using Microsoft.Azure.Cosmos.Performance.Tests.Benchmarks;
using Newtonsoft.Json;
using Microsoft.Azure.Cosmos.Json;

[MemoryDiagnoser]
[BenchmarkCategory("GateBenchmark")]
[Config(typeof(BinaryEncodingEnabledBenchmark.CustomBenchmarkConfig))]
public class BinaryEncodingEnabledBenchmark
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to model it as a param on existing Benchmark? (To avoid code duplication)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or derive from existing ItemBenchmark tests?

{
private MockedItemBenchmarkHelper benchmarkHelper;
private ItemRequestOptions requestOptions;
private Container container;

[Params(true, false)]
public bool EnableBinaryResponseOnPointOperations;

[GlobalSetup]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is GlobalCleanup needed?

public async Task GlobalSetupAsync()
{
Environment.SetEnvironmentVariable("COSMOS_ENABLE_BINARY_ENCODING", this.EnableBinaryResponseOnPointOperations.ToString());

JsonSerializationFormat serializationFormat = this.EnableBinaryResponseOnPointOperations
? JsonSerializationFormat.Binary
: JsonSerializationFormat.Text;

this.benchmarkHelper = new MockedItemBenchmarkHelper(serializationFormat: serializationFormat);
this.container = this.benchmarkHelper.TestContainer;

this.requestOptions = new ItemRequestOptions
{
EnableBinaryResponseOnPointOperations = this.EnableBinaryResponseOnPointOperations
};

// Create the item in the container
using (MemoryStream ms = this.benchmarkHelper.GetItemPayloadAsStream())
using (ResponseMessage response = await this.container.CreateItemStreamAsync(
ms,
new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId)))
{
if ((int)response.StatusCode > 300 || response.Content == null)
{
throw new InvalidOperationException($"Failed to create item with status code {response.StatusCode}");
}
}
}


[Benchmark]
public async Task CreateItemAsync()
{
ItemResponse<ToDoActivity> itemResponse = await this.container.CreateItemAsync(
item: this.benchmarkHelper.TestItem,
partitionKey: new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
requestOptions: this.requestOptions);

if (itemResponse.StatusCode != HttpStatusCode.Created && itemResponse.StatusCode != HttpStatusCode.OK)
{
throw new InvalidOperationException($"Item {this.benchmarkHelper.TestItem.id} was not created.");
}
}

[Benchmark]
public async Task CreateItemStreamAsync()
{
using (MemoryStream ms = this.benchmarkHelper.GetItemPayloadAsStream())
using (ResponseMessage response = await this.container.CreateItemStreamAsync(
ms,
new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
this.requestOptions))
{
if ((int)response.StatusCode > 300 || response.Content == null)
{
throw new InvalidOperationException($"Item {this.benchmarkHelper.TestItem.id} was not created stream.");
}
}
}

[Benchmark]
public async Task ReadItemAsync()
{
ItemResponse<ToDoActivity> itemResponse = await this.container.ReadItemAsync<ToDoActivity>(
id: MockedItemBenchmarkHelper.ExistingItemId,
partitionKey: new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
requestOptions: this.requestOptions);

if (itemResponse.StatusCode != HttpStatusCode.OK)
{
throw new InvalidOperationException($"Item {MockedItemBenchmarkHelper.ExistingItemId} was not read.");
}
}

[Benchmark]
public async Task ReadItemStreamAsync()
{
ResponseMessage response = await this.container.ReadItemStreamAsync(
id: MockedItemBenchmarkHelper.ExistingItemId,
partitionKey: new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
requestOptions: this.requestOptions);

if (response.StatusCode != HttpStatusCode.OK)
{
throw new InvalidOperationException($"Item {MockedItemBenchmarkHelper.ExistingItemId} was not read stream.");
}
}

[Benchmark]
public async Task UpsertItemAsync()
{
ItemResponse<ToDoActivity> itemResponse = await this.container.UpsertItemAsync(
item: this.benchmarkHelper.TestItem,
partitionKey: new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
requestOptions: this.requestOptions);

if (itemResponse.StatusCode != HttpStatusCode.OK)
{
throw new InvalidOperationException($"Item {this.benchmarkHelper.TestItem.id} was not upserted.");
}
}

[Benchmark]
public async Task UpsertItemStreamAsync()
{
using (MemoryStream ms = this.benchmarkHelper.GetItemPayloadAsStream())
using (ResponseMessage response = await this.container.UpsertItemStreamAsync(
ms,
new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
this.requestOptions))
{
if ((int)response.StatusCode > 300 || response.Content == null)
{
throw new InvalidOperationException($"Item {this.benchmarkHelper.TestItem.id} was not upserted stream.");
}
}
}

[Benchmark]
public async Task ReplaceItemAsync()
{
ItemResponse<ToDoActivity> itemResponse = await this.container.ReplaceItemAsync(
item: this.benchmarkHelper.TestItem,
id: MockedItemBenchmarkHelper.ExistingItemId,
partitionKey: new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
requestOptions: this.requestOptions);

if (itemResponse.StatusCode != HttpStatusCode.OK)
{
throw new InvalidOperationException($"Item {this.benchmarkHelper.TestItem.id} was not replaced.");
}
}

[Benchmark]
public async Task ReplaceItemStreamAsync()
{
using (MemoryStream ms = this.benchmarkHelper.GetItemPayloadAsStream())
using (ResponseMessage response = await this.container.ReplaceItemStreamAsync(
ms,
MockedItemBenchmarkHelper.ExistingItemId,
new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
this.requestOptions))
{
if (response.StatusCode != HttpStatusCode.OK)
{
throw new InvalidOperationException($"Item {this.benchmarkHelper.TestItem.id} was not replaced stream.");
}
}
}

[Benchmark]
public async Task DeleteItemAsync()
{
ItemResponse<ToDoActivity> itemResponse = await this.container.DeleteItemAsync<ToDoActivity>(
id: MockedItemBenchmarkHelper.ExistingItemId,
partitionKey: new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
requestOptions: this.requestOptions);

if (itemResponse.StatusCode != HttpStatusCode.OK)
{
throw new InvalidOperationException($"Item {MockedItemBenchmarkHelper.ExistingItemId} was not deleted.");
}
}

[Benchmark]
public async Task DeleteItemStreamAsync()
{
ResponseMessage response = await this.container.DeleteItemStreamAsync(
id: MockedItemBenchmarkHelper.ExistingItemId,
partitionKey: new PartitionKey(MockedItemBenchmarkHelper.ExistingItemId),
requestOptions: this.requestOptions);

if (response.StatusCode != HttpStatusCode.OK)
{
throw new InvalidOperationException($"Item {MockedItemBenchmarkHelper.ExistingItemId} was not deleted stream.");
}
}

[GlobalCleanup]
public void Cleanup()
{
// Restore the environment variable to its original value
Environment.SetEnvironmentVariable("COSMOS_ENABLE_BINARY_ENCODING", "false");
}

private class CustomBenchmarkConfig : ManualConfig
{
public CustomBenchmarkConfig()
{
this.AddColumn(StatisticColumn.OperationsPerSecond);
this.AddColumn(StatisticColumn.Q3);
this.AddColumn(StatisticColumn.P80);
this.AddColumn(StatisticColumn.P85);
this.AddColumn(StatisticColumn.P90);
this.AddColumn(StatisticColumn.P95);
this.AddColumn(StatisticColumn.P100);

this.AddDiagnoser(MemoryDiagnoser.Default);
this.AddDiagnoser(ThreadingDiagnoser.Default);
this.AddColumnProvider(DefaultConfig.Instance.GetColumnProviders().ToArray());

// Minimal run to reduce time
this.AddJob(Job.ShortRun
.WithStrategy(BenchmarkDotNet.Engines.RunStrategy.Throughput));

this.AddExporter(HtmlExporter.Default);
this.AddExporter(CsvExporter.Default);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ namespace Microsoft.Azure.Cosmos.Performance.Tests.Benchmarks
{
using System;
using System.IO;
using System.Text;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Cosmos.CosmosElements;
using Microsoft.Azure.Cosmos.Json;
using Microsoft.Azure.Cosmos.Json.Interop;
using Newtonsoft.Json;

/// <summary>
Expand All @@ -24,42 +27,63 @@ public class MockedItemBenchmarkHelper
internal ToDoActivity TestItem { get; }
internal CosmosClient TestClient { get; }
internal Container TestContainer { get; }

internal byte[] TestItemBytes { get; }
internal JsonSerializationFormat SerializationFormat { get; }

/// <summary>
/// Initializes a new instance of the <see cref="MockedItemBenchmark"/> class.
/// Initializes a new instance of the <see cref="MockedItemBenchmarkHelper"/> class.
/// </summary>
public MockedItemBenchmarkHelper(
internal MockedItemBenchmarkHelper(
bool useCustomSerializer = false,
bool includeDiagnosticsToString = false,
bool useBulk = false,
bool isDistributedTracingEnabled = false,
bool isClientMetricsEnabled = false)
bool isClientMetricsEnabled = false,
JsonSerializationFormat serializationFormat = JsonSerializationFormat.Text)
{
this.TestClient = MockDocumentClient.CreateMockCosmosClient(useCustomSerializer,
this.TestClient = MockDocumentClient.CreateMockCosmosClient(
useCustomSerializer,
(builder) => builder
.WithBulkExecution(useBulk)
.WithClientTelemetryOptions(new CosmosClientTelemetryOptions()
{
DisableDistributedTracing = !isDistributedTracingEnabled,
IsClientMetricsEnabled = isClientMetricsEnabled
}));
.WithBulkExecution(useBulk)
.WithClientTelemetryOptions(new CosmosClientTelemetryOptions()
{
DisableDistributedTracing = !isDistributedTracingEnabled,
IsClientMetricsEnabled = isClientMetricsEnabled
}));

this.TestContainer = this.TestClient.GetDatabase("myDB").GetContainer("myColl");
this.IncludeDiagnosticsToString = includeDiagnosticsToString;
this.SerializationFormat = serializationFormat;

// Load the test item from the JSON file
string payloadContent = File.ReadAllText("samplepayload.json");
this.TestItem = JsonConvert.DeserializeObject<ToDoActivity>(payloadContent);

using (FileStream tmp = File.OpenRead("samplepayload.json"))
using (MemoryStream ms = new MemoryStream())
// Serialize TestItem into the desired format (Text or Binary)
if (this.SerializationFormat == JsonSerializationFormat.Binary)
{
tmp.CopyTo(ms);
this.TestItemBytes = ms.ToArray();
using (CosmosDBToNewtonsoftWriter writer = new CosmosDBToNewtonsoftWriter(JsonSerializationFormat.Binary))
{
writer.Formatting = Newtonsoft.Json.Formatting.None;
Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Serialize(writer, this.TestItem);
this.TestItemBytes = writer.GetResult().ToArray();
}
}

using (MemoryStream ms = new MemoryStream(this.TestItemBytes))
else
{
string payloadContent = File.ReadAllText("samplepayload.json");
this.TestItem = JsonConvert.DeserializeObject<ToDoActivity>(payloadContent);
using (MemoryStream ms = new MemoryStream())
using (StreamWriter sw = new StreamWriter(ms, new UTF8Encoding(false, true), 1024, true))
using (Newtonsoft.Json.JsonWriter writer = new Newtonsoft.Json.JsonTextWriter(sw))
{
writer.Formatting = Newtonsoft.Json.Formatting.None;
Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Serialize(writer, this.TestItem);
writer.Flush();
sw.Flush();
this.TestItemBytes = ms.ToArray();
}
}
}

Expand All @@ -74,7 +98,7 @@ public void IncludeDiagnosticToStringHelper(
string diagnostics = cosmosDiagnostics.ToString();
if (string.IsNullOrEmpty(diagnostics))
{
throw new Exception();
throw new Exception("Diagnostics string is empty.");
}
}

Expand All @@ -88,4 +112,4 @@ public MemoryStream GetItemPayloadAsStream()
publiclyVisible: true);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,25 @@
"MockedItemBulkBenchmark.UpsertItem;[Type=OfTWithClientMetricsEnabled]": "1206121.5",
"MockedItemBulkBenchmark.UpsertItem;[Type=OfTWithDiagnosticsToString]": "1204822",
"MockedItemBulkBenchmark.UpsertItem;[Type=OfTWithDistributedTracingEnabled]": "1208600.25",
"MockedItemBulkBenchmark.UpsertItem;[Type=Stream]": "768928.75"
"MockedItemBulkBenchmark.UpsertItem;[Type=Stream]": "768928.75",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The values are same for both binary and non-binary flow correct ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes keeping the same values for all point operations for both binary encoding flag enabled and disabled.

"BinaryEncodingEnabledBenchmark.CreateItemAsync;[EnableBinaryResponseOnPointOperations=True]": "38127.25",
"BinaryEncodingEnabledBenchmark.CreateItemStreamAsync;[EnableBinaryResponseOnPointOperations=True]": "26697.5",
"BinaryEncodingEnabledBenchmark.ReadItemAsync;[EnableBinaryResponseOnPointOperations=True]": "34509.75",
"BinaryEncodingEnabledBenchmark.ReadItemStreamAsync;[EnableBinaryResponseOnPointOperations=True]": "26752.5",
"BinaryEncodingEnabledBenchmark.UpsertItemAsync;[EnableBinaryResponseOnPointOperations=True]": "38337.25",
"BinaryEncodingEnabledBenchmark.UpsertItemStreamAsync;[EnableBinaryResponseOnPointOperations=True]": "26919.25",
"BinaryEncodingEnabledBenchmark.ReplaceItemAsync;[EnableBinaryResponseOnPointOperations=True]": "38345",
"BinaryEncodingEnabledBenchmark.ReplaceItemStreamAsync;[EnableBinaryResponseOnPointOperations=True]": "26924.5",
"BinaryEncodingEnabledBenchmark.DeleteItemAsync;[EnableBinaryResponseOnPointOperations=True]": "33015.25",
"BinaryEncodingEnabledBenchmark.DeleteItemStreamAsync;[EnableBinaryResponseOnPointOperations=True]": "25829.5",
"BinaryEncodingEnabledBenchmark.CreateItemAsync;[EnableBinaryResponseOnPointOperations=False]": "38127.25",
"BinaryEncodingEnabledBenchmark.CreateItemStreamAsync;[EnableBinaryResponseOnPointOperations=False]": "26697.5",
"BinaryEncodingEnabledBenchmark.ReadItemAsync;[EnableBinaryResponseOnPointOperations=False]": "34509.75",
"BinaryEncodingEnabledBenchmark.ReadItemStreamAsync;[EnableBinaryResponseOnPointOperations=False]": "26752.5",
"BinaryEncodingEnabledBenchmark.UpsertItemAsync;[EnableBinaryResponseOnPointOperations=False]": "38337.25",
"BinaryEncodingEnabledBenchmark.UpsertItemStreamAsync;[EnableBinaryResponseOnPointOperations=False]": "26919.25",
"BinaryEncodingEnabledBenchmark.ReplaceItemAsync;[EnableBinaryResponseOnPointOperations=False]": "38345",
"BinaryEncodingEnabledBenchmark.ReplaceItemStreamAsync;[EnableBinaryResponseOnPointOperations=False]": "26924.5",
"BinaryEncodingEnabledBenchmark.DeleteItemAsync;[EnableBinaryResponseOnPointOperations=False]": "33015.25",
"BinaryEncodingEnabledBenchmark.DeleteItemStreamAsync;[EnableBinaryResponseOnPointOperations=False]": "25829.5"
}
Loading
Loading