Skip to content

Commit

Permalink
[Client encryption]: Add baseline benchmarks for `Microsoft.Azure.Cos…
Browse files Browse the repository at this point in the history
…mos.Encryption.Custom` (#4679)

# Pull Request Template

## Description

Adding a `Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests`
with `Encrypt`/`Decrypt` benchmarks for different document size.
Attaching also initial baseline report with the current memory
allocations.

## Type of change

Please delete options that are not relevant.

- [x] New feature (non-breaking change which adds functionality)

## Closing issues

Contributes to #4678

---------

Co-authored-by: Santosh Kulkarni <[email protected]>
Co-authored-by: Kiran Kumar Kolli <[email protected]>
  • Loading branch information
3 people authored Sep 27, 2024
1 parent c6d907c commit a883414
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Microsoft.Azure.Cosmos.Encryption.Custom/src/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.Encryption.Custom.Tests" + Microsoft.Azure.Cosmos.Encryption.Custom.AssemblyKeys.TestPublicKey)]
[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests" + Microsoft.Azure.Cosmos.Encryption.Custom.AssemblyKeys.TestPublicKey)]
[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.Encryption.Custom.EmulatorTests" + Microsoft.Azure.Cosmos.Encryption.Custom.AssemblyKeys.TestPublicKey)]
[assembly: InternalsVisibleTo("Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests" + Microsoft.Azure.Cosmos.Encryption.Custom.AssemblyKeys.TestPublicKey)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
namespace Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests
{
using System.IO;
using BenchmarkDotNet.Attributes;
using Microsoft.Data.Encryption.Cryptography;
using Moq;

[RPlotExporter]
public partial class EncryptionBenchmark
{
private static readonly byte[] DekData = Enumerable.Repeat((byte)0, 32).ToArray();
private static readonly DataEncryptionKeyProperties DekProperties = new(
"id",
CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized,
DekData,
new EncryptionKeyWrapMetadata("name", "value"), DateTime.UtcNow);
private static readonly Mock<EncryptionKeyStoreProvider> StoreProvider = new();

private CosmosEncryptor? encryptor;

private EncryptionOptions? encryptionOptions;
private byte[]? encryptedData;
private byte[]? plaintext;

[Params(1, 10, 100)]
public int DocumentSizeInKb { get; set; }

[GlobalSetup]
public async Task Setup()
{
StoreProvider
.Setup(x => x.UnwrapKey(It.IsAny<string>(), It.IsAny<KeyEncryptionKeyAlgorithm>(), It.IsAny<byte[]>()))
.Returns(DekData);

Mock<DataEncryptionKeyProvider> keyProvider = new();
keyProvider
.Setup(x => x.FetchDataEncryptionKeyWithoutRawKeyAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(() => new MdeEncryptionAlgorithm(DekProperties, EncryptionType.Randomized, StoreProvider.Object, cacheTimeToLive: TimeSpan.MaxValue));

this.encryptor = new(keyProvider.Object);
this.encryptionOptions = CreateEncryptionOptions();
this.plaintext = this.LoadTestDoc();

Stream encryptedStream = await EncryptionProcessor.EncryptAsync(
new MemoryStream(this.plaintext),
this.encryptor,
this.encryptionOptions,
new CosmosDiagnosticsContext(),
CancellationToken.None);

using MemoryStream memoryStream = new MemoryStream();
encryptedStream.CopyTo(memoryStream);
this.encryptedData = memoryStream.ToArray();
}

[Benchmark]
public async Task Encrypt()
{
await EncryptionProcessor.EncryptAsync(
new MemoryStream(this.plaintext!),
this.encryptor,
this.encryptionOptions,
new CosmosDiagnosticsContext(),
CancellationToken.None);
}

[Benchmark]
public async Task Decrypt()
{
await EncryptionProcessor.DecryptAsync(
new MemoryStream(this.encryptedData!),
this.encryptor,
new CosmosDiagnosticsContext(),
CancellationToken.None);
}

private static EncryptionOptions CreateEncryptionOptions()
{
EncryptionOptions options = new()
{
DataEncryptionKeyId = "dekId",
EncryptionAlgorithm = CosmosEncryptionAlgorithm.MdeAeadAes256CbcHmac256Randomized,
PathsToEncrypt = TestDoc.PathsToEncrypt
};

return options;
}

private byte[] LoadTestDoc()
{
string name = $"Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests.sampledata.testdoc-{this.DocumentSizeInKb}kb.json";
using Stream resourceStream = typeof(EncryptionBenchmark).Assembly.GetManifestResourceStream(name)!;

byte[] buffer = new byte[resourceStream!.Length];
resourceStream.Read(buffer, 0, buffer.Length);

return buffer;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AssemblyName>Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests</AssemblyName>
<OutputType>Exe</OutputType>
<TargetFrameworks>net6</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<EmbeddedResource Include="sampledata\testdoc-100kb.json" />
<EmbeddedResource Include="sampledata\testdoc-10kb.json" />
<EmbeddedResource Include="sampledata\testdoc-1kb.json" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.3" />
<PackageReference Include="Moq" Version="4.13.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.Azure.Cosmos.Encryption.Custom.csproj" />
</ItemGroup>

<PropertyGroup>
<SignAssembly>true</SignAssembly>
<DelaySign>true</DelaySign>
<AssemblyOriginatorKeyFile>..\..\..\testkey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests
{
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains.InProcess.Emit;

internal class Program
{
public static void Main(string[] args)
{
ManualConfig dontRequireSlnToRunBenchmarks = ManualConfig
.Create(DefaultConfig.Instance)
.AddJob(Job.MediumRun.WithToolchain(InProcessEmitToolchain.Instance))
.AddDiagnoser(MemoryDiagnoser.Default);

BenchmarkRunner.Run<EncryptionBenchmark>(dontRequireSlnToRunBenchmarks, args);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
``` ini

BenchmarkDotNet=v0.13.3, OS=Windows 11 (10.0.22631.4169)
11th Gen Intel Core i9-11950H 2.60GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=9.0.100-rc.1.24452.12
[Host] : .NET 6.0.33 (6.0.3324.36610), X64 RyuJIT AVX2

Job=MediumRun Toolchain=InProcessEmitToolchain IterationCount=15
LaunchCount=2 WarmupCount=10

```
| Method | DocumentSizeInKb | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|-------- |----------------- |------------:|-----------:|-----------:|---------:|---------:|---------:|-----------:|
| **Encrypt** | **1** | **61.45 μs** | **1.676 μs** | **2.457 μs** | **4.9438** | **1.2207** | **-** | **61.25 KB** |
| Decrypt | 1 | 77.89 μs | 1.959 μs | 2.933 μs | 5.7373 | 1.4648 | - | 71.22 KB |
| **Encrypt** | **10** | **171.64 μs** | **3.341 μs** | **4.791 μs** | **21.2402** | **3.6621** | **-** | **260.97 KB** |
| Decrypt | 10 | 255.57 μs | 7.833 μs | 11.724 μs | 29.2969 | 4.3945 | - | 363.84 KB |
| **Encrypt** | **100** | **2,601.33 μs** | **215.481 μs** | **322.522 μs** | **199.2188** | **125.0000** | **123.0469** | **2464.88 KB** |
| Decrypt | 100 | 3,156.06 μs | 321.419 μs | 481.084 μs | 355.4688 | 300.7813 | 261.7188 | 3413.05 KB |
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace Microsoft.Azure.Cosmos.Encryption.Custom.Performance.Tests
{
using System.Text;
using Newtonsoft.Json;

public partial class EncryptionBenchmark
{
internal class TestDoc
{
public static List<string> PathsToEncrypt { get; } = new List<string>() { "/SensitiveStr", "/SensitiveInt", "/SensitiveDict" };

[JsonProperty("id")]
public string Id { get; set; }

public string NonSensitive { get; set; }

public string SensitiveStr { get; set; }

public int SensitiveInt { get; set; }

public Dictionary<string, string> SensitiveDict { get; set; }

public TestDoc()
{
}

public static TestDoc Create(int approximateSize = -1)
{
return new TestDoc()
{
Id = Guid.NewGuid().ToString(),
NonSensitive = Guid.NewGuid().ToString(),
SensitiveStr = Guid.NewGuid().ToString(),
SensitiveInt = new Random().Next(),
SensitiveDict = GenerateBigDictionary(approximateSize),
};
}

private static Dictionary<string, string> GenerateBigDictionary(int approximateSize)
{
const int stringSize = 100;
int items = Math.Max(1, approximateSize / stringSize);

return Enumerable.Range(1, items).ToDictionary(x => x.ToString(), y => GenerateRandomString(stringSize));
}

private static string GenerateRandomString(int size)
{
Random rnd = new Random();
const string characters = "abcdefghijklmnopqrstuvwxyz0123456789";

StringBuilder sb = new();
for (int i = 0; i < size; i++)
{
sb.Append(characters[rnd.Next(0, characters.Length)]);
}
return sb.ToString();
}
}

}
}

Large diffs are not rendered by default.

Loading

0 comments on commit a883414

Please sign in to comment.