4. 批量计算框架选型与对比

逐笔成交数据的特点是“行数极多、单行字段少、计算逻辑相对固定”。一条典型的逐笔记录可能只有证券代码、成交时间、成交价格、成交数量、买卖方向等十来个字段,但一天全市场的数据量可达数千万行甚至上亿行。面对这种数据形态,选型的核心问题不是“哪个框架更先进”,而是“计算密度与数据规模是否匹配框架的调度开销”。本节围绕向量化计算、多进程并行、分布式计算框架(Dask、Spark)三条路线,结合逐笔数据的实际特征给出可操作的决策依据。

三条技术路线的本质差异

向量化计算的本质是把逐行循环改写为整列数组运算。以 Pandas/NumPy 为例,计算每笔成交的成交金额只需 df["price"] * df["volume"],底层由 C 循环完成,避免了 Python 解释器逐行调度的开销。它的优势在于单机内存可容纳数据时性能极高,代码也最简洁;瓶颈在于内存上限和单线程执行——当数据超过内存,或单列计算已经吃满一个核时,向量化本身无法继续加速。

多进程并行是在向量化之上做“横向切分”。典型做法是按证券代码或按时间分片,把数据切成若干块,用 multiprocessing 或 concurrent.futures 分发到多个进程,每个进程内部仍执行向量化逻辑,最后合并结果。它解决的是“单核跑不满”的问题,代价是进程间数据拷贝和结果合并的开销,以及分片边界处需要小心处理的状态(例如按时间切分时,跨片的滚动窗口需要额外对齐)。

分布式计算框架把切分、调度、容错、落盘都交给框架。Dask 的 DataFrame 接口与 Pandas 高度相似,适合在单机多核到小集群之间平滑扩展;Spark 则以 JVM 为基础、以 RDD/DataFrame 为抽象,适合多机、超大数据量、需要与数仓生态打通的场景。两者的共同代价是调度与序列化开销:对于几百万行的小数据,启动一个 Spark 作业的时间可能远超实际计算时间。

逐笔特征批量计算:框架选型决策路径 输入:单日逐笔成交数据 数据能否全部 装入单机内存? 是 向量化计算 Pandas / NumPy 单机单线程,最快落地 否 单机多核能否 在时限内算完? 能 多进程并行 按代码/时间分片 进程内仍向量化 不能 是否多机 / 需与 数仓生态打通? 否 Dask Pandas 风格接口 单机到小集群平滑扩展 是 Spark 多机分布式、容错 对接 Hive / 数据湖 判断依据:数据量、内存上限、时限要求、集群资源与生态依赖

逐笔数据下的性能对比要点

把三条路线放到同一份逐笔数据上比较,需要关注四个量:数据规模、单核计算耗时、调度开销、结果合并成本。假设某日单市场逐笔成交约 3000 万行、约 2GB 内存占用,计算 20 个特征(成交金额、买卖方向符号化、按秒聚合的成交量、滚动 20 笔均价等)。在单机 8 核、64GB 内存的机器上,纯向量化实现可能耗时数十秒到数分钟;多进程按证券代码切成 8 片后,理论加速接近线性,但受限于最慢分片和合并开销,实际加速比通常在 4~6 倍;Dask 在单机上与多进程量级相近,但额外承担了任务图构建与调度开销,小数据下反而略慢;Spark 在单机模式下启动与序列化开销明显,只有在数据量达到数十 GB 以上、或需要多机并行时才体现优势。

需要强调的是,上述数字属于教学示例中的合理假设,用于说明量级关系,实际性能必须在本机用真实数据基准测试。一个实用的基准方法是:固定同一份逐笔样本,分别用三种实现跑同一套特征,记录“读入—计算—写出”的端到端时间,并单独记录调度与合并耗时,避免把 I/O 时间误算成计算加速。

选型决策的实操判断

决策可以按以下顺序自问:第一,数据能否装进单机内存?能,优先向量化,把逐行循环全部改写为列运算,这是性价比最高的优化。第二,装不进内存但单机多核可在时限内完成?选多进程,按证券代码分片最自然,因为逐笔特征大多在单只证券内部计算,分片边界清晰;若特征含跨证券的横截面计算,则需在合并阶段补一步全局聚合。第三,单机多核仍不够,且团队已有集群与数仓,选 Spark;若只是单机内存不够、希望保留 Pandas 写法,选 Dask 更轻。

分片键的选择直接影响正确性:按证券代码分片时,单只证券的逐笔序列完整落在同一分片,滚动窗口不会跨片;按时间分片时,窗口需要向相邻分片多取一段“预热数据”,否则每片开头的滚动特征会缺失或错误。

一个可复用的选型对照

维度向量化多进程DaskSpark
适用数据规模单机内存内单机内存的数倍单机到小集群数十 GB 以上、多机
代码改动量最小中,需分片与合并小,接口近似 Pandas大,需改写为 DataFrame API
主要开销单核算力进程拷贝与合并任务图调度启动、序列化、Shuffle
容错能力无需自行处理中等强
典型场景单日单市场特征多日批量回算研究环境扩展生产级全历史流水线

落到工程实践,建议采用“先向量化、再并行、最后分布式”的渐进路线:先把特征逻辑写成纯列运算并做单元测试,保证单分片结果正确;再用多进程包装成可分片的批处理函数,验证分片与合并后结果与单机一致;只有当数据规模或时限确实超出单机能力时,才把同一套逻辑迁移到 Dask 或 Spark。这样迁移成本最低,也避免了在小数据上过早引入分布式框架带来的调度负担。