En plus de l'excellente réponse de réglage matériel / configuration de @jimwise, "Linux à faible latence" implique:
- C ++ pour des raisons de déterminisme (pas de retard surprenant au démarrage du GC), accès à des installations de bas niveau (E / S, signaux), puissance du langage (utilisation complète de TMP et STL, sécurité de type).
- préférez la vitesse par rapport à la mémoire:> 512 Go de RAM sont courants; les bases de données sont des produits NoSQL en mémoire, mis en cache en amont ou exotiques.
- choix de l'algorithme: aussi rapide que possible par rapport à sain d'esprit / compréhensible / extensible, par exemple des tableaux à plusieurs bits sans verrouillage au lieu d'un tableau d'objets avec des propriétés booléennes.
- utilisation complète des fonctionnalités du système d'exploitation telles que la mémoire partagée entre les processus sur différents cœurs.
- sécurise. Le logiciel HFT est généralement colocalisé dans une bourse de sorte que les possibilités de logiciels malveillants sont inacceptables.
Beaucoup de ces techniques se chevauchent avec le développement de jeux, ce qui est l'une des raisons pour lesquelles l'industrie des logiciels financiers absorbe les programmeurs de jeux récemment redondants (au moins jusqu'à ce qu'ils paient leurs arriérés de loyer).
Le besoin sous-jacent est de pouvoir écouter un flux de données de marché très large bande passante telles que les prix des titres (actions, matières premières, fx), puis de prendre une décision d'achat / vente / de ne rien faire très rapide en fonction de la sécurité, du prix et les avoirs actuels.
Bien sûr, tout cela peut aussi mal tourner de façon spectaculaire .
Je vais donc développer le point des tableaux de bits . Disons que nous avons un système de trading à haute fréquence qui fonctionne sur une longue liste d'ordres (acheter 5k IBM, vendre 10k DELL, etc.). Disons que nous devons déterminer rapidement si toutes les commandes sont remplies, afin de pouvoir passer à la tâche suivante. Dans la programmation OO traditionnelle, cela ressemblera à ceci:
class Order {
bool _isFilled;
...
public:
inline bool isFilled() const { return _isFilled; }
};
std::vector<Order> orders;
bool needToFillMore = std::any_of(orders.begin(), orders.end(),
[](const Order & o) { return !o.isFilled(); } );
la complexité algorithmique de ce code va être O (N) car c'est un balayage linéaire. Jetons un coup d'œil au profil de performance en termes d'accès à la mémoire: chaque itération de la boucle à l'intérieur de std :: any_of () va appeler o.isFilled (), qui est en ligne, devient ainsi un accès à la mémoire de _isFilled, 1 octet (ou 4 selon votre architecture, le compilateur et les paramètres du compilateur) dans un objet de disons 128 octets au total. Nous accédons donc à 1 octet sur 128 octets. Lorsque nous lisons le 1 octet, en supposant le pire des cas, nous obtiendrons un cache de données CPU manquant. Cela provoquera une demande de lecture vers la RAM qui lit une ligne entière de la RAM ( voir ici pour plus d'informations ) juste pour lire 8 bits. Le profil d'accès à la mémoire est donc proportionnel à N.
Comparez cela avec:
const size_t ELEMS = MAX_ORDERS / sizeof (int);
unsigned int ordersFilled[ELEMS];
bool needToFillMore = std::any_of(ordersFilled, &ordersFilled[ELEMS+1],
[](int packedFilledOrders) { return !(packedOrders == 0xFFFFFFFF); }
le profil d'accès à la mémoire de celui-ci, en supposant à nouveau le pire des cas, est ELEMS divisé par la largeur d'une ligne RAM (varie - pourrait être double canal ou triple canal, etc.).
Donc, en fait, nous optimisons les algorithmes pour les modèles d'accès à la mémoire. Aucune quantité de RAM ne sera utile - c'est la taille du cache de données CPU qui provoque ce besoin.
est-ce que cela aide?
Il y a un excellent CPPCon parler de la programmation à faible latence (pour HFT) sur YouTube: https://www.youtube.com/watch?v=NH1Tta7purM