如题,比如在tick data处理和后续交易信号分析之间交换数据,采用多线程系统的延迟小,但不利于代码功能区分;多个程序之间端口通讯的方法则代码比较清晰,但显然会增加延迟。如何取舍?
回复内容:
如果通讯方式一样,比如都用共享内存,那么多线程和多进程间的主要区别就只有程序崩溃时的隔离特性,多线程一崩溃就全完蛋,多进程只会影响一部分。
如果还考虑软件工程意义,不同进程通常意味着不同的程序,甚至不同的项目,操作风险会降低,这是很多交易系统做两个进程而不是一个进程的重要原因,风控进程和策略进程软件工程意义上隔离,改策略不会增加乱报单的可能性。
这两点考虑到了之后,进程数越少越好。不同逻辑流(线程)间的同步越少越好。低计算量下,最低延迟通常是单线程逻辑得到的。
做低延迟都需要考虑I/O blocking到被唤醒执行所耗费的时间,用Socket等需要syscall和context switch的当然更应该能避免则避免了。
当然这一切都是基于高频交易,更准确的说是低延迟交易领域。不是所有电子交易系统都处于这一领域,其他领域的架构根据不同的需求可能完全不同。
首先我同意软件解藕的方法很多,按进程拆分不过是其中一种,但不必然是最好的一种。事实上要提高开发效率和降低维护难度,是一项需要根据团队成员的技术特点不断摸索的艺术,不必预先把自己的思维模式限制死了,尝试一下其他方法(比如 OO ,Functional )也许有更好的效果。
根据我(多年打杂)的经验来看,交易系统也是特别符合《人月神话》中推荐的外科手术团队的开发模式。开发这种高精密系统人多了其实有负面作用,最好的办法是以一两个高水平程序员为主力设计和开发,再加几个杂兵跑堂就可以了。所以解决这个问题有一个四海一家的终极大法:让老板涨工资高薪聘请一流的技术专家。当你有一个一流高手坐镇主攻的时候,解藕就从多人协作变成一项个人品味的问题了,只要根据主攻手的口味选择方案就好。
当然我明白人在江湖,身不由己。以上建议对于已经有很多历史遗留或者起了一半的项目来说显然是远水解不了近渴。所以下面尝试给点实际的建议。
首先进程间通信(IPC
)有很多种方法,端口通讯(Socket)绝对不是最快的那种。Named pipe 和共享内存都可以做的更好,事实上最快的办法是共享内存+独占 CPU core,可以参考 Java-Chronicle
的实现。这种方法做到几十钠秒的通信延迟是没问题的,见:fastest (low latency) method for Inter Process Communication between Java and C/C++
。
关于端口通讯为什么慢,只要看这篇文章就可以理解:Know your TCP system call sequences
,见下图:
即便你用的是本地接口(localhost),一样要经过系统调用(system call)切换进 kernel space,中间经过无数内存拷贝,协议检查之类浪费时间的事情。这些问题在共享内存方案下都不存在。所以一个简单的做法是重写你们的通信模块,采用这些更快的通信方法。即便你用的是本地接口(localhost),一样要经过系统调用(system call)切换进 kernel space,中间经过无数内存拷贝,协议检查之类浪费时间的事情。这些问题在共享内存方案下都不存在。所以一个简单的做法是重写你们的通信模块,采用这些更快的通信方法。
如果你的摊子实在太烂以至于修改通信代码也是不能承受之重,那么还有一手秘籍叫做替换系统调用。简言之就是用修改函数指针的方式把系统调用入口换成自己的程序,比如用共享内存来实现端口通讯的 API。这样你可以在不改动现有程序的基础上改变通信方式来提高通信速度。具体怎么做可以参考流行的开源实现 OpenOnload
,虽然它主要是替换 TCP/IP 协议栈,但思路是一样的。
其实这个时候是最能体现 Java 等基于虚拟机字节码的语言的优势的。如果你用 Java,那么替换系统调用就很容易,对 JVM 上 instrumentation 即可,嫌麻烦可以直接用 JMockit
之类的库,用法很简单(请允许我对于深陷 C++ 囹圄中苦苦挣扎的同学们表示一下同情)。
回头看了下自己的答案,过于关注在模块化这个编程视角了,没有跟行业需求结合,觉得有些简单粗暴了,修改一下。
@董可人@卢旺彬回答都很详细,分别提到了遗留系统的处理方法和分析多线程和通讯层面的优劣比较,非常有借鉴意义。
补充一下,由于题主对高频系统的具体信息甚少,也不清楚是否只需要接一个交易所,因此大多数人只能按低延迟系统来回答,我这边稍微发散一下,引出几个相关的信息,供大家深入讨论。
首先, 系统各业务功能的模块化与主程序采用什么样的部署运行状态(多线程或多进程)是不矛盾的,在各部分系统用同一种编程语言的前提下,两者可以轻松地同时得到。这也是大家在答案中都提到过的解耦,但如果是多语言开发的系统,彼此之间还是需要数据通讯,或者是多个策略需要共用一个前端数据源,比如交易所只允许接一个连接,多个策略系统要用,可能没办法部署在一台机器上,这样的情况下网络通讯都不可避免,可以升级通过内部网络和机器硬件来处理,换句话说,得具体问题具体分析和优化。
其次,要分析下程序造成延迟的主要原因,或者从需求来讲需要低延迟的主要因素在哪儿,需要具体分析下,任何需求的满足都是要付出代价的,尤其是一些非功能性的需求,有时会导致整个系统成本急剧地上升。在高频交易中,具体高频到什么程度,是否网络间的数据传输延迟是最大的瓶颈?是在内网还是外网,如果是外网,托管机器就可消除网络传输的延迟,如果是内网,改善网络设备和机器也是可行方案,这些相关的环节都是需要分析的问题,曾经我们的一个客户,在这个方面纠结了很久,但后来发现,如果算法上做出些改进,系统一下子减少了上百个ms的延迟,所以抓住主要矛盾才是解决问题的关键。
再次,回归到题主的具体问题,假定只集中在这个问题上, tick data已经有了,分析tick data的模块和tick data采集之间如何交换数据,实际上高频交易数据部分的传输是单向的,tick data的数据采集是生产者,分析部分是消费者,很简单的计算模型,用共享内存的方式即可解决,因为交易所数据是不断推过来的,数据分析部分必须在下一条数据到来之前完成处理,否则看缓冲区的处理方式可能会有堆积或数据丢失,建议采用类似于操作系统对硬件中断的两步处理模式,把分析任务细分解一下,什么时候数据足够了启动分析,和读取数据之间做个平衡处理,在数据分析方面尽可能地改进算法,减少它的延迟才是关键,如果它的计算时间少于tick data数据的间隔,很多复杂的优化工作就可以省了。
最后,一点建议,跟我们最近的一个R语言的策略开发SDK实例相关,R语言层面写的策略只能是单线程的,而后端需要支持多个交易所的行情数据采集源、交易通道接口,必须是多线程,前后之间通过用C++开发R语言扩展包来衔接,中间就是采用的共享内存数据来通讯的,供借鉴参考。
-------以下是原回答 ------------
作为程序员,很不明白楼主的问题,多线程怎么就不能做到代码功能的区分了呢? 模块化跟是否用多线程,还是部署成多个程序,用网络通信没有关系的,主要在于程序员对业务的理解程度,以及能否把这个需求用编程技术优雅地实现出来的功力。
只有一个程序,哪怕源代码只有一个文件,也可以做到很好的模块化。
一点经验分享:
1. 交易系统或是高频交易系统必然是多线程的,原因略去,有时间我再修改加上去。
2. 直接分享我写高频程序的经验(在不影响我策略的基础上尽量分享),仅供参考。
以下所有层都是单独运作,互相之间没有Dependencies,数据区共享。
A : 单向数据取得层,(深度,成交量等..来源:网络,延时时间:不确定),基本是每个数据项开一个“无限” 循环去取数据放入公共数据区(内存数据,如果数据量巨大,可以考虑基于内存的数据库。)
B: 数据加工层: 数据来源,内存(A),延时,基本无延时,数据输出-->>依然是公共数据区,内存。
C: 主逻辑层:数据来源,内存(B 加工后的数据,avg..spread..top value..之类的),根据你的策略作出买卖之类的动作(开启线程D,), 主逻辑层依然做自己的事。
D1: 订单跟踪层: 数据来源,网络,跟踪订单状态及其后续要采取的动作。。
D2:............
D3...........
D4...........
P.S..如果楼主指的是本地到网络的端口,而不是线程之间的端口。那一个普通PC就有好几万。如果还是不够。我们再讨论。
1.怎么感觉在【高频交易】这个话题下的问题的技术水平如此低下?错觉么?
2.既然主要需求是极端高的性能,以及极端低的延迟,那么整个系统的设计原则肯定是:怎么高性能怎么来,怎么简单怎么来。所以:
3.代码功能区分(其实是模块化,低耦合,低硬编码)、多个程序之间端口通讯的方法(难道是端口复用?或者是基于TCP/UDP的进程间通信?)等这些显然会增加延迟的技术,那就肯定不用。
甚至...甚至...甚至....嗯嗯......
4444444.有条件的最好用FPGA、DSP来代替低性能的普通PC机、工作站或服务器,有研发技术实力的研究所或公司甚至可以用Hyper®IFR51(基于光信量子技术的51单片机)来进行超速计算。有条件的单位可以使用微波通信来代替普通光纤网络,有海量资金的高端企业甚至可以通过核心人脉去租用电信最新最保密的军用级P3OEMod25k(基于核子共振技术的25k极速拨号电话网络)来进行超速通信。噗....
题主应该是The Art of Unix Programming这类书看到沟里去了吧,言必称进程,视线程和面向对象为毒物。
1. 不要过度追求“distribution architecture”
2. 坚定不要过度追求
3. 打散你的code,分不同的module
3.1 可以学习C是怎么做的,哪怕是non-OO语言,也是可以打散的
4. 干货时间, martin fowler的microservices: Microservices
4.1 理解透了,你也知道哪些应该“单独”进程,哪些放在一个进程里multi thread处理即可
PS: 我很不喜欢下面的一些答案,不管咋就喷,从我的经验来看我觉得这个问题挺好的:
market data 和 策略order之间的协调是永远的难点之一。
> 多线程系统的延迟小,但不利于代码功能区分
为嘛不利于代码功能区分?
> 多个程序之间端口通讯的方法则代码比较清晰,但显然会增加延迟
增加多大的延迟?是不可接受的么?莫非你的系统需要到微妙以内?
你的目的是解决问题,而不是提出一些不存在的问题。
我们自己开发的系统基本上还是用socket为主,确实考虑到开发成本的问题。如果数据量大的时候,偶发有线程互斥等其他问题。这也是没办法的事情,一来觉得交易所的数据给的就慢。你本地提高几十毫秒未见得对整体系统有多大提升,换其他的方式还需要各种测试。综合来看,socket易用,也便宜。有些问题可以靠算法来优化,只要稳定,国内基本都ok,国内的数据慢,也不用想太多。
All process should be single thread.
You should use cup isolation to pin a process to a core.
They communicate thru shared memory if they are execution related processes, otherwise should be multicast, like risk related processes.