用途
该技能用于优化 F# 项目基于图的类型检查关键路径,目标是在有编译器测量证据的前提下,让编译器保守的文件依赖图更小、更浅。适用于 F# 构建在类型检查阶段花费大量时间的情况。技能明确要求:不能把更简单的依赖图当作构建更快的证据,必须分别证明测量、依赖结构和兼容性。
主要能力
技能提供了一套分步骤的优化流程:
-
固定测量契约:记录项目、SDK、编译器版本、配置、目标框架和构建命令,记录机器条件和构建状态,对编译器阶段和每个文件活动计时,同时记录完整构建时间。定义运行次数、预热策略和最小有用改进。使用重复运行并比较中位数,保留原始报告,保持工作负载、标志和清理过程一致。
-
捕获当前依赖图:使用诊断标志(依赖 F# 编译器版本,需先用所选 SDK 检查)收集基线。通过扩展而非替换 OtherFlags 来启用图检查、图转储和时间报告。保留生成的 .graph.md 文件和时间 CSV,识别最长依赖链、高扇出文件、关键链上昂贵的 CheckOneImplFile 活动以及被不必要串行化的独立文件。
-
分类保守边:检查导致编译器推断依赖的未类型化源构造,包括共享命名空间中直接暴露的类型、全局命名空间、非前缀 [<AutoOpen>] 模块、大型模块或文件、显式 open、限定名称、继承、扩展和推断类型。将依赖图视为保守超集,编译器可能推断出程序并不需要的语法边。
-
选择最小干扰的切割:针对测量到的原因匹配实验,例如对不必要的共享命名空间边隔离内部声明、对昂贵的高扇出实现测试 .fsi 文件、对全局或自动打开声明移除虚假依赖、对大型真实边界测试文件/模块/项目拆分。在组合切割前先分别测量每个切割。
-
保留现有公共契约:检查当前源导入、CLR 名称、扩展查找、反射使用和已编译消费者,用最小的仓库特定机制保留所需行为。规则包括:优化不要求公共移动时保留现有公共路径;除非现有消费者契约要求,否则不添加外观或自动打开文件;仅在合理命名空间或模块变更后必须保留隐式源查找时使用程序集级 AutoOpen;程序集级 AutoOpen 是一种条件兼容策略,不恢复 CLR 全名或二进制兼容性;仅包含 open 语句的模块不会重新导出打开的声明;命名空间移动可能改变 private 的含义或合法性,改为 internal 会扩大可访问性并需要 API 审查;命名空间移动可能破坏类型别名、扩展目标、反射名称、C# 调用方和已编译消费者。
-
证明行为与兼容性:验证所有适用层,包括构建确切项目和完整解决方案、运行仓库测试、编译最小源消费者(仅使用文档化的公共导入和计算表达式值)、比较前后公共 API/程序集元数据、在需要二进制兼容性时运行已编译消费者、检查每个目标框架和相关条件编译分支。不能仅从仓库测试推断兼容性。
-
重新测量并决策:重复基线过程,比较中位编译器与完整构建时长、每文件类型检查时长、关键路径深度和扇出、方差、源/二进制/行为兼容性结果。仅当测量收益证明其 API 和维护成本合理时才接受重构。准确报告小或嘈杂的收益。结构实验即使回退也能提供有用证据。
使用方式
技能以步骤化流程使用,需要配合 F# 编译器诊断标志。示例标志配置为:
<OtherFlags>$(OtherFlags) --test:GraphBasedChecking --test:DumpCheckingGraph --times:report.csv</OtherFlags>
这些诊断标志依赖 F# 编译器版本,需先用所选 SDK 检查。构建后保留生成的 .graph.md 文件和时间 CSV。
适用场景
适用于 F# 构建在类型检查阶段花费大量时间,且需要基于测量证据优化依赖图的情况。
限制
诊断标志依赖 F# 编译器版本,需先检查。不能仅凭更简单的依赖图认定构建更快。不能仅从仓库测试推断兼容性。程序集级 AutoOpen 不恢复 CLR 全名或二进制兼容性。命名空间移动可能破坏类型别名、扩展目标、反射名称、C# 调用方和已编译消费者。改为 internal 会扩大可访问性并需要 API 审查。
报告要求
返回包含基线、候选、变更、运行次数和方差的证据表,链接两个依赖图和原始时间报告,分别说明移除的虚假边、剩余关键路径和已证明的兼容性级别,给出保留、修改或回退的决定。