Method | Mean | Error | StdDev |
---|---|---|---|
InMemory_Blob | 1,860.0 us | 25.45 us | 21.25 us |
InMemory_Queue | 1,562.3 us | 26.89 us | 25.15 us |
InMemory_Table | 975.7 us | 8.65 us | 7.23 us |
Disk_Blob | 5,505.8 us | 91.37 us | 89.74 us |
Disk_Queue | 5,327.1 us | 104.47 us | 111.78 us |
Disk_Table | 963.6 us | 11.34 us | 9.47 us |
Last active
October 17, 2023 17:18
-
-
Save joelverhagen/8394b3eaa276f4baa07806867b5caa37 to your computer and use it in GitHub Desktop.
Performance test for in-memory Azurite
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Azure.Data.Tables; | |
using Azure.Storage.Blobs; | |
using Azure.Storage.Queues; | |
using Azure.Storage.Queues.Models; | |
using BenchmarkDotNet.Attributes; | |
using BenchmarkDotNet.Extensions; | |
using System.Diagnostics; | |
using System.Net; | |
using System.Text; | |
// var summary = BenchmarkRunner.Run<AzuriteInMemoryVsDisk>(); | |
var test = new AzuriteInMemoryVsDisk(); | |
await test.InMemory_Setup(); | |
var blobData = new byte[64 * 1024 * 1024]; // 64 MiB | |
try | |
{ | |
var blobServiceClient = new BlobServiceClient("UseDevelopmentStorage=true"); | |
var blobContainer = blobServiceClient.GetBlobContainerClient("testcontainer"); | |
await blobContainer.CreateIfNotExistsAsync(); | |
using var process = Process.GetProcessesByName("node").OrderByDescending(x => x.StartTime).First(); | |
var instanceName = GetProcessInstanceName(process.Id); | |
using var counter = new PerformanceCounter("Process", "Working Set - Private", instanceName); | |
var i = 0; | |
long total = 0; | |
while (true) | |
{ | |
var blob = blobContainer.GetBlobClient($"blob{i:D6}.dat"); | |
total += blobData.Length; | |
Console.Write($"Uploading 64 MiB to {blob.Name}..."); | |
var sw = Stopwatch.StartNew(); | |
await blob.UploadAsync(new MemoryStream(blobData)); | |
sw.Stop(); | |
var memory = Convert.ToInt64(counter.NextValue()); | |
Console.WriteLine($" done in {sw.ElapsedMilliseconds}ms. [node process: {memory / (1024.0 * 1024 * 1024):0.00} GiB, uploaded: {total / (1024.0 * 1024 * 1024):0.00} GiB]"); | |
i++; | |
} | |
} | |
finally | |
{ | |
test.InMemory_Cleanup(); | |
} | |
// Source: https://stackoverflow.com/a/9115662 | |
static string GetProcessInstanceName(int pid) | |
{ | |
PerformanceCounterCategory cat = new PerformanceCounterCategory("Process"); | |
string[] instances = cat.GetInstanceNames(); | |
foreach (string instance in instances) | |
{ | |
using (PerformanceCounter cnt = new PerformanceCounter("Process", | |
"ID Process", instance, true)) | |
{ | |
int val = (int)cnt.RawValue; | |
if (val == pid) | |
{ | |
return instance; | |
} | |
} | |
} | |
throw new Exception("Could not find performance counter " + | |
"instance name for current process. This is truly strange ..."); | |
} | |
public class AzuriteInMemoryVsDisk | |
{ | |
private static readonly byte[] Data = Encoding.UTF8.GetBytes(new string('a', 8 * 1024)); | |
private Process? _azuriteProcess; | |
private BlobServiceClient? _blobServiceClient; | |
private BlobContainerClient? _blobContainer; | |
private QueueServiceClient? _queueServiceClient; | |
private QueueClient? _queue; | |
private TableServiceClient? _tableServiceClient; | |
private TableClient? _table; | |
private async Task InitializeAzuriteAsync(string[] arguments) | |
{ | |
await StartAzuriteAsync(arguments); | |
await InitializeClientsAsync(); | |
} | |
private async Task InitializeClientsAsync() | |
{ | |
var connectionString = "UseDevelopmentStorage=true"; | |
_blobServiceClient = new BlobServiceClient(connectionString); | |
_blobContainer = _blobServiceClient.GetBlobContainerClient("testcontainer"); | |
await _blobContainer.CreateIfNotExistsAsync(); | |
_queueServiceClient = new QueueServiceClient(connectionString); | |
_queue = _queueServiceClient.GetQueueClient("testqueue"); | |
await _queue.CreateIfNotExistsAsync(); | |
_tableServiceClient = new TableServiceClient(connectionString); | |
_table = _tableServiceClient.GetTableClient("testtable"); | |
await _table.CreateIfNotExistsAsync(); | |
} | |
private async Task StartAzuriteAsync(string[] arguments) | |
{ | |
try | |
{ | |
_azuriteProcess = new Process | |
{ | |
StartInfo = | |
{ | |
WorkingDirectory = @"C:\z\Git\joelverhagen\Azurite\", | |
FileName = "npm", | |
ArgumentList = { "run", "azurite", "--" }, | |
UseShellExecute = true, | |
}, | |
}; | |
foreach (var arg in arguments) | |
{ | |
_azuriteProcess.StartInfo.ArgumentList.Add(arg); | |
} | |
Console.WriteLine("Starting Azurite..."); | |
_azuriteProcess.Start(); | |
var sw = Stopwatch.StartNew(); | |
using var httpClient = new HttpClient(); | |
while (sw.Elapsed < TimeSpan.FromSeconds(30)) | |
{ | |
await Task.Delay(TimeSpan.FromSeconds(1)); | |
var ready = true; | |
foreach (var port in Enumerable.Range(10000, 3)) | |
{ | |
using var request = new HttpRequestMessage(HttpMethod.Head, $"http://127.0.0.1:{port}/"); | |
try | |
{ | |
using var response = await httpClient.SendAsync(request); | |
if (response.StatusCode != HttpStatusCode.BadRequest) | |
{ | |
response.EnsureSuccessStatusCode(); | |
} | |
Console.WriteLine($"Port {port} is ready."); | |
} | |
catch | |
{ | |
Console.WriteLine($"Port {port} is not ready yet."); | |
ready = false; | |
break; | |
} | |
} | |
if (ready) | |
{ | |
break; | |
} | |
} | |
} | |
catch | |
{ | |
try | |
{ | |
_azuriteProcess?.Kill(entireProcessTree: true); | |
} | |
catch | |
{ | |
// best effort | |
} | |
throw; | |
} | |
} | |
private void CleanupAzurite() | |
{ | |
_azuriteProcess?.KillTree(); | |
} | |
[GlobalSetup(Targets = new[] { nameof(InMemory_Blob), nameof(InMemory_Queue), nameof(InMemory_Table) })] | |
public async Task InMemory_Setup() | |
{ | |
var arguments = new[] { "--inMemoryPersistence", "--silent" }; | |
await InitializeAzuriteAsync(arguments); | |
} | |
[GlobalCleanup(Targets = new[] { nameof(InMemory_Blob), nameof(InMemory_Queue), nameof(InMemory_Table) })] | |
public void InMemory_Cleanup() | |
{ | |
CleanupAzurite(); | |
} | |
[GlobalSetup(Targets = new[] { nameof(Disk_Blob), nameof(Disk_Queue), nameof(Disk_Table) })] | |
public async Task Disk_Setup() | |
{ | |
var arguments = new[] { "--silent" }; | |
await InitializeAzuriteAsync(arguments); | |
} | |
[GlobalCleanup(Targets = new[] { nameof(Disk_Blob), nameof(Disk_Queue), nameof(Disk_Table) })] | |
public void Disk_Cleanup() | |
{ | |
CleanupAzurite(); | |
} | |
[Benchmark] | |
public async Task InMemory_Blob() | |
{ | |
await Blob(); | |
} | |
[Benchmark] | |
public async Task InMemory_Queue() | |
{ | |
await Queue(); | |
} | |
[Benchmark] | |
public async Task InMemory_Table() | |
{ | |
await Table(); | |
} | |
[Benchmark] | |
public async Task Disk_Blob() | |
{ | |
await Blob(); | |
} | |
[Benchmark] | |
public async Task Disk_Queue() | |
{ | |
await Queue(); | |
} | |
[Benchmark] | |
public async Task Disk_Table() | |
{ | |
await Table(); | |
} | |
private async Task Blob() | |
{ | |
var blob = _blobContainer!.GetBlobClient("testblob"); | |
await blob.UploadAsync(new BinaryData(Data), overwrite: true); | |
await blob.SetMetadataAsync(new Dictionary<string, string> { { "foo", "bar" } }); | |
await blob.DeleteAsync(); | |
} | |
private async Task Queue() | |
{ | |
await _queue!.SendMessageAsync(new BinaryData(Data)); | |
QueueMessage message = await _queue.ReceiveMessageAsync(); | |
await _queue.DeleteMessageAsync(message.MessageId, message.PopReceipt); | |
} | |
private async Task Table() | |
{ | |
var entity = new TableEntity("pk", "rk"); | |
entity["Data"] = Data; | |
await _table!.UpsertEntityAsync(entity); | |
var results = _table.QueryAsync<TableEntity>(x => x.PartitionKey == "pk"); | |
await foreach (var x in results) | |
{ | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment