@Jimwise से उत्कृष्ट हार्डवेयर / सेटअप ट्यूनिंग उत्तर के अलावा, "कम विलंबता लिनक्स" का अर्थ है:
- नियतात्मकता के कारणों के लिए C ++ (जीसी किक के दौरान कोई आश्चर्य की बात नहीं है), निम्न-स्तरीय सुविधाओं (I / O, सिग्नल), भाषा शक्ति (TMP और STL का पूर्ण उपयोग, प्रकार सुरक्षा) तक पहुंच।
- गति-ओवर-मेमोरी पसंद करें:> 512 जीबी रैम आम है; डेटाबेस इन-मेमोरी, कैश्ड अप-फ्रंट या एक्सक्लूसिव नोएसक्यूएल उत्पाद हैं।
- एल्गोरिथ्म विकल्प: जैसे-फास्ट-एज़-संभव-बनाम बनाम सेंस / समझने योग्य / एक्स्टेंसिबल, जैसे लॉक-फ़्री, ऐर-ऑफ-ऑब्जेक्ट्स-बूल-प्रॉपर्टीज़ के बजाय कई बिट एरेज़।
- विभिन्न कोर पर प्रक्रियाओं के बीच साझा मेमोरी जैसी ओएस सुविधाओं का पूर्ण उपयोग।
- सुरक्षित। HFT सॉफ्टवेयर आमतौर पर स्टॉक एक्सचेंज में सह-स्थित होता है इसलिए मैलवेयर संभावनाएं अस्वीकार्य हैं।
इनमें से कई तकनीकों में गेम के विकास के साथ ओवरलैप है जो एक कारण है कि वित्तीय सॉफ्टवेयर उद्योग किसी भी हाल ही में निरर्थक गेम प्रोग्रामर को अवशोषित करता है (कम से कम जब तक वे अपने किराए के बकाया का भुगतान नहीं करते हैं)।
अंतर्निहित डेटा को बाजार के डेटा की बहुत उच्च बैंडविड्थ स्ट्रीम जैसे सुरक्षा (स्टॉक, कमोडिटीज़, एफएक्स) की कीमतों को सुनने में सक्षम होना है और फिर सुरक्षा के आधार पर बहुत तेज़ खरीद / बिक्री / कुछ भी निर्णय नहीं करना है। और वर्तमान होल्डिंग्स।
बेशक, यह सब शानदार रूप से गलत हो सकता है।
तो मैं बिट सरणियों बिंदु पर विस्तृत करूँगा । मान लीजिए कि हमारे पास एक उच्च आवृत्ति ट्रेडिंग सिस्टम है जो ऑर्डर की लंबी सूची (5k आईबीएम खरीदें, 10k डेल बेचें आदि) पर संचालित होता है। मान लें कि हमें यह निर्धारित करने की आवश्यकता है कि क्या सभी आदेश भरे गए हैं, ताकि हम अगले कार्य पर जा सकें। पारंपरिक OO प्रोग्रामिंग में, यह दिखने वाला है:
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(); } );
इस कोड की एल्गोरिथम जटिलता ओ (एन) होने जा रही है क्योंकि यह एक रैखिक स्कैन है। आइए मेमोरी एक्सेस के संदर्भ में प्रदर्शन प्रोफ़ाइल पर एक नज़र डालते हैं: std के अंदर लूप का प्रत्येक पुनरावृत्ति :: any_of () o.isFilled () कॉल करने जा रहा है, जो इनबिल्ड है, इसलिए _isFilled की मेमोरी एक्सेस बन जाती है, 1 बाइट (या 4 अपने आर्किटेक्चर, कंपाइलर और कंपाइलर सेटिंग्स पर निर्भर करते हुए) चलो एक वस्तु में 128 बाइट्स कुल कहते हैं। इसलिए हम प्रत्येक 128 बाइट्स में 1 बाइट एक्सेस कर रहे हैं। जब हम 1 बाइट पढ़ते हैं, तो सबसे खराब स्थिति को देखते हुए, हमें सीपीयू डेटा कैश मिस मिलेगा। यह रैम के लिए एक रीड रिक्वेस्ट का कारण बनेगा जो रैम से पूरी लाइन को पढ़ता है ( अधिक जानकारी के लिए यहां देखें ) सिर्फ 8 बिट्स को पढ़ने के लिए। तो मेमोरी एक्सेस प्रोफाइल एन के समानुपाती है।
इसके साथ तुलना करें:
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); }
इस की मेमोरी एक्सेस प्रोफाइल, सबसे खराब स्थिति मानकर, एक रैम लाइन की चौड़ाई से विभाजित किया गया ELEMS (भिन्न होता है - दोहरे चैनल या ट्रिपल-चैनल, आदि हो सकता है)।
तो, वास्तव में, हम मेमोरी एक्सेस पैटर्न के लिए एल्गोरिदम का अनुकूलन कर रहे हैं। राम की कोई भी राशि मदद नहीं करेगी - यह सीपीयू डेटा कैश आकार है जो इस आवश्यकता का कारण बनता है।
क्या यह मदद करता है?
YouTube पर निम्न-विलंबता प्रोग्रामिंग (HFT के लिए) के बारे में एक उत्कृष्ट CPPCon बात है: https://www.youtube.com/watch?v=NH1Tta7purM