|Best Practices||Table of Content||High Throughput Best Practices|
|Low Latency Best Practices|
This section summarizes our findings and recommends best practices to tune the different layers of OnixS .NET FIX Engine for similar latency-sensitive workloads. By latency-sensitive, we mean workloads that are looking at optimizing for a few microseconds to a few tens of microseconds end-to-end latencies; we don’t mean workloads in the hundreds of microseconds to tens of milliseconds end-to-end-latencies. In fact, many of the recommendations in this description, that can help with the microsecond level latency, can actually end up hurting the performance of applications that are tolerant of higher latency. Please note that the exact benefits and effects of each of these configuration choices will be highly dependent upon the specific applications and workloads, so we strongly recommend experimenting with the different configuration options with your workload before deploying them in a production environment.
A process priority class encompasses a range of thread priority levels. We recommend to set ProcessPriorityClass.RealTime. For more details, please see Process.PriorityClass Property.
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
Using MemoryBasedStorage instead of FileBasedStorage boosts performance since FIX messages are stored directly in memory. Alternatively, it's possible to use a custom pluggable storage (using PluggableStorage) which does nothing on FIX message-related operations as soon as no resend requests are to be supported. Also you can use the Async File-based Session Storage if you need to keep the file based storage functionality and the good-performance.
By default, threads are used by FIX session to send and receive FIX messages. They can be executed on any of available processors/cores. Specifying CPU affinity for each session thread may give a significant performance boost: ReceivingThreadAffinity, SendingThreadAffinity
ReceiveSpinningTimeoutUsec property can be used to decrease the latency of the data receiving. If the value is zero (by default), the receiving thread will wait a new FIX message in the blocking wait mode. If the value greater than zero, the receiving thread will wait a new FIX message in the spin loop mode before switching to the blocking wait mode. The property specifies the spin loop period in microseconds. ReceiveSpinningTimeoutUsec property using makes sense when your session receives FIX messages frequently in this case waiting in the loop is cheaper than the thread context switch to the blocking wait mode. However please note that the spin wait increases the CPU usage so the spin wait period should not be too long.
SendSpinningTimeoutUsec property can be used to decrease the latency of the data sending. If the value is zero (by default) and the outgoing message cannot be sent immediately it is placed to the outgoing queue. If the value greater than zero, the Send(Message) method waits for the socket sending buffer availability in the spin loop mode before placing the message to the outgoing queue (to be sent later by the sending thread). The property specifies the spin loop period in microseconds. SendSpinningTimeoutUsec property using makes sense when your session sends FIX messages frequently in this case waiting in the loop is cheaper than the thread context switch. However please note that the spin wait increases the CPU usage and blocks the thread from which OnixS::FIX::Session::send method is called, so the spin wait period should not be too long.
WarmUp(SerializedMessage) method can be used to warm up the sending path. It makes sense if your session sends FIX messages infrequently, in this case the sending path and associated data structures will not be in a cache and this can increase the send latency. You can periodically (a recommended period is 500 usec and less) call the WarmUp(SerializedMessage) to avoid cache misses and keep the sending path fast. SerializedMessage object is required to warm up the sending path including the FIX message assembling and it will not be actually sent.
Object creation is an expensive operation in .NET, with impact on both performance and memory consumption. The cost varies depending on the amount of initialization that needs to be performed, when the object is to be created. OnixS .NET FIX Engine exposes an ability to reuse message instances and event arguments in event handlers by the Session. We highly recommend to turn on ReuseIncomingMessage, ReuseOutgoingMessage, ReuseEventArguments to minimize the excess object creation and garbage collection overhead.
If ReuseIncomingMessage turns on, the client's code must copy a message for using outside of inbound callbacks.
If ReuseOutgoingMessage turns on, the client's code must copy a message for using outside of outbound callbacks.
If ReuseEventArguments turns on, the client's code must copy event arguments for using outside of callbacks.
Send(Message) and Send(SerializedMessage) methods can be used to send messages in a batch, it can decrease the send latency because all messages in the batch will be sent in a one TCP packet. This ability is available for Message and SerializedMessage classes. Also you can decrease the send latency a little more by using SendAsIs(SerializedMessage)/SendAsIs(SerializedMessage) methods. These methods send serialized message(s) without any fields updating, you can prepare message(s) before sending by setting all necessary fields and using PreFill(SerializedMessage)/PreFill(SerializedMessage) methods.
If no messages are re-sent on counterparty's Resend Request messages, then it is possible to set the Resending Queue size to zero, in order to increase overall performance.
EngineSettings settings; settings.ResendingQueueSize = 0;
Validation significantly reduces performance due to miscellaneous checks that are performed on each FIX message. To disable FIX messages validation, the following settings should be used:
Code sample below shows how to disable such options:
EngineSettings settings; settings.ValidateEmptyFieldValues = false; settings.ValidateFieldValues = false; settings.ValidateRequiredFields = false; settings.ValidateUnknownFields = false; settings.ValidateUnknownMessages = false;
UseMemoryPressure option can be set to false. This option used to inform the runtime about allocated unmanaged memory. If this value was false, a latency will be improved because of less GC work, but in case of a lot amount of large messages can cause OutOfMemoryException. It is safe to set this option to false, if Dispose() called for each message.
When FIX Message is constructed, the space for all the fields, that are defined for the message, is allocated. The latest FIX specifications define a lot of fields for each message type. Even when most of fields are not used, FIX Engine reserves the space for them, and this has negative effect on FIX Engine's performance.
Editing dictionaries descriptions and excluding messages and fields (especially repeating groups), which are not used by application, have direct influence onto FIX messages processing speed and thus decrease general latency of FIX message related processing.
Constructing FIX messages, each time message has to be sent, is not efficient because it involves memory allocation and subsequent destruction. Message class provides ability to reuse message of a particular type. It exposes the Reset() method, which wipes out all the FIX fields that are assigned to the given instance. This method does not deallocate all memory occupied, it just brings the message to the initial state, as it was just constructed. However, the previously allocated memory is reused, and thus second use of the object allows to setup FIX fields faster.
A common strategy is to use the same message instance for sending multiple times. As soon as a message of a certain type is constructed, it is updated with common fields for subsequent re-use. Afterwards, before sending it to the counterparty, it is updated with mutable fields (like price or quantity) exactly for the case and sent to the counterparty.
Mutable fields are updated each time message is about to be sent to counterparty.
As an advance for reusing FIX messages, the concept of pre--SerializedMessage has been developed. In general, it follows the same approach of updating only mutable fields, before sending a message to the counterparty.
The only difference is that the message is serialized into 'tag=value' presentation in advance and, the serialization is not required when the message is actually sent.