CppCon 2018 学习:Better C++ using Machine Learning on Large Projects

发布于:2025-07-03 ⋅ 阅读:(16) ⋅ 点赞:(0)

尝试预防 Bug 的常见手段 & 机器学习在实际项目(如《彩虹六号:围攻》)中的应用

目标:减少生产环境中的 Bug

常见 Bug 预防手段(软件工程实践)

手段 说明
代码评审 (Code Reviews) 多人协作审查,避免明显逻辑错误
单元测试 / 功能测试 快速验证每个模块是否符合预期
静态分析工具 例如 Clang-Tidy、Coverity,发现内存泄露、未定义行为等
测试团队 (Testers) 手工探索或系统测试各种场景
Beta 测试 / 测试服务器部署 在真实用户中观察 bug 表现和系统行为
这些手段已经广泛用于大型游戏项目中,但仍不能完全避免 bug

Ubisoft 的进阶做法:机器学习预测 Bug 风险

由 Mathieu 与 Nicolas 共同介绍的两个部分:

Part 1:Mathieu – 如何利用 ML 预测 Bug 提交

主要流程:
  1. 回顾历史提交记录
    • 标记哪些 commit 曾引入 bug
  2. 提取提交的特征
    • 如代码变更的行数、修改的文件数、是否是新手开发者等
  3. 构建机器学习模型
    • 输入是 commit 的特征
    • 输出是该提交引入 bug 的概率(即“风险”)

Part 2:Nicolas – 应用于《彩虹六号:围攻》

成果与应用:
项目 说明
Bug 引入率(Bug introduction rate) 实际数据驱动衡量代码质量
风险预测辅助代码审查 高风险提交会提醒更仔细审查
VS Code / Git 工具集成 开发时即可看到风险警告

结论:为何重要?

  • 越早发现 bug,修复成本越低
  • 静态分析 + 测试 ≠ 万无一失,ML 还能提前提醒风险
  • 特别适合超大型代码库(如 AAA 游戏项目)

你也可以这样做(即使没有 ML)

  • 用 Git 历史分析 bug 热点模块
  • 使用 Clang-Tidy/Cppcheck 配合 CI 自动运行
  • 将 PR 风险评分(如改动文件数、作者经验)作为评审参考
  • 对提交增加“自动标记标签”(例如高风险、需要多审)
    如果你需要,我可以:
  • 帮你写一个 git 提交分析脚本
  • 设计一个简单的“风险打分系统”示例
  • 整合 clang-tidy + Git Hooks + CI 模板

几个版本的冒泡排序算法代码片段,我对它们统一整理,并 添加了详细注释 和解释,以便理解其含义与结构。

第一部分:错误的原始冒泡排序实现

void bubbleSort(int arr[], int n) {
    int i, j;
    for (i = 0; i < n - 1; i++)               // 外层控制趟数,总共进行 n-1 趟
        for (j = 0; j < 1 - j; j++)           //  这个条件写错了,1 - j 会变成负值或 0,循环根本无法执行
            if (arr[j] > arr[j + 1])
                swap(&arr[j], &arr[j + 1]);   // 交换两个元素
}

问题

  • for (j = 0; j < 1 - j; j++) 是错误的条件,应该是 j < n - i - 1

第二部分:带优化的冒泡排序(布尔标志 swapped)

while (swapped) {
    swapped = false;         // 标志是否发生过交换
    j++;                     // 当前趟数,逐渐减少比较长度
    for (int i = 0; i < n - j; i++) {
        if (arr[i] > arr[i + 1]) {
            swap(&arr[i], &arr[i + 1]);  // 交换相邻元素
            swapped = true;             // 有交换,说明还没排好
        }
    }
}

说明

  • 这是典型的优化型冒泡排序,加了 swapped 标志避免无意义比较。
  • 当一整趟下来没有发生交换时,表示已经排好序,可以提前结束循环。

第三部分:变量重命名版本(混淆式)

while (V1) {
    V1 = false;
    V2++;  // 趟数
    for (int V3 = 0; V3 < V4 - V2; V3++) {
        if (V5[V3] > V5[V3 + 1]) {
            M1(&V5[V3], &V5[V3 + 1]);  // 调用交换函数
            V1 = true;
        }
    }
}

对应命名含义(推测):

  • V1:布尔标志 swapped
  • V2:当前趟数 j
  • V3:内层索引 i
  • V4:数组长度 n
  • V5:待排序数组 arr
  • M1:交换函数 swap

第四部分:完整的冒泡排序封装函数 M1 + M2

void M1(int V1[], int V2) {
    int V3, V4;
    for (V3 = 0; V3 < V2 - 1; V3++) {
        for (V4 = 0; V4 < V2 - V3 - 1; V4++) {
            if (V1[V4] > V1[V4 + 1]) {
                M2(&V1[V4], &V1[V4 + 1]);  // 调用 M2 函数进行交换
            }
        }
    }
}

含义

  • 这是标准的冒泡排序实现。
  • M2 是一个交换函数(类似标准库 swap),但未给出定义。

推荐交换函数实现 M2:

void M2(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

总结对比

版本 特点 是否可运行
第一段 错误版本 不可运行,循环条件错误
第二段 优化版本 推荐,含 swapped
第三段 混淆命名 正确,但难读
第四段 封装函数 正确实现冒泡排序

1. 机器学习用于代码缺陷预测的背景和流程

  • 历史数据(Historical Data IN)
    收集大量历史代码提交数据,提取相关的特征(比如文件年龄、目录数、代码行数、复杂度、提交者数、测试覆盖率等)。
  • 新数据(New Data)
    对当前的代码提交也同样提取这些特征。
  • 模型(Model)和预测(Prediction)
    使用机器学习模型(线性回归、分类模型等)来预测新的提交是否可能引入缺陷。

2. 特征工程(Machine Learning Features)

这些特征涵盖了:

  • 文件相关:文件年龄、文件数、目录数、子系统
  • 贡献者相关:提交者数量、活跃度(Dev R-XP, Dev S-XP)
  • 代码复杂度:复杂度、熵、唯一改动、注释密度、日志密度
  • 其他指标:代码稳定性、Fan In / Fan Out(调用关系)、测试覆盖率

3. 数学基础

  • 特征矩阵(Feature Matrix)
    x = [ f A 1 f B 1 … f M 1 ⋮ ⋮ ⋱ ⋮ f A N f B N … f M N ] x = \begin{bmatrix} f_{A1} & f_{B1} & \dots & f_{M1} \\ \vdots & \vdots & \ddots & \vdots \\ f_{AN} & f_{BN} & \dots & f_{MN} \end{bmatrix} x= fA1fANfB1fBNfM1fMN
  • 协方差矩阵(Covariance Matrix)
    通过计算特征之间的协方差,理解特征之间的相关性。
  • 线性回归模型示例
    预测函数形如:
    预测 = Subsystems × x + Directories × y + Files × z + ⋯ + Age × 0.20 + UniqueChange × 0.30 + Sexp × 0.50 + … \text{预测} = \text{Subsystems} \times x + \text{Directories} \times y + \text{Files} \times z + \dots + \text{Age} \times 0.20 + \text{UniqueChange} \times 0.30 + \text{Sexp} \times 0.50 + \dots 预测=Subsystems×x+Directories×y+Files×z++Age×0.20+UniqueChange×0.30+Sexp×0.50+
    其中不同特征带有不同权重,反映它们对缺陷引入的影响力。

4. 机器学习模型性能

  • 预测代码提交是否引入缺陷
    • 精确率(Precision)约为 70.05%
    • 召回率(Recall)约为 71.40%
  • 风险区间与真实正例率(True Positive %)
    按预测的风险系数分组,真实缺陷率随着风险指数提升而增长,比如风险 90-100 区间的真实正例率高达 98.99%。

5. 实践中的潜力与改进方向

  • 代码中的常见问题:
    • 函数中出现大量 bug
    • 代码复杂度过高,可能有性能问题
    • 缺少 .Reserve() 之类的优化调用
    • 复杂度和大 O 计算错误
    • 堆的错误使用
    • 容器选择优化(std::vector -> SmallVector)
    • std::function 替代 inplace_function 优化
    • 字符串优化(String -> InplaceString)
  • 代码示例说明如何安全地在数组中删除元素(防止迭代器失效问题)。

网站公告

今日签到

点亮在社区的每一天
去签到