GC Free Sample
Source code
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using OnixS.Fix;
using OnixS.Fix.Fix42;
namespace GCFree
{
using static Console;
internal static class GCFree
{
private static readonly AutoResetEvent ready = new AutoResetEvent(false);
#if DEBUG
private const int WarmupNumberOfMessages = 100000;
private const int NumberOfMessages = 100000;
#else
private const int WarmupNumberOfMessages = 500000;
private const int NumberOfMessages = 1000000;
#endif
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main()
{
WriteLine(GetProductName());
try {
var settings = new EngineSettings()
{
LicenseStore = GetLicenseStoreFolder(),
};
settings.ListenPorts.Add(10450);
Engine.Init(settings);
const string Sender = "Acceptor";
const string Target = "Initiator";
const ProtocolVersion Version = ProtocolVersion.Fix42;
using var acceptor = new Session(Sender, Target, Version, false)
{
ReuseEventArguments = true,
ReuseInboundMessage = true
};
acceptor.InboundApplicationMessage += (object sender, InboundMessageEventArgs e) =>
{
((Session)sender).Send(e.Message);
};
acceptor.LogonAsAcceptor();
using var initiator = new Session(Target, Sender, Version, false)
{
ReuseEventArguments = true,
ReuseInboundMessage = true
};
var receivedReply = new AutoResetEvent(false);
long initiatorReceiverAllocatedStart = 0, initiatorSenderAllocatedStart = 0,
initiatorReceiverAllocatedEnd = 0, initiatorSenderAllocatedEnd = 0;
initiator.InboundApplicationMessage += (object sender, InboundMessageEventArgs e) =>
{
if (initiatorReceiverAllocatedStart == 0)
initiatorReceiverAllocatedStart = GC.GetAllocatedBytesForCurrentThread();
initiatorReceiverAllocatedEnd = GC.GetAllocatedBytesForCurrentThread();
receivedReply.Set();
};
initiator.OutboundApplicationMessage += (object sender, MessageEventArgs e) =>
{
if (initiatorSenderAllocatedStart == 0)
initiatorSenderAllocatedStart = GC.GetAllocatedBytesForCurrentThread();
initiatorSenderAllocatedEnd = GC.GetAllocatedBytesForCurrentThread();
};
initiator.LogonAsInitiator(IPAddress.Loopback, settings.ListenPorts[0]);
const int NumberOfOrders = 1000;
var order = CreateOrder(Version);
order.Validate();
// Warmup
initiator.Send(order);
receivedReply.WaitOne();
long startBytes = GC.GetAllocatedBytesForCurrentThread();
for (int i = 0; i < NumberOfOrders; ++i)
{
initiator.Send(order);
receivedReply.WaitOne();
}
long allocatedBytes = GC.GetAllocatedBytesForCurrentThread() - startBytes;
long initiatorSenderAllocatedBytes = initiatorSenderAllocatedEnd - initiatorSenderAllocatedStart;
long initiatorReceiverAllocatedBytes = initiatorReceiverAllocatedEnd - initiatorReceiverAllocatedStart;
Console.WriteLine($"Main: {allocatedBytes} bytes were allocated per {NumberOfOrders} normal orders");
Console.WriteLine($"Sender: {initiatorSenderAllocatedBytes} bytes were allocated per {NumberOfOrders} normal orders");
Console.WriteLine($"Receiver: {initiatorReceiverAllocatedBytes} bytes were allocated per {NumberOfOrders} normal orders");
initiatorSenderAllocatedStart = initiatorSenderAllocatedEnd = initiatorReceiverAllocatedStart = initiatorReceiverAllocatedEnd = 0;
var serializedOrder = new SerializedMessage(order);
// Warmup
initiator.Send(serializedOrder);
receivedReply.WaitOne();
startBytes = GC.GetAllocatedBytesForCurrentThread();
for (int i = 0; i < NumberOfOrders; ++i)
{
initiator.Send(serializedOrder);
receivedReply.WaitOne();
}
allocatedBytes = GC.GetAllocatedBytesForCurrentThread() - startBytes;
initiatorSenderAllocatedBytes = initiatorSenderAllocatedEnd - initiatorSenderAllocatedStart;
initiatorReceiverAllocatedBytes = initiatorReceiverAllocatedEnd - initiatorReceiverAllocatedStart;
Console.WriteLine($"{allocatedBytes} bytes were allocated per {NumberOfOrders} serialized orders");
Console.WriteLine($"Sender: {initiatorSenderAllocatedBytes} bytes were allocated per {NumberOfOrders} serialized orders");
Console.WriteLine($"Receiver: {initiatorReceiverAllocatedBytes} bytes were allocated per {NumberOfOrders} serialized orders");
initiator.Logout();
acceptor.Logout();
Engine.Shutdown();
}
catch (Exception ex) {
WriteLine("Exception: " + ex);
}
}
private static Message CreateOrder(ProtocolVersion version)
{
var order = new Message(MsgType.Order_Single, version, 130);
order.Set(Tag.HandlInst, "1")
.Set(Tag.ClOrdID, "ClOrdID")
.Set(Tag.Symbol, "IBM")
.Set(Tag.Side, "1")
.Set(Tag.OrderQty, 1000)
.Set(Tag.OrdType, "1")
.Set(Tag.TransactTime, DateTime.UtcNow.ToString("yyyyMMdd-HH:mm:ss", CultureInfo.InvariantCulture));
return order;
}
private static string GetLicenseStoreFolder()
{
string path = Path.Join(AppContext.BaseDirectory, "../../../../../license");
if (Directory.Exists(path))
return path;
// We assume to run after `dotnet publish` using the default path.
return Path.Join(AppContext.BaseDirectory, "../../../../../../license");
}
private static string GetProductName()
{
var assemblyName = typeof(Engine).Assembly.GetName();
return $"GC Free Sample\n\n{assemblyName.Name} version {assemblyName.Version}\n";
}
}
}