32

我认为 C/C++ 与 C#/Java 性能问题已经很好地解决了,这意味着我已经阅读了足够多的证据表明 VM 语言不一定比“接近硅”的语言慢。主要是因为 JIT 编译器可以做静态编译语言不能做的优化。

然而,我最近收到了一个人的简历,他声称基于 Java 的高频交易总是被 C++ 打败,而且他曾经遇到过这种情况。

快速浏览求职网站确实表明 HFT 申请人需要 C++ 知识,并且查看Wilmott论坛显示所有从业者都在谈论 C++。

有什么特别的原因导致这种情况吗?我原以为现代金融业务有点复杂,具有类型安全、托管内存和丰富库的 VM 语言将是首选。这样生产率会更高。另外,JIT 编译器越来越好。他们可以在程序运行时进行优化,因此您会认为他们使用该运行时信息来击败非托管程序的性能。

也许这些人正在用 C++ 编写关键位并从托管环境(P/Invoke 等)中调用它们?那可能吗?

最后,有没有人对这个中心问题有经验,这就是为什么在这个领域中,非托管代码无疑比托管代码更受欢迎?

据我所知,高频交易人员需要尽可能快地对传入的市场数据做出反应,但这并不一定是硬实时要求。如果你很慢,你的情况会更糟,这是肯定的,但你不需要保证每次响应都有一定的速度,你只需要一个快速的平均值。

编辑

是的,到目前为止,有几个很好的答案,但很笼统(广为人知)。让我指定 HFT 人员将运行什么样的程序。

主要标准是反应能力。当订单进入市场时,您希望成为第一个能够对其做出反应的人。如果你迟到了,其他人可能会先于你,但每家公司的策略略有不同,所以如果一个迭代有点慢,你可能会没事。

该程序整天运行,几乎没有用户干预。无论处理每条新市场数据的功能如何,每秒都会运行数十次(甚至数百次)。

这些公司通常对硬件的价格没有限制。

4

15 回答 15

37

首先,1 ms 在 HFT 中是永恒的。如果您认为不是,那么最好多阅读有关该域的内容。(就像距离交易所 100 英里一样。)吞吐量和延迟是紧密交织在一起的,任何基本排队理论教科书中的公式都会告诉你。相同的公式将显示抖动值(如果网络结构正确并且您没有配置足够多的内核,则通常由 CPU 队列延迟的标准偏差决定)。

高频交易套利的问题之一是,一旦您决定获取价差,就有两条腿(或更多条腿)来实现利润。如果你没有打完所有的腿,你可能会得到一个你真的不想要的头寸(以及随后的损失)——毕竟你是在套利而不是投资。

除非您的策略是预测(非常近期!!!)未来(不管你信不信,这件事做得非常成功),否则你不想要头寸。如果您距离交易所只有 1 毫秒,那么您的大部分订单将不会被执行,而您想要的将会被取走。最有可能已经执行了一条腿的人最终会失败,或者至少不会盈利。

无论你的策略是为了争论,让我们说它最终的赢/输率是 55%/45%。即使是赢/输比率的微小变化也可能导致盈利能力发生巨大变化。

回复:“运行数十个(甚至数百个)”似乎相差了几个数量级即使每秒查看 20000 个滴答声似乎也很低,尽管这可能是他正在查看的仪器组一整天的平均值。

在任何给定的秒内看到的速率都有很大的可变性。我举个例子。在我的一些测试中,我在一天的中间查看了 7 支场外交易股票(CSCO、GOOG、MSFT、EBAY、AAPL、INTC、DELL),该流的每秒速率可以从 0 mps(非常非常罕见)到每个峰值秒几乎有近 2000 个报价和交易。(看看为什么我认为上面的 20000 很低。)

我为这个领域构建了基础设施和测量软件,我们谈论的数字是每秒 100000 和数百万。我有 C++ 生产者/消费者基础设施库,可以在生产者和消费者(32 位,2.4 GHz 内核)之间推送几乎 5000000(500 万)条消息/秒。这些是 64 字节的消息,在生产者端具有新的、构造的、入队的、同步的、同步的、出队的、触摸每个字节的、运行虚拟析构函数的、免费的在消费者方面。现在诚然,这是一个没有 Socket IO 的简单基准测试(并且套接字 IO 可能很难看),就像在端点管道阶段的端点一样。它是所有自定义同步类,仅在空时同步,自定义分配器,自定义无锁队列和列表,偶尔的 STL(带有自定义分配器)但更常见的是自定义侵入式集合(其中我有一个重要的库)。我不止一次地为这个领域的供应商提供了四倍(甚至更多)的吞吐量,而没有增加套接字端点的批处理。

我有 OrderBook 和 OrderBook::Universe 类,当平均超过 22000 个仪器时,新、插入、查找、部分填充、查找、第二次填充、擦除、删除序列所需的时间不到 2us。该基准在插入第一次填充和最后一次填充之间连续迭代所有 22000 个仪器,因此不涉及廉价的缓存技巧。对同一本书的操作被 22000 本书的访问分开。这些都不是真实数据的缓存特性。真实数据在时间上更加本地化,​​连续交易经常出现在同一本书中。

所有这些工作都涉及仔细考虑所用集合的任何算法成本中的常量和缓存特性。(有时,K O(n) K O(n*log n) 等中的 K 似乎被忽略了一点太圆滑了)

我从事市场数据基础设施方面的工作。甚至想到使用 java 或托管环境来完成这项工作都是不可思议的。当您可以使用 C++ 获得这种性能时,我认为在托管环境中获得百万以上/mps 的性能是相当困难的)我无法想象任何重要的投资银行或对冲基金(他们的薪水为 250000 美元)一流的 C++ 程序员什么都不是)不会使用 C++。

有人真的从托管环境中获得 2000000+/mps 的性能吗?我在这个领域认识一些人,但从来没有人向我吹嘘过。而且我认为托管环境中的 2mm 会有一些吹牛的权利。

我知道一位主要参与者的 FIX 顺序解码器每秒进行 12000000 场解码。(3Ghz CPU)它是 C++,编写它的人几乎向任何人提出了挑战,要在托管环境中提出甚至是该速度一半的东西。

从技术上讲,这是一个有趣的领域,有很多有趣的性能挑战。考虑当基础证券发生变化时的期权市场 - 可能会有 6 个具有 3 或 4 个不同到期日的未偿价格点。现在每笔交易可能有 10-20 个报价。这些报价可以触发期权的价格变化。因此,对于每笔交易,期权报价可能会有 100 或 200 次变化。这只是大量的数据——不是大型强子对撞机碰撞检测器般的数据量,但仍然有点挑战。这与处理击键有点不同。

甚至关于 FPGA 的争论也在继续。许多人认为运行在 3GHZ 商品硬件上的编码良好的解析器可以击败 500MHz FPGA。但即使稍微慢一点(不是说它们慢),基于 FPGA 的系统也往往具有更紧密的延迟分布。(阅读“趋向”——这不是一个笼统的陈述)当然,如果你有一个很棒的 C++ 解析器,你可以通过 Cfront 推送,然后通过 FPGA 图像生成器推送它……但那是另一场辩论……

于 2010-07-06T05:05:41.140 回答
28

很多都归结为事实和理论之间的简单区别。人们有先进的理论来解释为什么 Java 应该(或至少可能)比 C++ 快。大多数论点与 Java 或 C++本身几乎没有关系,而是与动态编译与静态编译有关,Java 和 C++ 实际上只是两者的示例(当然,可以静态编译 Java 或 C++动态)。这些人中的大多数都有基准来“证明”他们的主张。当对这些基准进行任何详细检查时,很快就会发现,在相当多的情况下,他们采取了相当极端的措施来获得他们想要的结果(例如,相当多的在编译 Java 时启用了优化,但特别是编译 C++ 时禁用优化)。

将此与Computer Language Benchmarks Game进行比较,几乎任何人都可以提交条目,因此所有代码都倾向于优化到合理的程度(在少数情况下,甚至是不合理的程度)。很明显,相当多的人将其视为本质上的竞争,每种语言的拥护者都尽最大努力“证明”他们首选的语言是最好的。由于任何人都可以提交任何问题的实现,因此特别糟糕的提交对整体结果几乎没有影响。在这种情况下,C 和 C++ 成为明显的领导者。

更糟糕的是,这些结果可能比完全准确的结果更能说明 Java。特别是,使用 C 或 C++ 并且真正关心性能的人可以(并且经常会)使用英特尔的编译器而不是 g++。与 g++ 相比,这通常会使速度至少提高 20%。

编辑(回应 jalf 提出的几点,但真的太长了,无法合理地放在评论中):

  1. 指针是优化器编写者的噩梦。这确实有点夸大其词(相当)。指针会导致混叠的可能性,这会在某些情况下阻止某些优化。也就是说,内联在很多时候可以防止不良影响(即,编译器可以检测是否存在别名,而不是总是在假设可能存在的情况下生成代码)。即使代码确实必须假设别名,缓存也可以最大限度地减少这样做对性能的影响(即,L1 缓存中的数据只比寄存器中的数据慢一点点)防止别名将有助于 C++ 中的性能,但并不像您想象的那么多。

  2. 垃圾收集器的分配速度要快得多。确实,许多 C++ 实现中的默认分配器比大多数(当前)垃圾收集分配器提供的要慢。C++ 中的分配往往在堆栈上,这也很快,而在 GC 语言中,几乎所有的分配通常都在堆上,这一点得到了平衡(至少在一定程度上)。更糟糕的是,在托管语言中,您通常为每个对象单独分配空间,而在 C++ 中,您通常为范围内的所有对象分配空间。

确实,C++ 直接支持全局和逐个类地替换分配器,所以当/如果分配速度真的是一个问题,它通常很容易解决。

最终,jalf 是对的:这两点无疑支持“托管”实现。但是,应该保持这种改进的程度:它们不足以让动态编译的实现在许多代码上运行得更快——甚至从一开始就设计的基准测试也不能尽可能地支持它们。

Edit2:我看到 Jon Harrop 试图插入他的两(十亿分之一)美分的价值。对于那些不认识他的人来说,乔恩多年来一直是一个臭名昭著 巨魔 垃圾邮件发送者 并且似乎正在寻找新的土地来播种杂草。我会尝试详细回复他的评论,但是(对于他来说,这是典型的)它完全由不合格、不受支持的概括组成,其中包含的实际内容太少,以至于不可能做出有意义的回复。所能做的就是向旁观者发出公平的警告,即他以不诚实、自私自利和最容易被忽视而闻名。

于 2010-07-04T15:41:03.337 回答
14

JIT 编译器理论上可以执行很多优化,是的,但是您愿意等待多长时间?一个 C++ 应用程序可能需要几个小时来编译,因为它发生在离线状态,并且用户不会坐在那里敲打他的手指并等待。

JIT 编译器必须在几毫秒内完成。那么您认为哪些可以摆脱最复杂的优化呢?

垃圾收集器也是一个因素。不是因为它本身比手动内存管理慢(我相信它的摊销成本相当不错,绝对可以与手动内存处理相媲美),而是它的可预测性较差。它几乎可以在任何时候引入停顿,这在需要极快响应的系统中可能是不可接受的。

当然,这些语言适合于不同的优化。C++ 允许您编写非常紧凑的代码,几乎没有内存开销,并且许多高级操作基本上是免费的(例如,类构造)。

另一方面,在 C# 中,您浪费了大量内存。并且简单地实例化一个类会带来很大的开销,因为Object必须初始化基类,即使您的实际类是空的。

C++ 允许编译器积极地剥离未使用的代码。在 C# 中,大部分内容必须存在,以便可以通过反射找到。

另一方面,C# 没有指针,这是优化编译器的噩梦。托管语言中的内存分配比 C++ 便宜得多。

无论哪种方式都有优势,因此期望您能得到一个简单的“一个或另一个”答案是幼稚的。根据确切的源代码、编译器、操作系统、运行它的硬件,其中一个可能更快。根据您的需求,原始性能可能不是第一目标。也许您对响应能力、避免不可预测的停顿更感兴趣。

通常,您的典型 C++ 代码的执行方式与等效 C# 代码类似。有时更快,有时更慢,但无论哪种方式都可能没有显着差异。

但同样,这取决于具体情况。这取决于您愿意花多少时间进行优化。如果你愿意花尽可能多的时间,C++ 代码通常可以实现比 C# 更好的性能。只是需要做很多工作。

当然,另一个原因是大多数使用 C++ 的公司已经拥有庞大的 C++ 代码库,他们并不想放弃这些代码库。他们需要这样才能继续工作,即使他们逐渐将(一些)新组件迁移到托管语言。

于 2010-07-04T15:13:56.107 回答
9

这些公司通常对硬件的价格没有限制。

如果他们也不在乎软件有多贵,那么我认为 C++ 当然可以更快:例如,程序员可能使用自定义分配或预分配的内存;和/或他们可以在内核中运行代码(避免环形转换),或在实时操作系统上,和/或将其与网络协议栈紧密耦合。

于 2010-07-04T15:48:19.547 回答
2

除了性能之外,还有其他原因使用 C++。有一个巨大的现有 C 和 C++ 代码库。用其他语言重写所有这些是不切实际的。为了使 P/Invoke 之类的东西正常工作,必须将目标代码设计为从其他地方调用。如果没有别的,您将不得不编写某种包装器来暴露完全 C API,因为您无法 P/Invoke 到 C++ 类。

最后,P/Invoke 是一项非常昂贵的操作。

JIT 编译器越来越好。他们可以在程序运行时进行优化

是的,他们可以做到这一点。但是您忘记了任何 C++ 编译器都能够进行相同的优化。当然,编译时间会更糟,但这种优化必须在运行时完成的事实本身就是开销。在某些情况下,托管语言可以在某些任务上击败 C++,但这通常是因为它们的内存模型,而不是运行时优化的结果。严格来说,您当然可以在 C++ 中拥有这样的内存模型,EDIT:例如 C# 对字符串的处理,/EDIT 但很少有 C++ 程序员像 JIT 人员那样花那么多时间优化他们的代码。

托管语言存在一些性能问题,即磁盘 I/O。这是一次性成本,但取决于应用程序,它可能很重要。即使使用最好的优化器,程序启动时仍需要从磁盘加载 30MB 以上的 JIT 编译器;而 C++ 二进制文件很少接近这个大小。

于 2010-07-04T15:11:14.990 回答
2

一个简单的事实是 C++ 是为速度而设计的。C#/Java 不是。

与 std::sort 或 std::for_each 的零开销相比,这些语言特有的无数继承层次结构(例如 IEnumerable)是通用的。C++ 的原始执行速度不一定更快,但程序员可以设计快速或零开销的系统。即使是缓冲区溢出之类的事情-您也无法关闭它们的检测。在 C++ 中,您拥有控制权。从根本上说,C++ 是一种快速的语言——你不需要为你不使用的东西付费。相比之下,在 C# 中,如果使用 stackalloc,则不能不进行缓冲区溢出检查。您不能在堆栈上或连续分配类。

还有整个编译时的事情,C++ 应用程序可能需要更长的时间,无论是编译还是开发。

于 2010-07-04T15:57:40.493 回答
2

这可能有点离题,但几周前我看了一个视频,你可能会感兴趣: http: //ocaml.janestreet.com/ ?q=node/61

它来自一家决定使用 ocaml 作为其主要交易语言的贸易公司,我认为他们的动机应该对你有所启发(基本上,他们当然重视速度,但也重视强大的打字和功能风格,以加快增量以及更容易理解)。

于 2010-07-04T19:48:49.517 回答
2

我们的大部分代码最终不得不在由 1000 台机器组成的网格上运行。

我认为这种环境改变了论点。例如,如果 c++ 和 c# 执行速度之间的差异为 25%,那么其他因素就会发挥作用。当它在网格上运行时,它的编码方式可能没有区别,因为整个过程一旦分布在机器上可能就不是问题或通过分配或购买更多机器来解决。最重要的问题和成本可能成为“上市时间”,c# 可能会证明是赢家和更快的选择。

c++ 和 c# 哪个更快?

C# 六个月......

于 2010-07-06T13:29:43.070 回答
1

这不仅仅是编程语言的问题,硬件和操作系统都将相关。
通过实时操作系统、实时编程语言和高效(!)编程,您将获得最佳的整体性能。

因此,您在选择操作系统方面有很多可能性,在选择语言方面也有一些可能性。有 C、Realtime Java、Realtime Fortran 和其他一些。

或者,您可能会在对 FPGA/处理器进行编程以消除操作系统成本方面获得最佳结果。

您必须做的最大选择是,您将忽略多少可能的性能优化,转而选择一种易于开发且运行更稳定的语言,因为您可以减少错误,从而提高系统的可用性。这不应该被忽视。开发一个比任何其他应用程序执行速度快 5% 的应用程序是没有胜利的,这些应用程序由于一些难以发现的小错误而每隔几个点就崩溃一次。

于 2010-07-04T15:59:07.773 回答
1

在 HFT 中,延迟是比吞吐量更大的问题。鉴于数据源中固有的并行性,您总是可以在问题上投入更多的内核,但您无法用更多的硬件来弥补响应时间。无论语言是预先编译的,还是即时编译的,垃圾收集都会破坏您的延迟。存在具有保证垃圾收集延迟的实时 JVM。这是一项相当新的技术,调整起来很痛苦,而且价格昂贵得离谱,但如果你有资源,就可以做到。随着早期采用者资助现在正在进行的研发,它可能会在未来几年变得更加主流。

于 2010-07-04T16:02:42.373 回答
1

C++ 中最有趣的事情之一是它的性能数字不是更好,而是更 可靠

它不一定比 Java/C#/... 快,但它在运行过程中是一致的

就像在网络中一样,有时吞吐量不如稳定的延迟重要

于 2010-07-06T13:58:22.347 回答
0

除了已经说过的之外,在这种情况下更喜欢 c++(或更低级别)的一个重要原因是,低级有一些适应性优势

如果硬件技术发生变化,您总是__asm { }可以在语言/编译器赶上之前进入一个块并实际使用它

例如,Java 中仍然不支持 SIMD。

于 2010-07-04T18:59:24.473 回答
0

虚拟执行引擎(JVM 或 .Net 的 CLR)不允许以高效的方式构建工作,因为流程实例无法在可能需要的多线程上运行。

相比之下,纯 C++ 可以在时间关键的执行路径之外执行并行算法和构建对象。这几乎就是一切——简单而优雅。此外,使用 C++,您只需为使用的内容付费。

于 2010-07-05T13:05:58.060 回答
0

这里房间里的大象是 C++ 比 Java 快的事实

我们都知道。但我们也知道,如果我们像我刚才所做的那样明确地陈述,我们就不能假装就这个无可争议的话题进行有意义的辩论。对于您的应用程序,C++Java 快多少?这有一个值得商榷的话题,但是,唉,除非你用两种语言实现你的应用程序,否则它永远是假设的,在这一点上它就没有争论的余地了。

让我们回到您的第一次设计会议:您的项目的硬性要求是高性能。房间里的每个人都会想到“C++”和一些其他编译语言。房间里建议 Java 或 C# 的人将不得不用证据(即原型)来证明它的合理性,而不是假设,不是供应商的声明,不是程序员八卦网站上的陈述,当然也不是“你好世界”的基准。

就目前的情况而言,你必须以你所知道的前进,而不是假设可能的。

于 2010-07-06T14:09:45.547 回答
0

Nikie 写道:“你能解释一下你可以用 C++ 线程而不是 .NET 线程来做什么吗?”</p>

.Net 线程几乎可以执行 C++ 线程可以执行的所有操作,除了:

  1. 高效执行 COM 封装的二进制代码。例如,可能必须对应用程序开发人员保密的敏感算法。(可能与高频交易有关)
  2. 使用厚实的构建块创建不会耗尽系统资源的精益线程——封装的操作系统 API 和同步和信令操作系统原语。(与 HFT 性能时间优化的并行算法极为相关)
  3. 在相同的硬件上以相同的延迟将业务流程应用程序的吞吐量提高 10 倍或更多倍。(与高频交易无关)
  4. 将每单位硬件同时处理的用户交互数量扩大 100 倍甚至更多倍。(与高频交易无关)

使用更多的 CPU 内核并不能完全弥补 .Net 构建块对系统资源的消耗,因为更多的 CPU 内核是内存争用出现的保证。

于 2010-07-07T05:17:11.940 回答