Benchmark Parsing Sample
Source code
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using OnixS.Fix;
namespace Benchmark.Parsing
{
using static Console;
/// <summary>
/// Measures the parsing performance.
/// </summary>
static class Parsing
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static int Main()
{
WriteLine(GetProductName());
try {
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
}
catch (Exception ex) {
WriteLine("\nWarning: unable to set real-time priority to this process - " + ex.Message);
WriteLine(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "To enable process priority control run this benchmark under administrator"
: "To enable process priority control run this benchmark using 'sudo dotnet Parsing.dll'\n");
}
try {
var settings = new EngineSettings()
{
LicenseStore = GetLicenseStoreFolder(),
DisableNetworkLevel = true,
ValidateRepeatingGroupEntryCount = false,
ValidateCheckSum = false
};
settings.Versions.Clear();
const ProtocolVersion FixProtocolVersion = ProtocolVersion.Fix40;
settings.Versions.Add(FixProtocolVersion);
Engine.Init(settings);
var dictionary = Engine.Instance.MessageInfoDictionaryManager[FixProtocolVersion];
const string TestCasesSectionName = "TestCases";
if (! (ConfigurationManager.GetSection(TestCasesSectionName) is NameValueCollection testCases))
{
throw new EngineException($"The configuration file '{ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath}'" +
$" with '{TestCasesSectionName}' section was not found.");
}
foreach (string testCase in testCases) {
WriteLine($"Test case: {testCase}");
Benchmark(testCases[testCase], dictionary);
}
}
catch (Exception ex) {
WriteLine("EXCEPTION: " + ex);
return 1;
}
finally {
if (Engine.IsInitialized)
Engine.Shutdown();
}
return 0;
}
private static void Benchmark(string fileName, IMessageInfoDictionary dictionary)
{
ReadOnlySpan<byte> rawMsg = GetRawMessage(fileName);
const int WarmUpCycles = 1000;
for (int i = 0; i < WarmUpCycles; ++i) {
Message msg = Message.Parse(rawMsg, dictionary);
}
long start = TimestampHelper.Ticks;
const int TestCycles = 1000000;
for (int i = 0; i < TestCycles; ++i) {
Message msg = Message.Parse(rawMsg, dictionary);
}
double elapsedSeconds = TimestampHelper.ElapsedSeconds(start);
double msgPerSec = TestCycles / elapsedSeconds;
double kbPerSec = rawMsg.Length * TestCycles / elapsedSeconds / 1024;
WriteLine($"Performance: {string.Format("{0:n0}", msgPerSec)} msg/sec, {string.Format("{0:n0}", kbPerSec)} kB/sec.");
WriteLine();
}
private static ReadOnlySpan<byte> GetRawMessage(string fileName)
{
Span<byte> rawMsg = File.ReadAllBytes(fileName);
const byte FieldDelimiter = 1;
int msgLength = rawMsg.LastIndexOf(FieldDelimiter);
return rawMsg.Slice(0, msgLength);
}
private static string GetLicenseStoreFolder()
{
string path = Path.Join(AppContext.BaseDirectory, "../../../../../../license");
if (Directory.Exists(path))
return path;
// expecting it run after dotnet publish using default paths
return Path.Join(AppContext.BaseDirectory, "../../../../../../../license");
}
private static string GetProductName()
{
var assemblyName = typeof(Engine).Assembly.GetName();
return $"Parsing Benchmark Sample\n\n{assemblyName.Name} version {assemblyName.Version}\n";
}
}
}