close
Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 81 additions & 57 deletions performance/benchmark/Descriptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
Expand All @@ -15,88 +15,112 @@
[ShortRunJob]
public class Descriptions
{
[Benchmark]
public async Task<OpenApiDocument> PetStoreYaml()
private enum DescriptionSource
{
return await ParseDocumentAsync(PetStoreYamlPath);
PetStoreYaml,
PetStoreJson,
GHESYaml,
GHESJson,
GHESNextYaml,
GHESNextJson
}

[Benchmark]
public async Task<OpenApiDocument> PetStoreJson()
{
return await ParseDocumentAsync(PetStoreJsonPath, OpenApiConstants.Json);
}
public Task<OpenApiDocument> PetStoreYaml() => ParseDocumentAsync(DescriptionSource.PetStoreYaml);

[Benchmark]
public async Task<OpenApiDocument> GHESYaml()
{
return await ParseDocumentAsync(GHESYamlDescriptionUrl);
}
public Task<OpenApiDocument> PetStoreJson() => ParseDocumentAsync(DescriptionSource.PetStoreJson, OpenApiConstants.Json);

[Benchmark]
public async Task<OpenApiDocument> GHESJson()
{
return await ParseDocumentAsync(GHESJsonDescriptionUrl, OpenApiConstants.Json);
}
public Task<OpenApiDocument> GHESYaml() => ParseDocumentAsync(DescriptionSource.GHESYaml);

[Benchmark]
public async Task<OpenApiDocument> GHESNextYaml()
{
return await ParseDocumentAsync(GHESNextYamlDescriptionUrl);
}
public Task<OpenApiDocument> GHESJson() => ParseDocumentAsync(DescriptionSource.GHESJson, OpenApiConstants.Json);

[Benchmark]
public async Task<OpenApiDocument> GHESNextJson()
{
return await ParseDocumentAsync(GHESNextJsonDescriptionUrl, OpenApiConstants.Json);
}
private readonly Dictionary<string, MemoryStream> _streams = new(StringComparer.OrdinalIgnoreCase);
public Task<OpenApiDocument> GHESNextYaml() => ParseDocumentAsync(DescriptionSource.GHESNextYaml);

[Benchmark]
public Task<OpenApiDocument> GHESNextJson() => ParseDocumentAsync(DescriptionSource.GHESNextJson, OpenApiConstants.Json);

private readonly Dictionary<DescriptionSource, MemoryStream> _streams = new(capacity: 6);

[GlobalSetup]
public async Task GetAllDescriptions()
{
_httpClient = new HttpClient();
readerSettings = new OpenApiReaderSettings
_httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };
_readerSettings = new OpenApiReaderSettings { LeaveStreamOpen = true };
_readerSettings.AddYamlReader();

var results = await Task.WhenAll(
LoadFromAssemblyAsync(DescriptionSource.PetStoreYaml, PetStoreYamlResourceName),
LoadFromAssemblyAsync(DescriptionSource.PetStoreJson, PetStoreJsonResourceName),
LoadFromUrlAsync(DescriptionSource.GHESYaml, GHESYamlDescriptionUrl),
LoadFromUrlAsync(DescriptionSource.GHESJson, GHESJsonDescriptionUrl),
LoadFromUrlAsync(DescriptionSource.GHESNextYaml, GHESNextYamlDescriptionUrl),
LoadFromUrlAsync(DescriptionSource.GHESNextJson, GHESNextJsonDescriptionUrl)
).ConfigureAwait(false);

foreach (var (source, stream) in results)
{
LeaveStreamOpen = true,
};
readerSettings.AddYamlReader();
await LoadDocumentFromAssemblyIntoStreams(PetStoreYamlPath);
await LoadDocumentFromAssemblyIntoStreams(PetStoreJsonPath);
await LoadDocumentFromUrlIntoStreams(GHESYamlDescriptionUrl);
await LoadDocumentFromUrlIntoStreams(GHESJsonDescriptionUrl);
await LoadDocumentFromUrlIntoStreams(GHESNextYamlDescriptionUrl);
await LoadDocumentFromUrlIntoStreams(GHESNextJsonDescriptionUrl);
_streams.Add(source, stream);
}
}
private OpenApiReaderSettings readerSettings;
private const string PetStoreYamlPath = @"petStore.yaml";
private const string PetStoreJsonPath = @"petStore.json";
private const string GHESYamlDescriptionUrl = @"https://raw.githubusercontent.com/github/rest-api-description/aef5e31a2d10fdaab311ec6d18a453021a81383d/descriptions/ghes-3.16/ghes-3.16.2022-11-28.yaml";
private const string GHESJsonDescriptionUrl = @"https://raw.githubusercontent.com/github/rest-api-description/aef5e31a2d10fdaab311ec6d18a453021a81383d/descriptions/ghes-3.16/ghes-3.16.2022-11-28.json";
private const string GHESNextYamlDescriptionUrl = @"https://raw.githubusercontent.com/github/rest-api-description/aef5e31a2d10fdaab311ec6d18a453021a81383d/descriptions-next/ghes-3.16/ghes-3.16.2022-11-28.yaml";
private const string GHESNextJsonDescriptionUrl = @"https://raw.githubusercontent.com/github/rest-api-description/aef5e31a2d10fdaab311ec6d18a453021a81383d/descriptions-next/ghes-3.16/ghes-3.16.2022-11-28.json";
private async Task<OpenApiDocument> ParseDocumentAsync(string fileName, string format = null)

private OpenApiReaderSettings _readerSettings;

private const string PetStoreYamlResourceName = "petStore.yaml";
private const string PetStoreJsonResourceName = "petStore.json";

private const string GHESRepoCommitSha = "aef5e31a2d10fdaab311ec6d18a453021a81383d";
private const string GHESReleaseVersion = "ghes-3.16";
private const string GHESDescriptionFileName = "ghes-3.16.2022-11-28";

// Building the four GHES URLs from shared constants means the pinned commit only needs to
// change in one place when the benchmark data set is refreshed, instead of four near-identical
// literals that can silently drift out of sync with each other.
private static string BuildGHESDescriptionUrl(string descriptionsFolder, string extension) =>
$"https://raw.githubusercontent.com/github/rest-api-description/{GHESRepoCommitSha}/{descriptionsFolder}/{GHESReleaseVersion}/{GHESDescriptionFileName}.{extension}";

private static readonly string GHESYamlDescriptionUrl = BuildGHESDescriptionUrl("descriptions", "yaml");
private static readonly string GHESJsonDescriptionUrl = BuildGHESDescriptionUrl("descriptions", "json");
private static readonly string GHESNextYamlDescriptionUrl = BuildGHESDescriptionUrl("descriptions-next", "yaml");
private static readonly string GHESNextJsonDescriptionUrl = BuildGHESDescriptionUrl("descriptions-next", "json");

private async Task<OpenApiDocument> ParseDocumentAsync(DescriptionSource source, string format = null)
{
format ??= OpenApiConstants.Yaml;
var stream = _streams[fileName];
var stream = _streams[source];
stream.Seek(0, SeekOrigin.Begin);
var (document, _) = await OpenApiDocument.LoadAsync(stream, format, readerSettings).ConfigureAwait(false);

var (document, _) = await OpenApiDocument.LoadAsync(stream, format, _readerSettings).ConfigureAwait(false);
return document;
}

private HttpClient _httpClient;
private async Task LoadDocumentFromUrlIntoStreams(string url)

private async Task<(DescriptionSource Source, MemoryStream Stream)> LoadFromUrlAsync(DescriptionSource source, string url)
{
var response = await _httpClient.GetAsync(url).ConfigureAwait(false);
using var response = await _httpClient.GetAsync(url).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var stream = new MemoryStream(); // NOT disposed on purpose

var stream = new MemoryStream();
await response.Content.CopyToAsync(stream).ConfigureAwait(false);
stream.Seek(0, SeekOrigin.Begin);
_streams.Add(url, stream);
return (source, stream);
}
private static readonly Assembly assembly = typeof(Descriptions).GetTypeInfo().Assembly;
private async Task LoadDocumentFromAssemblyIntoStreams(string fileName)

private static readonly Assembly _assembly = typeof(Descriptions).Assembly;

private async Task<(DescriptionSource Source, MemoryStream Stream)> LoadFromAssemblyAsync(DescriptionSource source, string resourceFileName)

Check warning on line 115 in performance/benchmark/Descriptions.cs

View check run for this annotation

Image SonarQubeCloud / SonarCloud Code Analysis

Make 'LoadFromAssemblyAsync' a static method.

See more on https://sonarcloud.io/project/issues?id=microsoft_OpenAPI.NET&issues=AaAflOYfIppGDUFpVp81&open=AaAflOYfIppGDUFpVp81&pullRequest=3047
{
using var resource = assembly.GetManifestResourceStream($"PerformanceTests.{fileName}");
var stream = new MemoryStream(); // NOT disposed on purpose
await resource.CopyToAsync(stream).ConfigureAwait(false);
using var resourceStream = _assembly.GetManifestResourceStream($"PerformanceTests.{resourceFileName}");
var stream = new MemoryStream();
await resourceStream.CopyToAsync(stream).ConfigureAwait(false);
stream.Seek(0, SeekOrigin.Begin);
_streams.Add(fileName, stream);
return (source, stream);
}

[GlobalCleanup]
public void Cleanup()
{
Expand Down
Loading