Ngoài câu trả lời điều chỉnh phần cứng / thiết lập tuyệt vời từ @jimwise, "linux có độ trễ thấp" có nghĩa là:
- C ++ vì lý do xác định (không có độ trễ bất ngờ trong khi GC khởi động), truy cập vào các cơ sở cấp thấp (I / O, tín hiệu), sức mạnh ngôn ngữ (sử dụng đầy đủ TMP và STL, loại an toàn).
- thích bộ nhớ quá tốc độ:> 512 Gb RAM là phổ biến; cơ sở dữ liệu là các bộ nhớ trong, bộ nhớ cache hoặc các sản phẩm NoQuery kỳ lạ.
- lựa chọn thuật toán: càng nhanh càng tốt so với sane / có thể hiểu được / có thể mở rộng, ví dụ như mảng không khóa, nhiều bit thay vì mảng của các đối tượng với các thuộc tính bool.
- sử dụng đầy đủ các tiện ích HĐH như Bộ nhớ dùng chung giữa các tiến trình trên các lõi khác nhau.
- đảm bảo. Phần mềm HFT thường được đặt cùng trong một Sở giao dịch chứng khoán nên khả năng phần mềm độc hại là không thể chấp nhận được.
Nhiều trong số các kỹ thuật này trùng lặp với phát triển trò chơi, đó là một lý do tại sao ngành công nghiệp phần mềm tài chính hấp thụ bất kỳ lập trình viên trò chơi nào gần đây (ít nhất là cho đến khi họ trả tiền thuê nhà).
Nhu cầu cơ bản là có thể lắng nghe một luồng dữ liệu thị trường băng thông rất cao như giá bảo mật (cổ phiếu, hàng hóa, fx) và sau đó đưa ra quyết định mua / bán / không làm gì rất nhanh dựa trên bảo mật, giá cả và nắm giữ hiện tại.
Tất nhiên, điều này cũng có thể đi sai một cách ngoạn mục , quá.
Vì vậy, tôi sẽ giải thích về điểm mảng bit . Giả sử chúng ta có một hệ thống Giao dịch cao tần hoạt động trong một danh sách dài các Đơn đặt hàng (Mua 5k IBM, Bán 10k DELL, v.v.). Giả sử chúng ta cần nhanh chóng xác định xem tất cả các đơn đặt hàng đã được lấp đầy chưa, để chúng ta có thể chuyển sang nhiệm vụ tiếp theo. Trong lập trình OO truyền thống, điều này sẽ giống như:
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(); } );
độ phức tạp thuật toán của mã này sẽ là O (N) vì nó là quét tuyến tính. Chúng ta hãy xem cấu hình hiệu suất về các truy cập bộ nhớ: mỗi lần lặp của vòng lặp bên trong std :: any_of () sẽ gọi o.isFilt (), được nội tuyến, do đó trở thành quyền truy cập bộ nhớ của _isFilt, 1 byte (hoặc 4 phụ thuộc vào cài đặt kiến trúc, trình biên dịch và trình biên dịch của bạn) trong một đối tượng giả sử tổng cộng 128 byte. Vì vậy, chúng tôi đang truy cập 1 byte trong mỗi 128 byte. Khi chúng ta đọc 1 byte, giả sử trong trường hợp xấu nhất, chúng ta sẽ bị mất bộ đệm dữ liệu CPU. Điều này sẽ gây ra yêu cầu đọc tới RAM, đọc toàn bộ dòng từ RAM ( xem tại đây để biết thêm thông tin ) chỉ để đọc ra 8 bit. Vì vậy, hồ sơ truy cập bộ nhớ tỷ lệ thuận với N.
So sánh điều này với:
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); }
cấu hình truy cập bộ nhớ của trường hợp này, giả sử trường hợp xấu nhất một lần nữa, là ELEMS chia cho chiều rộng của dòng RAM (khác nhau - có thể là kênh đôi hoặc kênh ba, v.v.).
Vì vậy, trong thực tế, chúng tôi tối ưu hóa các thuật toán cho các mẫu truy cập bộ nhớ. Không có dung lượng RAM nào có thể giúp - đó là kích thước bộ đệm dữ liệu CPU gây ra nhu cầu này.
Không giúp đỡ à?
Có một CPPCon xuất sắc nói về tất cả các chương trình có độ trễ thấp (cho HFT) trên YouTube: https://www.youtube.com/watch?v=NH1Tta7purM