发布于: 修改于:雪球转发:0回复:11喜欢:4

$恒生电子(SH600570)$

这篇文章要单列,我更多是看个热闹,门道是看不出来的。

但在过去研究恒生的这2多个月里,我有一个切身体会,恒生关于技术、产品、管理等方面的文章,且多数由高管、首席、专家们署名发表的文章,非常多。有些是单篇,有些是系列,比如前天分享的LIGHTDB系列,还是O32操作手册、白皮书等,LIGHT洞察系列等等。

基于我非常有限的阅历,个人粗暴的认为,这是国内极少优秀公司的应有表现。

特别是对于一家TO B型的软件公司,这非常的难得。刘董认为恒生是工程师文化,软件工程师相对来讲,是更擅长技术而非文档的。从恒生人员构成看,销售人员才400多人,占比才3%点多;销售人员更擅长写文章、PPT。

刚好,也是不知道是不是碰巧,恒生的股权结构(蚂蚁金服未实际经营管理),可能是A股里面,最接近HW股权模式的公司之一了。HW由于有企业TO B业务,在管理、技术、产品方面的分享方面,做的也是非常之好,输出的文档、书籍都是业界广为传阅的。

从气质上,恒生对标HW IT业务,还是有那么一点像的。这非常的不容易,2个月前,我完全没想到恒生是这样的一家公司。公司文化、质量方面,有些超预期。

恒生首席技术专家毛银杰:全方位分析低延时交易的性能新极限 2022-07-18

2022年7月8日,QECon全球软件质量&效能大会在深圳召开。本次大会聚焦研发效能、数字驱动、质量工程、数智化测试等领域,聚集了20多位企业一线技术委员会专家共同策划和推动国内软件质量效能方向的技术交流、发展。
以下为演讲实录的裁剪转发:
...分享内容主要分纳秒级交易技术的背景、影响要素、瓶颈定位、性能评估、未来挑战和展望。

“机构交易对低延迟的要求是永无止境的”

在金融证券领域,机构交易的占比是逐年提升的。//散户长期存活不容易呀
以往金融证券领域高性能一般来说都是指高吞吐量,追求在满足一定延迟要求下的高吞吐量。
但随着机构交易的占比逐渐提升,对机构交易来说,时间就是金钱,所以对延迟的要求是逐步不停地在提升,可以说是永无止境。由此也导致了对券商来说是新一轮的军备竞赛—谁能够在延迟上取得最低的延迟,谁就可以掌握客户。//机构们是不是想把自己变成机器人?
在信创背景下,随着机构交易对极致低延迟需求的提出,全内存化交易未来将是一个趋势。

影响延迟的关键因素有哪些?

对一个已经比较合理设计的,并且算法上也已经过优化的去DB(数据库)的内存系统来说,影响延迟的因素有哪些呢?我们这里总结了一下,从用户有感的角度出发排序:第一个是网络通信。网络通信一般来说是整个全链路交易延迟当中的大头,可能占了百分之五六十以上的延迟。
另外一个是业务逻辑处理当中不可避免的会存在一些锁的操作。锁的操作开销取决于这个锁的类型以及锁的粒度,它的延迟可能是纳秒级,也有可能是微秒级,但是对一个设计不当的系统来说,甚至可以到达毫秒级。
再一个是我们要尽量的避免陷入内核。因为每一次陷入内核,它都是一个近乎微秒级的延迟。
而我们现在在这里讲的性能都是全链路交易延迟,是要达到个位数的微秒级,所以每一次陷入内核对我们来说就是一个灾难。
再一个是要尽量的避免线程切换。因为线程切换实际上也是有陷入内核的,并且它还会有上下文的切换,这个开销挺大的。
除去前面的这些,后面实际上是一些我们可能会比较忽略的内存申请,这个延迟可能它也是不等的。
刚才所讲的网络延迟是一个大方面,我分析一下网络延迟的开销。网络延迟实际上我们可以把它分为三部分。一是传输延迟。传输延迟就是说我把报文放到介质上所需要的时间,那个是受限于带宽‘;二是传播延迟,就是从A地传输到 B地所花费的时间;再有就是在中间可能会有经过各种网络设备的交换延迟。举个例,假设是个千兆带宽,100个字节,传输 1000 公里,传输延迟大概就是在 1微秒左右。然后传播延迟会达到几毫秒,大概是 4、5 毫秒。交换延迟就视中间的交换设备而定,每增加一个交换设备,那理论上就增加1微秒左右的一个延迟。
但是这里面的传输延迟1微秒其实是一个很理想化的数字。因为我们这里面已经忽略了协议栈的处理延迟,其实这里面协议栈的处理延迟是一个大头。
那么如何避免这个协议栈的处理延迟?这里面可能就涉及到一个Kernel Bypass技术,再一个是锁。实际上一方面我们要考虑的是锁的固有延迟开销。比如说锁的类型有读写锁、互斥锁、自旋锁,这里面锁的固有延迟开销,自旋锁最低,它可能是只有几个纳秒,也就是说几个时钟周期。
互斥锁是其次,读写锁的固有开销是最大的。但是我们不能够只考虑它的固有延迟开销,我们可能还需要考虑到锁的争用频度和粒度。
如果结合业务上下文来考虑,我们会发现哪怕我们用的是自旋锁(因为自旋锁只有几纳秒的一个延迟开销,成本是最低的),但是如果考虑到我们的业务逻辑,锁的粒度如果比较粗,并且它的争用频度比较高,那么使用自旋锁可能对整个系统来说是一场灾难,大量的 CPU 都消耗在无谓的等待这把自旋锁。
再一个我们要考虑到如果加了锁,实际上一般来说是会增加一个线程切换的。

系统瓶颈出现在哪里?

目前系统已经达到了一定的瓶颈,如何根据当前系统的表现,根据观察一些现象,来判断瓶颈大概出现在哪里?

一般来说我们可以从这几方面来判断:一个就是还是从网络带宽来判断,因为网络肯定是个最大的瓶颈。再一个,对于一个可能实现不是很优的系统来说,可能还会有个磁盘I/O。之所以前面没有把磁盘I/O列上去,是因为在我们做微秒级应用,一般来说不太会去考虑磁盘I/O。因为如果你有了磁盘I/O, 你这个性能估计就会不达标。所以我前面没有把它放上去,如果有了大量的磁盘I/O,那整个系统肯定是不能够达标的。再一个是我们来看当前的 CPU 的利用率是怎样的,根据 CPU 利用率大概的来做一下判断。
有了前面这些表象的判断之后,我们再来分析我们自己内部的应用实现当中,我们的线程亲缘性是怎样的?我们是怎样来使用我们的内存的?我们大概需要多少的内存?这个内存的数据结构、表达形式是怎样的?从这方面来考虑、来分析大概的瓶颈。
比如说我们从带宽上来说,一般来说我们追求的是低延迟,并且我们要追求大吞吐量。那要追求大吞吐量,必然我们的网络带宽我们希望越高越好,否则就说明我们的吞吐量上不去。但是当网络带宽高的时候,其实我们的单笔延迟势必是受影响的,必然会对单笔延迟会有一定的冲击。
还有一个是我们追求的是能够使得网络带宽能够打上去,但是网络带宽打上去的时候,真的吞吐量上升了吗?其实是要打个问号的。因为这里面我们就要关心这个带宽流量是有效载荷还是无效载荷,这里边的有效无效的一个比例大概是怎样。一方面,有可能这里面的带宽都是由于大量的重传导致的,因为我们自己对延迟要求比较高,可能会有一个在比较短的时间之内就会有一个重传。那么实际上是不是这里面大量的重传导致了带宽的上升?还有一个是我们的应用层协议是不是设计合理的,是不是有效数据在整个的报文当中占比过小?如果是这样,是不是可以通过压缩来提升性能?那这里面要评估的是,因为我的压缩是需要消耗 CPU 的,在 CPU 的消耗和网络流量降低之间,这两个能不能取得一个收益的平衡?网络比较好的值是达到百分之五六十,那一般会认为可能单笔延迟是可以得到保障的,因为网络不忙,整体不忙。但实际上我们所看到的,很多的监控工具、运维分析工具所看到的网络带宽只有百分之五六十,并且是一条水平的,整个表现都很好,没有高峰也没有低谷。但实际上我们这个时候也还要问一下自己,因为我们追求的是微秒级,这是真实的现状吗?这里面可能会涉及到网络微爆的产生,就是说在某个极短的时间之内,我们的网络流量其实是有个突破的尖峰的,只是我们的运维没有观测到。然后还有一个是我们来看 CPU。对于低延迟,一般来说当我们的系统达到瓶颈时,我们总能够看到CPU是有一个高峰的。但是这里面的高峰我们就要考虑到,对于多核系统来说,是所有的核都忙了,还是说只忙了仅仅几个核?然后在所有的核都忙的情况下,那这里边是不是我的工作不停地在每个核之间做切换,也就是说不能够达到CPU的线程亲缘性,突破了它的亲缘性会导致很大的问题,后面我们会讲到。再一个是如果说CPU忙,它是真的忙了吗?忙是在做有效的业务吗?还是实际上是内耗掉了?因为线程切换太多,实际上他在做大量的系统的切换,CPU不是在为我们业务干活,而是在为他自己内部的一些切换内耗掉了。
刚才讲到是不是在做大量的线程切换,这里面其实也牵扯到一个问题,就是做内存访问的一个特性。
内存访问,寄存器的访问最快,因为它是跟 CPU 周期数相等,那么我们认为是小于纳秒级,小于 1 纳秒。第一个一级缓存是1纳秒;二级10纳秒,三级在20纳秒,主存大概是在50纳秒,这样一个性能指标。
然后我们再考虑NUMA架构,如果说是一个远端内存,那肯定还要在50 纳秒之上再加个几十纳秒,会达到百纳秒这个级别。那么线程亲缘性就是在利用局部性原理来增加它的一个缓存命中率,尽可能使得内存访问都在使用寄存器和一二级缓存,而不要每次都去做一个真实的内存访问。
那除去刚才的缓存亲缘性这个之外,可能还会有一个就是我们所谓的TLB颠簸。因为当我如果说我的工作集,前面也有一点就是我的工作集大小,工作集内存越大,那么所需要使用的页表就越多,但是页表它也是需要被寻址的,这个资源是受限的。
如果说页表本身需要再次被寻址,那这里面就会有个 TLB 动作,那他的寻址的周期数时间延迟可能就会达到几十微秒甚至于到毫秒级。

如何对系统做出合理评估

有了前面的那些性能上面的数据之后,我们再来看一下怎么来对系统做一个大概的性能评估。首先根据带宽以及传输的路径和中间所经过的网络设备,就是整个的网络拓扑图,大概可以预估出网络延迟大概是多少。再一个根据这个磁盘I/O,也就是尽量避免磁盘I/O,但是一般来说会有一些紧急性的日志之类的。那这里面我要来评估一下,大概来做一个持久化,它的一个I/O 延迟会是多少?但是一般来说,这个磁盘I/O都是异步的,所以基本上是可以忽略的。
再一个是我根据自己业务的特性、数据结构、需要进行一笔完整的业务、它的工作集大小是多少,可以再根据缓存的大小、自己主机缓存的大小和工作集的大小,大概可以评估出应该具备的合理的缓存命中率是多少。
那么有了这个缓存命中率和工作集大小,大概就可以评估出内存访问的时间应该是多少。然后根据这些之后,就可以找到在全链路上延迟最大的瓶颈在哪里,并且合理的全链路延迟大概是多少。
其实对一个最简单的一个 echo 服务,客户端到服务端的一个 echo 服务,客户端发送一个数据,服务端回送一个数据,这是一个最简单的案例,实际上这里面把它的线程模型拆解成三种形式。
第一种是最简单的,客户端跟服务端大家都是一个线程。服务端一直都是一个线程不动,然后客户端的线程模型变一下,变成这三种模型。
这里的数据就是延迟差异相当大。最慢的是第一种,线程模型1,这个延迟是最慢的,它的 RTT 会达到四五微秒左右。然后最快的应该是线程模型2,这是最快的,大概会在三微秒左右。就是说好的跟差的大概能达到有个接近两微秒的性能差异。
可能被我们忽略掉的一个延迟的大头是在于内存访问,要对内存访问做一个严格的规划。但是我们一般在做开发的时候,大家都会觉得内存是个很快的,因为我们的直觉也告诉我们内存访问是很快的。那我们怎么来真正获得一个内存访问的延迟呢?
我们觉得内存很快是因为实际上我们的内存访问的延迟绝大部分都被系统给隐藏掉了,一部分是 OS, 一部分是硬件层,都被隐藏掉了。那我们这边通过一个测试案例来获得一个真正的内存访问。就通过这样子的我根据一个步长,每间隔一个步长来访问一个字节,然后回过头来把整个4G全部访问完,和我顺序访问他的时间差距相当大,这里边的数据我不给了。
还有一个是矩阵乘法的案例。一般的传统的矩阵乘法就是我这张图里边的左边部分, A 乘 B 获得一个 C 。那这样子的一个矩阵乘,它实际上是突破了局部性原理,破坏了局部性原理。

然后我把它转换成先把B做转置,然后A跟B 的转置做这样一个另外一种模式的乘,这样子它就很好地利用了局部性原理。

我们可以看它的一个时间性能,随着你矩阵的增加,他这两种的一个性能差距是很明显的,到最后能够有一个数量级的提升。当然这只是一个最简单的一块,实际上还可以再做更进一步的优化,更好地利用他的一个局部性原理。

软硬结合,突破性能天花板

从软件层面而言,性能到了极致之后,内存访问的瓶颈最终是不可消除的,除非材料科学的突破,否则内存访问延迟是不可能降低的。

另外,在后摩尔时代,实际上CPU主频的发展其实已经跟不上我们业务对延迟需求的发展了。再是,其实这样下去,大家各厂商的性能趋势是趋于雷同的。

所以我们可以发现,为了追求极致的低延迟,都有一个单体式应用的趋势。这实际上是在反分布式这个趋势的。那这里面我们为什么会有这个单体式应用,就是因为网络通信所造成的——每增加一个节点,总是要增加几个微秒的延迟,这对于低延迟来说是不可承受的。

但是如果利用 FPGA、利用硬件,实际上我中间每加入一个节点,它的延迟大概也就在160纳秒左右,而160 纳秒其实已经跟本地内存访问相差无几了。那么有了这个基础之后,我就完全可以软件和硬件结合。

我们有一个FPGA的开发平台。在FPGA平台上面,本身每个主机上面可以做一个软硬分离,硬件部分在硬件做,然后复杂逻辑穿透PCIE到达软件部分,再通过FPGA网口和另外一个FPGA进行交互。

全部讨论

别被迷惑了,我是恒生高性能被裁的

这货魔障了,

06-27 12:25

这类提高交易性能的技术十年前就在谷歌阿里腾讯系里广泛使用了,之前那位推顶点的行内人士已经点出了问题的关键,你稍微懂一点技术可能就躲过了这次的新低

06-27 09:13

2022年对于交易性能,已有这样全面的认知,怎么可能在性能这个指标上,落后顶点太多,或者说会落后,我觉得不太可能。