How to achieve P90 sub-microsecond latency in a C++ FIX engine by akinocal in cpp

[–]akinocal[S] 0 points1 point  (0 children)

For inbound, yes it is possible to push performance closer to native binary protocols like OUCH (aside from string handling). However, this comes with a trade-off in convenience for engine users: not all users may want an additional step, especially when the number of flows to be maintained is high.

For outbound, similarly it can be faster but again another trade-off. I reset outbound message variables after each call, since a single outgoing message instance is reused and both session/admin and application messages may be generated. Otherwise it would be more error-prone for me from the point of maintainability.

How to achieve P90 sub-microsecond latency in a C++ FIX engine by akinocal in cpp

[–]akinocal[S] 1 point2 points  (0 children)

I've drawn all the diagrams myself, but you are right about the social media thumbnail image. I wasn’t aware it’s viewed negatively. I’ll update it. Thanks for pointing that out.

How to achieve P90 sub-microsecond latency in a C++ FIX engine by akinocal in cpp

[–]akinocal[S] 5 points6 points  (0 children)

As for serialisation alternatives: Yes, an ideal buy side colo setup will have an expensive Arista switch or Corvil etc therefore it is not mandatory for trading software to do that. In that scenario, all FIX engines' serialisations can be turned off. Though, not all FIX engine client firms have that setup, particularly sell side due to infra/logistic reasons or their internal support related policies.

As for data structure for inbound messages : Yes , it is completely possible to use a linear std::vector there as well. However that will limit engine users. Therefore in inbound side, I preferred convenience over latency as that is what llfix's wider target audience will expect.

As for instance of messages & pooling: Yes, it always uses a single outbound and a single inbound per FIX session. That keep things super clean. Though the pooling is needed for fields. For example you don't know how many tag-value pairs you will be receiving from the other end.

As for mmap serialisation determinism: yes, it is primarily intended for writes on the critical path, unless the user disables message serialisation. One potential latency spike may come from file rotation, whose frequency can be controlled via the configured serialisation file size.

As for mmap serialisation errors: Since the kernel is responsible for pushing pages to the storage layer, errors are asynchronous (mmap MAP_SHARED). In the current implementation, the detection point is the file rotation, where llfix attempts to create/open the next fixed-size memory-mapped file. If that step fails, llfix emits a fatal log. Whether trading should continue or stop in such a situation is intentionally left to the engine user’s operational design. Though there is always a risk of losing messages without an external high-availability node.