@Jimwise'dan mükemmel donanım / kurulum ayarlama cevabına ek olarak, "düşük gecikme süresi linux" şunları ifade eder:
- Determinizm nedenleriyle C ++ (GC devreye girdiğinde sürpriz gecikme yok), düşük seviyeli tesislere (G / Ç, sinyaller) erişim, dil gücü (TMP ve STL'nin tam kullanımı, tip güvenliği).
- bellekte hızı tercih edin:> 512 Gb RAM yaygındır; veritabanları bellek içi, önbelleğe alınmış ön veya egzotik NoSQL ürünleridir.
- algoritma seçimi: aklı başında / anlaşılabilir / genişletilebilir, mümkün olduğu kadar hızlı, örneğin bool özellikli nesnelerin dizisi yerine kilitsiz, çoklu bit dizileri.
- Farklı çekirdeklerdeki işlemler arasında Paylaşılan Bellek gibi işletim sistemi olanaklarının tam kullanımı.
- güvenli. HFT yazılımı genellikle bir Borsa'da bulunur, bu nedenle kötü amaçlı yazılım olanakları kabul edilemez.
Bu tekniklerin birçoğu oyun geliştirme ile örtüşmektedir, bu da finansal yazılım endüstrisinin yakın zamanda yedekli olan herhangi bir program programcısını (en azından kira borçlarını ödeyene kadar) emmesinin bir nedenidir.
Temel ihtiyaç, güvenlik (hisse senetleri, emtia, fx) fiyatları gibi piyasa verilerinin çok yüksek bant genişlikli bir akışını dinleyebilmek ve daha sonra güvenlik, fiyat temelinde çok hızlı bir alım / satım / hiçbir şey yapma kararı verebilmektir. ve mevcut varlıklar.
Tabii ki, bu da tamamen yanlış gidebilir .
Bu yüzden bit dizileri noktasında ayrıntıya gireceğim . Uzun bir Siparişler listesinde çalışan Yüksek Frekanslı Ticaret sistemimiz olduğunu varsayalım (5k IBM Satın Al, 10k DELL Sat, vb.). Diyelim ki tüm siparişlerin yerine getirilip getirilmediğini hızlı bir şekilde belirlememiz gerekiyor, böylece bir sonraki göreve geçebiliriz. Geleneksel OO programlamasında bu şöyle görünecektir:
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(); } );
bu kodun algoritmik karmaşıklığı doğrusal bir tarama olduğu için O (N) olacaktır. Bellek erişimleri açısından performans profiline bakalım: std :: any_of () içindeki döngünün her yinelemesi, satır içi olan o.isFilled () öğesini çağıracak, bu nedenle _isFilled, 1 bayt bellek erişimi olacak (veya mimarinize, derleyici ve derleyici ayarlarına bağlı olarak 4) bir nesnenin toplam 128 bayt olduğunu varsayalım. Yani her 128 baytta 1 bayta erişiyoruz. En kötü durumda olduğu varsayılarak 1 baytı okuduğumuzda, CPU veri önbelleğini kaçırırız. Bu, RAM'den tüm satırı okuyan ( daha fazla bilgi için buraya bakın) RAM'e okuma isteğine neden olacak ve sadece 8 biti okuyacaktır. Bu yüzden bellek erişim profili N ile orantılıdır.
Bunu şununla karşılaştır:
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); }
bunun en kötü durumda olduğu varsayıldığında, bellek erişim profili ELEMS'in bir RAM hattının genişliğine bölünmesiyle elde edilir (değişir - çift kanallı veya üç kanallı olabilir, vb.).
Aslında, bellek erişim kalıpları için algoritmaları optimize ediyoruz. Hiçbir RAM miktarı yardımcı olmaz - bu ihtiyaca neden olan CPU veri önbellek boyutu.
Bu yardımcı olur mu?
YouTube'da düşük gecikmeli programlama (HFT için) hakkında mükemmel bir CPPCon konuşması var: https://www.youtube.com/watch?v=NH1Tta7purM