低延迟Unix / Linux


11

大多数低延迟/高频编程作业(基于作业规范)似乎是在unix平台上实现的。在许多规范中,他们特别要求具有“低延迟linux”类型经验的人员。

假设这并不意味着可以使用实时linux操作系统,人们是否可以在这方面提供帮助?我知道您可以设置线程的CPU亲和力,但我假设他们对此的要求远远超过此。

内核调优?(尽管我听说像solarflare这样的制造商还是会生产内核旁路网卡)?

DMA或进程之间可能共享的内存呢?如果人们可以给我简短的想法,我可以去Google上进行研究。

(此问题可能需要熟悉高频交易的人员)


2
内核调整是使非实时操作系统尽可能实时的一种方法。线程固定也是必需的。:你可以阅读更多关于这篇文章中coralblocks.com/index.php/2014/04/...
rdalmeida

Answers:


26

我在IB和对冲基金设置中为HFT小组提供了很多支持工作。我将从sysadmin视图中进行回答,但是其中一些也适用于此类环境中的编程。

雇主在提到“低延迟”支持时通常需要注意几件事。其中一些是“原始速度”问题(您知道要购买哪种类型的10g卡,以及要插入哪个插槽?),但更多的是关于高频交易环境与传统交易方式不同的方式Unix环境。一些例子:

  • 传统上,Unix被调整为支持运行大量进程而不会耗尽其中任何一个资源,但是在HFT环境中,您可能希望以绝对最小的上下文切换开销运行一个应用程序,依此类推。作为一个经典的小示例,在Intel CPU上启用超线程可以一次运行更多进程-但是对每个单独进程的执行速度有很大的性能影响。作为程序员,您同样将不得不考虑诸如线程和RPC之类的抽象的成本,并找出在哪里使用更单一的解决方案(虽然不太干净)可以避免开销。

  • 通常对TCP / IP进行调整,以防止连接断开并有效利用可用带宽。如果您的目标是从非常快速的链接中获得尽可能低的延迟 -而不是从更受约束的链接中获得尽可能高的带宽 -您将要调整网络堆栈的调整。从编程的角度来看,您同样将要查看可用的套接字选项,并找出对带宽和可靠性进行调整而不是为了减少延迟而对默认设置进行了更多调整。

  • 与网络一样,对于存储也是如此-您将想知道如何从应用程序问题中分辨出存储性能问题,并了解哪种I / O使用模式最不可能干扰程序性能(例如,例如,了解使用异步IO的复杂性可以为您带来哪些好处以及不利之处。

  • 最后,更痛苦的是:我们Unix管理员希望获得尽可能多的有关所监视环境状态的信息,因此我们喜欢​​运行SNMP代理之类的工具,Nagios这样的主动监视工具以及sar(1)这样的数据收集工具。但是,在需要绝对最小化上下文切换并严格控制磁盘和网络IO使用的环境中,我们必须在监视费用和所监视的盒子的裸机性能之间找到适当的权衡。同样,您正在使用哪些技术使编码更容易,但是却会降低性能?

最后,随着时间的流逝还有其他事情发生。您从经验中学到的技巧和细节。但是这些功能更加专业化(我何时使用epoll?为什么具有理论上相同的PCIe控制器的两种型号的HP服务器的性能如此不同?),它们与您的特定商店所使用的内容紧密相关,并且更有可能从一年更改为另一年。


1
谢谢您,尽管我对编程答案很感兴趣,但这非常有用且有用。
user997112 2013年

5
@ user997112这是一个编程答案。如果不是这样,请继续阅读,直到它为止:)
Tim Post

15

除了@jimwise提供的出色的硬件/设置调整答案之外,“低延迟linux”还意味着:

  • C ++出于确定性的原因(GC启动时不会出现意外延迟),访问低级功能(I / O,信号),语言能力(充分使用TMP和STL,类型安全)。
  • 首选内存速度:> 512 Gb RAM是常见的;数据库是内存中的,预先缓存的或特殊的NoSQL产品。
  • 算法选择:尽可能快与理智/可理解/可扩展,例如无锁定的多位数组,而不是带有布尔属性的对象数组。
  • 充分利用操作系统功能,例如不同内核上的进程之间的共享内存。
  • 安全。HFT软件通常位于联合交易所中,因此无法接受恶意​​软件。

这些技术中有许多与游戏开发重叠,这就是金融软件行业吸收任何最近裁员的游戏程序员的原因之一(至少直到他们付了欠款为止)。

基本需求是能够侦听诸如安全性(股票,商品,外汇)价格之类的市场数据的带宽很高的流,然后根据安全性,价格做出非常快速的买/卖/不做决定。和当前持股。

当然,这全都可能出错


因此,我将详细说明位阵列点。假设我们有一个高频交易系统,该系统可以处理大量订单(买入5k IBM,卖出10k DELL等)。假设我们需要快速确定是否所有订单都已完成,以便我们可以进行下一个任务。在传统的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(); } );

该代码的算法复杂度为O(N),因为它是线性扫描。让我们看一下内存访问方面的性能概况:std :: any_of()内循环的每次迭代都将调用内联的o.isFilled(),因此成为_isFilled的内存访问(1字节) (或4个取决于您的体系结构,编译器和编译器设置)在一个总共128个字节的对象中。因此,我们每128个字节访问1个字节。假设是最坏的情况,当我们读取1个字节时,将得到CPU数据缓存未命中。这将导致对RAM的读取请求,该请求从RAM读取整行(请参阅此处以获取更多信息),仅读取8位。因此,内存访问配置文件与N成正比。

与以下内容进行比较:

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除以RAM行的宽度(变量-可能是双通道或三通道等)。

因此,实际上,我们正在优化内存访问模式的算法。RAM数量无济于事-导致此需求的是CPU数据缓存大小。

这有帮助吗?


YouTube上有出色的CPPCon演讲,内容涉及低延迟编程(适用于HFT):https : //www.youtube.com/watch?v= NH1Tta7purM


“用多位数组代替带有布尔属性的对象数组”,这是什么意思?
2013年

1
我已经详细说明了示例和链接。
JBR威尔金森

再往前走-您可以只使用单个位,而不是使用整个字节来指示是否已执行订单。因此,在单个缓存行(64字节)中,您可以表示256个订单的状态。如此-少错过。
quixver,2015年

另外-如果您要进行内存的线性扫描-硬件预取器在加载数据方面做得很好。如果您按顺序或跨步或简单地访问内存。但是,如果您以任何非顺序方式访问内存,则CPU预取器会感到困惑。例如,二进制搜索。到那时,程序员可以通过提示-_mm_prefetch帮助CPU。
quixver,2015年

-2

由于我没有在生产中投入一两个高频软件,所以我要说的最重要的是:

  1. 硬件配置和系统管理员以及网络工程师并没有定义交易系统处理的订单数量的良好结果,但是如果他们不了解上述基本知识,他们可以将其降级很多。
  2. 真正使系统进行高频交易的唯一人是计算机科学家,他用c ++编写了代码

    在使用的知识中有

    A.比较和交换操作。

    • CAS如何在处理器中使用以及计算机如何支持将其用于所谓的非锁定结构处理。或无锁处理。我不会在这里写整本书。简而言之,GNU编译器和Microsoft编译器都支持直接使用CAS指令。它允许您的代码在从队列中提取元素或将新元素放入队列时具有“ No.Wair”。
  3. 有才华的科学家将使用更多。他应该在最近的新“模式”中找到最早出现在Java中的模式。称为DISRUPTOR模式。欧洲的LMAX交易所折衷向高频社区解释说,如果Daya队列与现代cpu缓存的大小不匹配= 64,那么现代处理器中基于线程的利用率将减少CPU释放内存缓存的处理时间。

    因此,对于该阅读,他们公开了一个Java代码,该代码允许多线程进程正确使用硬件CPU缓存,而无需解决冲突。优秀的计算机科学家已经发现该模式已经移植到c ++或移植自己了。

    这是超越任何管理员配置的一种熟练方式。今天,这确实是高频的心脏。

  4. 计算机科学专家不仅编写了许多C ++代码,而且还帮助QA人员。但也要
    • 在交易员中进行验证,面对已证明的实现速度
    • 谴责使用了不同的旧技术,并用自己的代码公开它们,以表明它们无法产生良好的结果
    • 基于成熟的pupe / select内核速度编写自己的多线程通信c ++代码,而不是再次使用旧技术。我会举一个例子-现代的tcp库是ICE。做到这一点的人很聪明。但是他们的优先重点是与多种语言的兼容性。所以。您可以在C ++中做得更好。因此,请基于ASYNCHRONOUS selectcall搜索性能最高的实例。而且不要让多个消费者成为多个生产者-而不是高频。
      您会惊奇地发现管道仅用于到达消息的内核通知。您可以在此处放置64位消息号-但是对于内容,您可以进入无锁CAS队列。由异步内核select()调用触发。
    • 此外。了解有关使用c ++线程关联性将消息分配给对消息进行管道/排队的线程的信息。该线程应具有核心亲和力。没有其他人可以使用相同的CPU内核号。
    • 等等。

如您所见-高频是一个发展中的领域。您不能只是一名成功的C ++程序员。

当我说要取得成功时,我的意思是,您将为之工作的对冲基金将认可旅行社在年度薪酬方面的努力,超出人们和招聘人员谈论的人数。

简单的构造函数/析构函数FAQ的时代已经一去不复返了。C++本身已经与新的编译器一起移植,以减轻您的内存管理负担,并实现了类的深度继承。浪费时间。代码重用范例已更改。这不仅与您在polymorph中制作了多少类有关。这是关于可重复使用的代码的直接确认时间性能。

因此,您可以选择是否去那里学习曲线。它永远不会碰到停车标志。


6
您可能需要在拼写和格式化上付出一些努力。以目前的形式,这个帖子简直难以理解。
CodesInChaos

1
您描述了10年前的情况。无论您如何优化C ++,基于硬件的解决方案如今都轻而易举地超越纯C ++。
Sjoerd

对于那些想知道什么是基于硬件的解决方案的人-大多数是FPGA解决方案,其中的代码实际上被刻录到快速存储器中,而无需重新刻录所谓的ROM存储器就不会更改。只读
alex p

@alexp您显然不知道您在说什么。FPGA与“代码烧入快速存储器”不同。
Sjoerd
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.