
运行时与JIT优化
高频交易对延迟极其敏感,.NET的垃圾回收和即时编译机制必须深度定制。采用Server GC模式,配合ConcurrentGC的容忍度设置,减少STW暂停。Tiered Compilation需禁用或调整,因为分层编译的热点识别延迟会干扰确定性。使用ReadyToRun预编译镜像减少启动JIT,但高频场景更多依赖Tiered PGO配合Server GC的协作。
必须掌握RuntimeConfigurationOptions中的System.GC.Server、System.GC.Concurrent、System.GC.RetainVM等开关。System.Runtime.Intrinsics提供了SIMD向量化指令,用于行情数据处理。JIT的DOTNET_TieredPGO可引导基于配置文件的优化,但需测试PGO带来的代码膨胀与延迟分布影响。

内存与数据结构
延迟主要来自缓存未命中和内存分配。使用Span<T>、Memory<T>、ArrayPool<T>消除堆分配。对象池化是标配,但需注意池化对象的伪共享。定义结构体布局时,使用StructLayout(LayoutKind.Sequential)和Pack控制缓存行大小。
无锁数据结构必不可少。实现无锁队列时,采用Michael-Scott队列或基于Interlocked的SPSC队列。System.Threading.Channels的UnboundedChannel不适合低延迟,需自建环形缓冲区。也可引入Disruptor.NET,其通过序列号屏障机制实现高吞吐。
内存屏障必须显式控制,Volatile.Read/Write和Thread.MemoryBarrier用于保证顺序。高频场景下,System.Threading.Volatile比锁更适用。还需注意MemoryOrder与Acquire/Release语义,正确处理乱序执行。
网络与I/O
网络层使用SocketAsyncEventArgs异步套接字,配合Socket.ReceiveFromAsync实现零拷贝接收。禁用Nagle算法并设置TCP_NODELAY,操作系统层面启用SO_REUSEADDR。使用AF_UNIX域套接字进行进程间通信时,相较TCP减少协议栈开销。
Netty风格的事件循环模型可复用到.NET,通过单线程处理连接。使用IOCompletionPort绑定到专用线程,减少上下文切换。行情分发采用UDP组播,用SocketAsyncEventArgs接收,避免UdpClient的缓存开销。
硬件时间戳是高频交易的核心,使用PTP设备时钟,并通过IOCTL获取网卡时间戳。普通DateTime.UtcNow精度不够,需用QueryPerformanceCounter和Stopwatch.GetTimestamp()。对于纳秒级时钟,可调用GetSystemTimePreciseAsFileTime。
并发与调度
线程模型采用专属线程处理关键路径,例如一个线程负责行情解析,另一个负责订单处理。使用ThreadPool会导致不可控的调度延迟,应使用TaskCreationOptions.LongRunning或直接创建Thread并设置优先级为AboveNormal。
锁竞争要竭力避免,引用计数用Interlocked.Increment/Decrement。生产者-消费者模式中,采用双缓冲机制:消费者访问当前快照,生产者更新备用缓冲区,通过内存屏障切换。使用SpinWait或SpinLock处理短暂等待,但注意在高冲突下让出CPU。
对于多核并发,使用ConcurrentDictionary等线程安全集合时,需评估其内部锁和CAS开销,必要时自建分片锁表。核心热路径必须做到无锁化和无系统调用。
第三方组件与工具
低延迟消息中间件常选择ZeroMQ(zeromq.dll)或NetMQ,其性能优于RabbitMQ。但注意ZeroMQ的IO线程与用户线程交互成本,可用zmq_poll或DEALER/ROUTER模式优化。
内存数据库Redis配合StackExchange.Redis,需注意其连接复用和管道化操作。也常使用InMemory的ConcurrentDictionary配合MemoryCache作为本地缓存。
FIX协议引擎可用QuickFIX/n,但高频场景最好自行定制,基于Span<byte>解析。C++互操作通过P/Invoke或C++/CLI混合模式,将核心逻辑置于原生层,但需避免GC handle编组。
性能监测与剖析
使用EventPipe和dotnet-trace收集运行时事件,分析GC分配和JIT编译。PerfView可查看CPU采样和线程调度延迟。集成ETW事件跟踪系统,监控网络丢包和socket接收时间。
构建实时监控仪表盘,采集关键指标:进程线程上下文切换次数、锁等待时间、GC暂停时长、网络往返延迟。使用System.Diagnostics.Metrics暴露自定义计数器,如处理百万笔行情耗时。
基准测试须采用BenchmarkDotNet,其自动处理预热和统计。测试必须隔离干扰,例如设置处理器亲和性,使用Process.ProcessorAffinity绑定到固定核心。
硬件与部署
CPU选择高频时钟型号,利于超线程但需禁用HT避免缓存共享。内存使用DDR4低时序。网卡采用Solarflare或Mellanox支持内核旁路,使用AF_XDP或DPDK,但.NET与DPDK集成需C++胶水。
部署环境风火电分离,使用VM或容器但需直通设备。启用Ryzen的NUMA感知,分配内存靠近心脏。用System.NUMA指定节点。
实战注意事项
避免使用async/await在关键路径,因状态机分配和调度开销。将整个交易流程封装为纯同步循环,利用SpinWait.SpinUntil等待订单回报。
使用System.Reflection.Emit动态生成IL代码,为特定合约定制计算逻辑,减少击虚调用。但需注意生成的IL与JIT的交互。
消息序列化放弃System.Text.Json,改用MessagePack或自定义二进制协议。反序列化时用Unsafe类读取字节,消除边界检查,但需确保安全。
.NET在高频交易中的低延迟实践需要深度理解CLR底层和硬件特性。通过合理架构和每一项技术的定制,.NET完全可以实现微秒级延迟。但必须持续剖析和调优,因为延迟的波动源于细微的系统行为。