8

我遇到了这个非常相似的问题,但该问题被标记为 QuickFIX(与我的问题无关),并且大多数答案都与 QuickFIX 相关。

我的问题更广泛。我正在寻找使用 C#解析FIX 协议消息的最有效方法。作为背景,FIX 消息由一系列由 ASCII<SOH>字符 (0x01) 分隔的标记/值对组成。消息中的字段数是可变的。

示例消息可能如下所示:

8=FIX.4.2<SOH>9=175<SOH>35=D<SOH>49=BUY1<SOH>56=SELL1<SOH>34=2482<SOH>50=frg<SOH>
52=20100702-11:12:42<SOH>11=BS01000354924000<SOH>21=3<SOH>100=J<SOH>55=ILA SJ<SOH>
48=YY77<SOH>22=5<SOH>167=CS<SOH>207=J<SOH>54=1<SOH>60=20100702-11:12:42<SOH>
38=500<SOH>40=1<SOH>15=ZAR<SOH>59=0<SOH>10=230<SOH>

对于每个字段,标签(整数)和值(我们的目的是字符串)由“=”字符分隔。(每个标签的精确语义在协议中定义,但这与这个问题并不是特别密切相关。)

通常情况下,在进行基本解析时,您只对 FIX 标头中的少数特定标签感兴趣,而不是真正对每个可能的字段进行随机访问。我考虑过的策略包括:

  • 使用String.Split, 遍历每个元素并将标签到索引映射放在哈希表中 - 如果在某些时候需要,可以提供对所有字段的完全随机访问

  • (轻微优化)使用String.Split,扫描数组以查找感兴趣的标签并将标签到索引映射到另一个容器(不一定是哈希表,因为它可能是相当少量的项目,并且在解析之前知道项目的数量)

  • String.IndexOf使用并以适当的结构存储感兴趣字段的偏移量和长度,逐个字段扫描消息

关于前两个 - 尽管我的测量结果表明String.Split速度非常快,但根据文档,该方法为结果数组的每个元素分配一个新字符串,如果您解析大量消息,这可能会产生大量垃圾。任何人都可以在 .NET 中找到解决此问题的更好方法吗?

编辑:

我遗漏了三个重要信息:

  1. 标签在 FIX 消息中不一定是唯一的,即在某些情况下可能会出现重复的标签。

  2. 某些类型的 FIX 字段可以<SOH>在数据中包含“嵌入”——这些标签被称为“数据”类型——字典列出了这种类型的标签号。

  3. 最终的要求是能够编辑消息(尤其是替换值)。

4

4 回答 4

8

假设您是通过网络获取这些消息,或者是从磁盘加载它们。在任何一种情况下,您都可以将它们作为字节数组访问,并以正向读取的方式读取字节数组。如果您想要/需要/需要高性能,然后自己解析字节数组(为了高性能,不要使用标签和值的哈希表字典,因为相比之下这非常慢)。自己解析字节数组也意味着您可以避免使用您不感兴趣的数据,并且您可以优化解析以反映这一点。

您应该能够轻松避免大多数对象分配。您可以在不创建对象的情况下非常轻松且非常快速地将 FIX 浮点数据类型解析为双精度数(您可以在此处使用您自己的版本大大胜过 double.parse)。您可能需要更多考虑的是作为字符串的标记值,例如 FIX 中的符号值。为了避免在这里创建字符串,您可以想出一个简单的方法来确定每个符号的唯一 int 标识符(这是一种值类型),这将再次帮助您避免在堆上分配。

正确完成消息的自定义优化解析应该很容易胜过 QuickFix,并且您可以在 .NET 或 Java 中无需垃圾收集即可完成所有操作。

于 2011-03-04T12:25:44.250 回答
3

我肯定会开始实施您的第一种方法,因为它听起来清晰易行。

ADictionary<int,Field>对我来说似乎非常好,可能包含在一个FixMessage暴露方法之类的类中GetFieldHavingTag(int tag)......

我不知道 FIX 协议,但看你的例子似乎消息通常很短,字段也很短,所以内存分配压力应该不是问题。

当然,确定一种方法是否适合您的唯一方法是实施并测试它。

如果您注意到在有大量消息的情况下该方法很慢,请对其进行分析并找出问题所在/位置。

如果你不能轻易解决它,那么是的,改变策略,但我想强制执行你需要先测试它,然后分析它并最终改变它的想法。

因此,让我们想象一下,在您的第一次实现之后,您注意到大量字符串分配会降低您的性能,以防出现许多消息。

那么是的,我会采用类似于您的第三种方法的方法,我们称之为“按需/惰性方法”。

我会构建一个FixMessage接收字符串消息的类,并且在需要任何消息字段之前什么都不做。
在那种情况下,我会使用IndexOf(或类似的东西)来搜索请求的字段,也许在另一个相同请求的情况下缓存结果会更快。

于 2011-02-05T16:50:59.550 回答
2

我知道这是对一个较老问题的答案——我最近才意识到有很多关于 SO 的 FIX 相关问题,所以我想我会尝试回答这个问题。

您的问题的答案可能取决于您实际解析的特定 FIX 消息。在某些情况下,是的 - 你可以只对字符串进行“拆分”,或者你有什么,但如果你要解析协议中定义的所有消息,你别无选择,只能参考FIX 数据字典,并逐字节解析消息。这是因为 FIX 消息中有长度编码的字段 - 根据规范,其中可能包含会干扰您可能想要采取的任何类型的“拆分”方法的数据。

最简单的方法是引用字典并根据您收到的消息的类型(标签 35)检索消息定义。然后,您需要一个接一个地提取标签,在消息定义中引用相应的标签定义,以便了解与标签关联的数据需要如何解析。这对于消息中可能存在的“重复组”也有帮助 - 如果您有字典中的消息定义,您将只能理解标签代表重复组的开始。

我希望这有帮助。如果您需要参考示例,我为 .NET 编写了 VersaFix 开源 FIX 引擎,其中包含基于字典的消息解析器。您可以通过将您的 SVN 客户端指向以下位置,直接从我们的 Subversion 服务器下载源代码:

svn://assimilate.com/VfxEngine/Trunk

干杯。

于 2011-11-15T01:04:24.493 回答
1

You are probably better off using QuickFix in all honesty and building a Managed C++ wrapper for it. If you are at all concerned with latency then you cannot perform allocations as part of the parsing since that can cause the GC to run which pauses your FIX engine. When paused you cannot send or receive messages which as I am sure you know is very very bad.

There was one company who Microsoft had highlighted a couple years ago as building a FIX engine entirely in c#. They would build a pool of objects to use over the course of the trading day and perform no allocations during the day.

I don't know what your latency requirements are but for what I am doing we have used codegen, different types of multithreaded heaps to get perf and reduce latency. We use a mixture of c++ and haskell.

Depending on your requirements maybe implement your parser as kernel mode driver to allow messages to be constructed as they are received off the wire.

@Hans: 10 microseconds is a very long time. NASDAQ matches orders in 98 microseconds and SGX has announced that it will take 90 microseconds to cross when they roll their new platform this year.

于 2011-02-08T16:45:39.443 回答