跳转至

文件写出布局(row_stream / column_buffered / column_chunked)

适用读者
  • 用 Python IR / ExecutionRequest 写宽表 Excel、关心峰值内存的使用方
  • 写 YAML books 报表、误以为有 streaming knobs 的集成方
  • 需要给调用方选型建议的 agent / 维护者

本文是写出布局选型的 SSOT(人类阅读页)。闭集 OutputWriteLayoutrow_stream / column_buffered / column_chunked)只挂在 Python 侧(DemandRunRuntimeOptions / ExecutionRequest),YAML authoring 禁止使用。未设置时由 streaming + excel_column_residency 推导,默认结果与下表一致。

更严的契约以 llmanspec/specs/runtime-output-write-layout/output-sink-contractsyaml-dsl-runtime-policy-boundary 为准。
Agent 入口:agentdev/skills/scalim-yaml-dsl/references/streaming-column-excel-guidance.md;迁移卡:.../upgrades/2026-08-11-output-write-layout.md

0. 交互演示

页面已加载脚本,下面四块可以点开看;没有脚本也不影响阅读后文的表格与 mermaid。

  • 写出时间线:切换三种 layout,看格子如何进内存、刷盘
  • 选型树:按 YAML books / csv / excel / 峰值选路,得到推荐值或 fail-fast
  • 峰值对照:宽表证据条
  • 工厂映射:layout 对应的具体 sink

1. 先分清三条路径

路径 典型入口 实际实现 峰值特征
A. YAML / workflow books resources.books + outputs.to 组合层强制行写出 → ExcelSink / workbook sheet 行流式;不是列式 sink
B. IR 列式 buffered(默认) OutputSpec(format="excel", streaming=False) ColumnExcelSink(layout=column_buffered 列缓存到 close;宽表在 close 前(pre_close)驻留可很高
C. IR 列式 chunked(opt-in) 同上 + OutputWriteLayout.COLUMN_CHUNKED StreamingColumnExcelSink 按行窗刷盘释放;宽表 peak 可大幅下降

证据来自双跑对拍。100k×300 的宽表:buffered 峰值 RSS ≈ 3.59GB,多 batch chunked ≈ 0.12GB,降幅约 97%。中量级(约 2万–5万行 × 50–100 列):buffered 的峰值 RSS 约为 chunked 的 2.5–4.9 倍,墙钟耗时相差约 2%,两者写出的行数一致。

产物在本地 .tmp/evidence/(不入库),复现脚本 scripts/bench_output_write_layout_dual_run.py;通俗说明见 llmanspec/notplan/c40-output-write-layout-advisory/research/write-layout-advisory-explainer.html

两处内存:pipeline 提早释放 ≠ sink 提早释放

pipeline 的"边写边算、写完即放"(列式删字段、行式删行)删除的是 BatchContext 那份值;sink 从 write_column 拿到的是副本column_bufferedColumnExcelSink 把副本缓存到 close() 才写盘释放——端到端峰值由 sink 主导,pipeline 侧省下的内存救不了 buffered 的峰值;要真降峰得用 column_chunked

2. 策略怎么选

推荐默认:column_buffered

  • 列数/行数不大,或机器内存充足
  • 需要与历史 ColumnExcelSink 行为完全一致
  • 不要改默认;未设置 layout / residency 时即为 column_buffered

何时启用:column_chunked

同时满足:

  1. 使用 列式 Excel:format=excelstreaming=False(或框架工厂走该分支)
  2. 宽表 / 高行数导致 pre_close / peak RSS 不可接受(启发式:约 n_fields × n_rows ≳ 1e6 时可优先考虑)
  3. 没有 output_composition(不是 YAML books 多输出行组合)

不会自动切换,也没有默认的 run_stats 布局 hint:要生效必须由调用方显式设置 OutputWriteLayout.COLUMN_CHUNKED。迁移期仍可用 ExcelColumnResidency.CHUNKED,但它只在未设 layout 时参与推导。

调用示例(推荐):

from scalim.dsl.yaml_dsl import DemandRunOptions, DemandRunRuntimeOptions, OutputWriteLayout
# 也可: from scalim.execution import OutputWriteLayout

options = DemandRunOptions(
    runtime=DemandRunRuntimeOptions(
        output_write_layout=OutputWriteLayout.COLUMN_CHUNKED,
    ),
    # security=...
)

或纯 IR:

from scalim.execution import ExecutionRequest, OutputSpec, OutputWriteLayout

req = ExecutionRequest(
    export_layout=...,
    output=OutputSpec(format="excel", path="out.xlsx", streaming=False),
    output_write_layout=OutputWriteLayout.COLUMN_CHUNKED,
)

迁移窗(未设 layout 时仍推导为 column_chunked):

from scalim.execution import ExcelColumnResidency, ExecutionRequest, OutputSpec

req = ExecutionRequest(
    export_layout=...,
    output=OutputSpec(format="excel", path="out.xlsx", streaming=False),
    excel_column_residency=ExcelColumnResidency.CHUNKED,
)

pipeline 的列模式按 batch 调用 set_row_ids(本批) 再写满全部列,与 chunked sink 的多 batch 语义一致。

正确性:改 chunked 会不会写错?

维度 结论
合法 IR 列式路径 buffered↔chunked 业务格子等价(对拍用业务列,勿比 xlsx 字节)
YAML books / composition COLUMN_* / CHUNKED 同开 → fail-fast
IR 行式 streaming=True 显式 layout 会覆盖 streaming 生效;仅「未设 layout + CHUNKED」的推导路径 fail-fast
某窗列未写齐 chunked close 更严报错

何时不要用 column_chunked

场景 原因
YAML / workflow resources.books Excel 已是行 sink;设 COLUMN_CHUNKED + composition → fail-fast(禁止假开关)
streaming=True 行式 Excel 不适用:显式 COLUMN_CHUNKED 会忽略 streaming 直接生效;推导路径(未设 layout + CHUNKED)才 fail-fast
指望 YAML 里写 streaming / layout knobs 禁止;runtime policy 只在 Python
shared-book 物化峰值 另案(spill 等);不是本 Enum
一次 set_row_ids(全量行) 再按列写 仍会预分配整张表的行壳(行索引 + 空值占位),peak 收益有限;应 按 batch 追加 set_row_ids

手写 sink

需要完全自管写出时,可直接:

from scalim.sinks import StreamingColumnExcelSink

按 batch:set_row_ids(本批) → 写满全部列 → close()
传入 ExecutionRequest.sink=... 时,若同时设了输出路径,工厂文件 sink 仍会创建,与你的 sink 以 tee 方式并行写入;只想用自管 sink 就别设输出路径。

3. run_demand / workflow 能设什么?(有手动、无自动)

现状:有 Python 手动开关,没有按宽表/长表自动选 sink,也没有默认写出布局 hint。

入口 能否指定 实际效果
YAML resources.books / outputs 不能写 layout / residency / streaming knobs 组合层强制 行式ExcelSink / CSVSink + write_row_aligned
DemandRunOptions.runtime.output_write_layout (推荐) 显式 ROW_STREAM / COLUMN_BUFFERED / COLUMN_CHUNKED;与 output_composition 同开列布局 → fail-fast
DemandRunOptions.runtime.excel_column_residency (迁移窗) 仅当未设 layout 时参与推导;CHUNKEDexcel+streaming=False;与 composition 同开 → fail-fast
WorkflowRunOptions.demand 嵌套同一套 DemandRunOptions.runtime 同上;workflow 没有单独的 layout/residency 字段
纯 IR ExecutionRequest(...) 完整可控 显式 layout 优先;否则 streaming+residency 推导 → 对应 sink
手写 ExecutionRequest.sink=... 最高自主 绕过工厂;pipeline 按 IColumnSink / 行 sink 二选一

工厂选择见 src/scalim/execution/run_ir.py_create_file_sink。无脚本时的静态图:

flowchart TD
  entry[run_demand_or_workflow_or_IR]
  entry --> hasComp{output_composition_YAML_books?}
  hasComp -->|yes| rowForced[force_row_streaming]
  hasComp -->|no| layoutQ{OutputWriteLayout_or_derive}
  rowForced --> rowAligned[write_row_aligned]
  layoutQ -->|row_stream| rowFile[ExcelSink_or_CSVSink]
  layoutQ -->|column_buffered_csv| colCsv[ColumnCSVSink]
  layoutQ -->|column_buffered_excel| colBuf[ColumnExcelSink]
  layoutQ -->|column_chunked| colChunk[StreamingColumnExcelSink]
  rowFile --> rowAligned

未设 output_write_layout 时的推导:streaming=Truerow_streamexcel+CHUNKED residency → column_chunked;其余列式 → column_buffered(CSV 忽略 residency)。

数据形状 × 策略(人工选型,非自动)

形状 推荐 原因
日常 YAML 报表(中等行列) 默认行流式 已 aligned;零配置
大量行、列不多、落盘 CSV/xlsx 行流式 / ROW_STREAM 峰低;写出税多在 IO / openpyxl
大宽表 Excel、IR、峰不可接受 COLUMN_CHUNKED buffered→chunked 可大幅降峰(见上文证据)
宽表但要历史列缓存语义 COLUMN_BUFFERED 峰高;行为兼容
二次处理全表 List[dict] sink=None / capture 最吃内存;勿当主路径
YAML books + 想 chunked 不可 fail-fast;改 IR 列式或接受行式

常见误区

在 YAML books 路径上设置:

DemandRunOptions(
    security=...,
    runtime=DemandRunRuntimeOptions(
        output_write_layout=OutputWriteLayout.COLUMN_CHUNKED,
    ),
)

不会把 books 变成列式 chunked;与 output_composition 同开会 fail-fast(有意禁止假开关)。
chunked 只对「无 composition 的 IR 列式 Excel(streaming=False)」生效。

4. 与 YAML 的关系(重要)

  • YAML authoring 没有 output_write_layout / excel_column_residency / write.streaming 字段
  • books 路径保持行组合;宽表 YAML 峰值问题请先排查 shared-book / 执行上下文,而不是找本开关
  • Python ResourcesPolicy / BookWritePolicy 只管 book 容器语义,不要把列式 layout 挂到 books 上

5. 相关链接

  • 公共 API:scalim.execution.OutputWriteLayoutExcelColumnResidencyscalim.dsl.yaml_dsl 同名导出、scalim.sinks.StreamingColumnExcelSink
  • Spec:llmanspec/specs/runtime-output-write-layout/
  • Agent skill 指引:agentdev/skills/scalim-yaml-dsl/references/streaming-column-excel-guidance.md
  • Upgrade 卡:agentdev/skills/scalim-yaml-dsl/references/upgrades/2026-08-11-output-write-layout.md
  • 并行调参:parallel-modes.md
  • Perf 判断链路:llmanspec/notplan/2026-08-11-perf-roi-judgment-chain.md
  • Advisory 研究(搁置实现):llmanspec/notplan/c40-output-write-layout-advisory/
  • 历史证据:原 2026-07-12-c0-* 变更目录已冻结,见 llmanspec/changes/archive/freezed_changes.7z.archived(含 c0-streaming-column-excel-sinkc0-streaming-column-excel-multi-batchc0-column-excel-sink-column-residency