大家好,这里是继续死磕C++的黑犬momo酱。

上周我们聊了“为什么量化的尽头是C++”,很多朋友在后台私信问我推荐C++的学习资料和学习工具 也收获了一些同路人的肯定和支持(十分感谢🙏)。说实话,这一周我复习着《C++ Primer》和网上的教程的笔记,最大的感受不是语法有多难记,而是**“恐惧”**。

以前写Python或Java时,我只关心“功能实现了吗?”;但当你戴上“量化开发(Quant Dev)”的眼镜去重新审视C++最基础的语法时,你会发现:哪怕是一个简单的变量定义,背后都标好了昂贵的“价格”。学完了一周的C++基础,我发现自己以前写的哪里叫代码应该叫CPU的“催命符”,真做交易得落后别人几千年。

本周我们不聊枯燥的if/else、循环抑或是模板类怎么写, 也不粘贴晦涩的代码小片段逐行拆解,这种基础的教程非常了,我们就不需要做重复的事情。我想要做的是,我们站在量化的角度,来聊聊隐藏在基础语法背后的“量化三观”和我的一些新认知,希望也能帮各位读者对QD日常工作有一些直观的印象。

💡如果有一些C++或者量化术语你不熟悉,没关系,重点是体会在发生什么,相关的知识我以后会有更细致的分享。

1. 变量“住”哪?(Stack vs Heap)

如果是做Web和前端开发,你可能不太在乎变量存在哪;但在高频交易里,住错了地方就是“事故”。

初学C++最容易滥用的是 new 关键字,或者malloc很多自己没有办法管理的内存。

想象这样一个场景,比如来了一个行情Tick,习惯性写一句 Order* o = new Order();。

在普通软件里这没问题,但在量化系统的**热路径(Hot Path, 简单理解为从接收到行情到策略计算结束发出报单的代码执行路径)**上,这是大忌。

  • 堆(Heap)的代价new 需要操作系统在杂乱的内存堆里帮你找空地,这不仅慢,而且时间不可控(可能这次1微秒,下次100微秒)。这种“抖动”对于追求微秒级响应的策略是致命的。

  • 栈(Stack)的优势:栈上的内存分配几乎是零成本的(指针移动一下而已)。

我的新认知:

在量化里,能用栈(局部变量)就绝不用堆。如果必须用动态内存,必须预先分配好内存池(Memory Pool),池化组件是一个好东西,当你没有性能优化方案的灵感时,先想想pool。

2. 浮点数的“谎言”

我们来聊聊钱。

如果你在很多编程语言里计算 0.1 + 0.2,结果往往不是 0.3,而是一个无限接近的小数。

基础语法告诉我们有 float 和 double。

但实战经验会告诉你:别信它们。

  • 精度陷阱:在金融里,一分钱的对账误差都可能导致系统熔断。double 的底层二进制表示注定会有精度损失。

  • 行业惯例:为什么很多交易所推过来的数据(FIX/二进制流)里,价格是整型(int64)?

    • 比如茅台价格 1500.23,传输过来往往是 15002300(单位是微元)。

我的新认知:

尽量使用**定点数(Fixed Point)**思维。用 int64_t 来存价格,算完了再除回去。

3. NO COPY,只传References

C++最迷人也最基础的特性之一:引用(Reference **&**)。

假设你有一个包含全市场5000只股票快照的大对象 MarketSnapshot。

你需要写一个函数检查风控:void CheckRisk(MarketSnapshot snap)。

  • 萌新写法(值传递):不加 &。C++会尽职尽责地把这5000只股票的数据完整拷贝一份给函数。CPU 疯了,内存爆了,延迟炸了。

  • 量化写法(引用传递)void CheckRisk(const MarketSnapshot& snap)。加了 const &,相当于只给函数一个“眼神”或者“遥控器”,零拷贝,直接操作原数据,且保证不被修改。

我的新认知:

C++程序员需要培养一种强迫症,看到复杂对象传参不带 &,就像看到钱掉在地上一样难受。

4. Vector是朋友,Map是杀手

C++教科书说查找要用 **std::map** (红黑树),因为它是 O(logN);**std::vector** 是 O(N),太慢。

但在HFT场景下(比如处理Level 2行情的十档买卖盘),这个结论往往是错的

  • 缓存友好性(Cache Locality)

    • Vector 的数据在内存里是排排坐的,CPU读取时可以一次性预取(Prefetch)很多,极快。
    • Map 的节点散落在内存各处,CPU读它需要跳来跳去,Cache Miss(缓存未命中)极其严重。

我的新认知:

在数据量不大(几十上百个元素)时,遍历一个 vector 往往比在 map 里查找要快得多。

5. 异常处理 (Exception):热路径上的“禁区”

C++教科书说遇到错误用 try...catch,优雅又安全。

但是在量化实战的Tick-to-Trade(行情触发交易)场景下,严禁抛出异常。

实际上C++的异常机制(Stack Unwinding)非常昂贵。一旦抛出异常,CPU需要花费大量周期去回溯堆栈,这对于微秒级的策略是毁灭性的。此外,异常的存在会阻碍编译器进行某些激进的优化。

真实的实盘交易的代码,总是在用if和return。

我的新认知:

在量化里,Performance > Elegance,速度永远第一顺位。


当然,C++语法基础还有很多部分譬如指针、模板类、CRTP、惯用法等等很多细碎的话题,限于篇幅我们不可能把所有和量化相关的细节都盘一遍。

重要的是,这一周的学习让我明白,所谓的**“量化开发门槛”,并不在于你能背多少个C++关键字,而在于你是否具备“硬件思维”**,关键是需要对自己每写的一行代码的准确性和性能负责,作为QD,我们的使命就是让程序跑的又快又准,而不是仅仅实现一个可用功能外加代码优雅而已(至少我感觉量化工作中并不追求代码要优雅,工程落地能力>代码规范)。

📂** 学习资源与代码归档:**

🔗 GitHub: https://github.com/shuheng-mo/qd-study-plan-104wk.git

下周预告:W03 - C++的OOP魔法(基础)

👋下周五见,我是还在死磕指针和红黑树的黑犬momo酱。

🙋提问:各位开发者在写代码的时候,什么时候考虑性能最优化?还是并不care?