Halo
发布于 2022-05-16 / 133 阅读 / 0 评论 / 0 点赞

c++ 低延迟系统

测量延迟

要控制和降低延迟,首先要能准确测量延迟.

  1. 首先用 NTP 同步机器的时间
  2. 同一机房两台机器用 ntp 同步后, 也会存在毫秒级的差异, 需要设法补偿这个时差.

网络延迟

网络延迟是延迟方面最大的瓶颈之一.
网络延迟分传输延迟和惯性延迟.

  • 输延迟是传送1字节消息的基本延迟,大致跟距离成正比, 广域网经常会发生.
  • 固有延时也是惯性延时,是任何电子器件都存在的一种延时特性,主要物理机制是分布电容效应. 惯性延迟跟消息大小成正比,跟网络带宽成反比, 局域网经常发生.

降低网络延迟的最好效果提升方法有

  • colo, 需要物理上与交易所的撮合机越近越好
  • 高的带宽和最快的nic卡及其模式(比如选择合适的openload模式)
  • 必要的话可以减小消息长度。举例来说,要发10k的消息,先花20us CPU时间,压缩到3k,接收端再花10us解压缩,一共“60us+传输延迟”,这比直接发送10k消息花“100us+传输延迟”要快一点点

系统资源带来的延迟

做高吞吐的服务程序,会尽量用完 cpu 和 io 等, 同时未来避免请求消息丢失会使用队列. 不富裕的 cpu 和 io 和队列会带来不可避免的等待和延迟.
因此吞吐量和系统资源要做一个协调

设法保证关键服务进程的资源充裕,避免侵占(主要是CPU和网络带宽)

比如把服务器的日志文件拷到别的机器会占用网络带宽,一个办法是慢速拷贝,写个程序,故意降低拷贝速度,每50毫秒拷贝50kB,这样用时间换带宽

程序内部延迟

普通的C++服务程序,内部延迟(从进程收到消息到进程发出消息)做到几百微秒(即亚毫秒级)是不需要特殊的努力的.

减少锁

减少锁和同步. 能用单线程解决问题,就千万不要多线程。

减少 run-time 的额外处理

  • 编译时处理多态. 能用 CRTP/expression 的地方就别用 dynamic polymorphism

  • 使用 memory pool 和 placement new, 尽量避免 memory allocation 带来的 overhead 和 memory fragmentation

  • 可以考虑重复使用同类的 object

  • 尽量用静态链接, 静态链接会比动态链接有百分之几的性能提升.

  • 使用多线程的话, 使用线程池, 动态创建/销毁线程会对延迟产生很大的影响.

  • 要了解自己待处理的数据,这样在一定条件下可以允许 undefined behavior 的存在。比如,vector[] 不做边界检查 vs vector.at() 做边界检查处理。对于一个 sub-microsecond 级别的系统,safety check 有时候都会 expensive.

利用好 cache

基本的规则大概就是: 能在cache里面存下data和instructions,就不用access main memory,能在registers里面存下,就不要access cache。

  • 尽量使用 contiguous blocks of memory,这也是为什么Bjarne Stroustrup本人也会推荐大家优先考虑使用 vector。

  • 分支处理要尽可能多执行代码, 由于多次走同样的代码, 提高了系统代码执行的 cache, 下次真正执行的效率就会比较高.

  • 大多数情况下,大家还是会首选用STL里面的container,但是还是需要谨慎,比如std::undered_map的性能对于低时延系统就不够用。std::unordered_map插入、删除的性能会比std::map稍差一点,但是查找速度std::unordered_map和std::map的比例基本上是2:1

利用好编译器优化

  • 变量的作用域尽可能小,尽可能用 const ,连接性也尽可能低. 让编译器知道更多,编译器知道更多的信息,就可能帮你做更多的优化.

评论