-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This adds the basic infrastructure required to support benchmarks. This is not meant as a comprehensive set of benchmarks. Resolves #7
- Loading branch information
Showing
14 changed files
with
1,397 additions
and
38 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
This folder contains data under the Norwegian licence for Open Government data (NLOD) distributed by | ||
the Norwegian Costal Administration - https://ais.kystverket.no/ | ||
The license can be found at https://data.norge.no/nlod/en/2.0 |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<Import Project="..\Common.NetCore_2_2.proj" /> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<IsPackable>false</IsPackable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Content Include="..\..\ExampleData\ais.kystverket.no\Ais1000Lines.nm4" Link="TestData\Ais1000Lines.nm4"> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
</Content> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Ais.Net\Ais.Net.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="TestData\" /> | ||
</ItemGroup> | ||
|
||
</Project> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// <copyright file="AisNetBenchmarks.cs" company="Endjin Limited"> | ||
// Copyright (c) Endjin Limited. All rights reserved. | ||
// </copyright> | ||
|
||
namespace Ais.Net.Benchmarks | ||
{ | ||
using System.IO; | ||
using System.Threading.Tasks; | ||
using BenchmarkDotNet.Attributes; | ||
|
||
/// <summary> | ||
/// Defines all of the benchmarks and global setup/teardown. | ||
/// </summary> | ||
[JsonExporterAttribute.Full] | ||
public class AisNetBenchmarks | ||
{ | ||
private const string TestPath1kLines = "TestData/Ais1000Lines.nm4"; | ||
private const string TestPath1mLines = "TestData/Ais1000000Lines.nm4"; | ||
|
||
/// <summary> | ||
/// Invoked by BenchmarkDotNet before running all benchmarks. | ||
/// </summary> | ||
[GlobalSetup] | ||
public void GlobalSetup() | ||
{ | ||
// We have 1000 lines of real test data to provide a realistic mix of messages. | ||
// However, this is too small to get a good measurement of the per-message overhead, | ||
// because the per-execution overheads are a significant proportion of the whole at | ||
// that size. | ||
// For example, on an Intel Core i9-9900K CPU 3.60GHz, the InspectMessageType test | ||
// processes a 1000-message file in about 510us, suggesting a per-message cost of | ||
// about 510ns. However, if we run the exact same test against a 1,000,000 message | ||
// file, it takes about 340ms, suggesting a per-message cost of just 340ns. So by | ||
// measuring over 1,000 messages, we get a reading that's 50% higher than we do at | ||
// 1M. (We get similar results at 10M, so 1M seems to be sufficient. 100K might also | ||
// be enough, but it's easier to read the results when we multiple things up three | ||
// orders of magnitude at a time: it means that test times in ms correspond to | ||
// per-message times in ns.) | ||
string[] testFileLines = File.ReadAllLines(TestPath1kLines); | ||
using (var f = new StreamWriter(TestPath1mLines)) | ||
{ | ||
for (int i = 0; i < 1000; ++i) | ||
{ | ||
foreach (string line in testFileLines) | ||
{ | ||
f.WriteLine(line); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Invoked by BenchmarkDotNet after running all benchmarks. | ||
/// </summary> | ||
[GlobalCleanup] | ||
public void GlobalCleanup() | ||
{ | ||
File.Delete(TestPath1mLines); | ||
} | ||
|
||
/// <summary> | ||
/// Benchmark: measure the speed at which we can perform the most minimal amount of | ||
/// processing of messages in a file. | ||
/// </summary> | ||
/// <returns>A task that completes when the benchmark has finished.</returns> | ||
[Benchmark] | ||
public Task InspectMessageTypesFromNorwayFile1M() => InspectMessageType.ProcessMessagesFromFile(TestPath1mLines); | ||
|
||
/// <summary> | ||
/// Benchmark: measure the speed at which we can read location data from message in a file. | ||
/// </summary> | ||
/// <returns>A task that completes when the benchmark has finished.</returns> | ||
[Benchmark] | ||
public Task ReadPositionsFromNorwayFile1M() => ReadAllPositions.ProcessMessagesFromFile(TestPath1mLines); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// <copyright file="InspectMessageType.cs" company="Endjin Limited"> | ||
// Copyright (c) Endjin Limited. All rights reserved. | ||
// </copyright> | ||
|
||
namespace Ais.Net.Benchmarks | ||
{ | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
/// <summary> | ||
/// Benchmark that measures how quickly we can read messages from a file and discover their | ||
/// types. | ||
/// </summary> | ||
internal static class InspectMessageType | ||
{ | ||
private static readonly TestProcessor Processor = new TestProcessor(); | ||
|
||
/// <summary> | ||
/// Execute the benchmark. | ||
/// </summary> | ||
/// <param name="path">The file from which to read messages.</param> | ||
/// <returns>A task that completes when the benchmark has finished.</returns> | ||
public static async Task ProcessMessagesFromFile(string path) | ||
{ | ||
await NmeaStreamParser.ParseFileAsync(path, Processor).ConfigureAwait(false); | ||
} | ||
|
||
private class TestProcessor : INmeaAisMessageStreamProcessor | ||
{ | ||
private readonly int[] messageTypeCounts = new int[30]; | ||
|
||
public void OnCompleted() | ||
{ | ||
} | ||
|
||
public void OnNext(in NmeaLineParser firstLine, in ReadOnlySpan<byte> asciiPayload, uint padding) | ||
{ | ||
int type = NmeaPayloadParser.PeekMessageType(asciiPayload, padding); | ||
if (type < this.messageTypeCounts.Length) | ||
{ | ||
this.messageTypeCounts[type] += 1; | ||
} | ||
} | ||
|
||
public void Progress(bool done, int totalNmeaLines, int totalAisMessages, int totalTicks, int nmeaLinesSinceLastUpdate, int aisMessagesSinceLastUpdate, int ticksSinceLastUpdate) | ||
{ | ||
} | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// <copyright file="Program.cs" company="Endjin Limited"> | ||
// Copyright (c) Endjin Limited. All rights reserved. | ||
// </copyright> | ||
|
||
namespace Ais.Net.Benchmarks | ||
{ | ||
using System.IO; | ||
using BenchmarkDotNet.Configs; | ||
using BenchmarkDotNet.Diagnosers; | ||
using BenchmarkDotNet.Jobs; | ||
using BenchmarkDotNet.Running; | ||
|
||
/// <summary> | ||
/// Program entry point type. | ||
/// </summary> | ||
internal static class Program | ||
{ | ||
/// <summary> | ||
/// Program entry point. | ||
/// </summary> | ||
/// <param name="args">Command line arguments.</param> | ||
/// <remarks> | ||
/// <p> | ||
/// When running in an Azure DevOps pipeline, a couple of things can go wrong with BenchmarkDotNet. | ||
/// First, by default it puts its output into a path relative to the current working directory, | ||
/// which will often be some way further up the folder hierarchy than we want. Second, the components | ||
/// under test will have been built with a /p:Version=x.y.z argument, setting the version number to | ||
/// whatever the build process has determined it should be. That can be problematic because | ||
/// BenchmarkDotNet rebuilds various elements for each benchmark, meaning everything would default back | ||
/// to v1.0.0.0. However, that seems to cause problems because the hosting benchmark project will | ||
/// have been build against the correct version number. This shouldn't be a problem because the | ||
/// benchmarks all run isolated, but weirdly, we get an error *after* the benchmarking is complete, | ||
/// causing this hosting program to exit with an error. | ||
/// </p> | ||
/// <p> | ||
/// To fix these problems, this application accepts two command line arguments. If present, they | ||
/// set the path of the folder into which to write results, and the version number to be used when | ||
/// rebuilding things. | ||
/// </p> | ||
/// </remarks> | ||
private static void Main(string[] args) | ||
{ | ||
IConfig config = DefaultConfig.Instance.With(MemoryDiagnoser.Default); | ||
if (args.Length > 0) | ||
{ | ||
string artifactsPath = args[0]; | ||
Directory.CreateDirectory(artifactsPath); | ||
config = config.WithArtifactsPath(artifactsPath); | ||
} | ||
|
||
if (args.Length > 1) | ||
{ | ||
string version = args[1]; | ||
config = config.With(Job.Default.With(new Argument[] { new MsBuildArgument($"/p:Version={version}") })); | ||
} | ||
|
||
BenchmarkRunner.Run<AisNetBenchmarks>(config); | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// <copyright file="ReadAllPositions.cs" company="Endjin Limited"> | ||
// Copyright (c) Endjin Limited. All rights reserved. | ||
// </copyright> | ||
|
||
namespace Ais.Net.Benchmarks | ||
{ | ||
using System; | ||
using System.Threading.Tasks; | ||
|
||
/// <summary> | ||
/// Benchmark that measures how quickly we can read messages from a file and read out any | ||
/// location data they contain. | ||
/// </summary> | ||
internal static class ReadAllPositions | ||
{ | ||
private static readonly StatsScanner Processor = new StatsScanner(); | ||
|
||
/// <summary> | ||
/// Execute the benchmark. | ||
/// </summary> | ||
/// <param name="path">The file from which to read messages.</param> | ||
/// <returns>A task that completes when the benchmark has finished.</returns> | ||
public static async Task ProcessMessagesFromFile(string path) | ||
{ | ||
await NmeaStreamParser.ParseFileAsync(path, Processor).ConfigureAwait(false); | ||
} | ||
|
||
private class StatsScanner : INmeaAisMessageStreamProcessor | ||
{ | ||
public long SummedLongs { get; private set; } = 0; | ||
|
||
public long SummedLats { get; private set; } = 0; | ||
|
||
public int PositionsCount { get; private set; } = 0; | ||
|
||
/// <inheritdoc/> | ||
public void OnNext( | ||
in NmeaLineParser firstLine, | ||
in ReadOnlySpan<byte> asciiPayload, | ||
uint padding) | ||
{ | ||
int messageType = NmeaPayloadParser.PeekMessageType(asciiPayload, padding); | ||
if (messageType >= 1 && messageType <= 3) | ||
{ | ||
var parsedPosition = new NmeaAisPositionReportClassAParser(asciiPayload, padding); | ||
AddPosition(parsedPosition.Latitude10000thMins, parsedPosition.Longitude10000thMins); | ||
} | ||
else if (messageType == 18) | ||
{ | ||
var parsedPosition = new NmeaAisPositionReportClassBParser(asciiPayload, padding); | ||
AddPosition(parsedPosition.Latitude10000thMins, parsedPosition.Longitude10000thMins); | ||
} | ||
else if (messageType == 19) | ||
{ | ||
var parsedPosition = new NmeaAisPositionReportExtendedClassBParser(asciiPayload, padding); | ||
AddPosition(parsedPosition.Latitude10000thMins, parsedPosition.Longitude10000thMins); | ||
} | ||
|
||
void AddPosition(int latitude10000thMins, int longitude10000thMins) | ||
{ | ||
this.SummedLats += latitude10000thMins; | ||
this.SummedLongs += longitude10000thMins; | ||
this.PositionsCount += 1; | ||
} | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void OnCompleted() | ||
{ | ||
} | ||
|
||
/// <inheritdoc/> | ||
public void Progress( | ||
bool done, | ||
int totalNmeaLines, | ||
int totalAisMessages, | ||
int totalTicks, | ||
int nmeaLinesSinceLastUpdate, | ||
int aisMessagesSinceLastUpdate, | ||
int ticksSinceLastUpdate) | ||
{ | ||
} | ||
} | ||
} | ||
} |
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
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
Oops, something went wrong.