实战:解决C++ AI引擎代码仓库难以维护的问题

0 背景

我们团队目前主要负责研发 AI 引擎(主要用 C++ 实现),简单说来就是将算法 SDK 及模型进行封装,对外提供统一的接口方便后续的应用开发。相信有这类工作的经历的人都会明显感觉到有如下特点:

  1. 对外接口基本不变
  2. 算法 SDK 版本多:CPU/GPU,X86/ARM…
  3. 算法模型更多:1.0,2.0,3.0…还不算麻烦,关键是还有各种组合模型比如 1.0+2.0,1.0+2.0+3.0

本来再复杂的事情只要做的次数少其实都还能接受。所以起初,我们将应用(Application)、算法SDK(SDK)、算法模型(Models)都放在一个仓库中进行管理,需要版本时就整体打包,也安安稳稳的过了些日子。

但是随着业务发展,各个项目需要的引擎版本差异就越来越大:可能A项目需要我们提供 CPU 版本 1.0 模型的安装包,同时B项目又需要我们提供 GPU 版本的 2.0+3.0模型的安装包,同时C项目需要我们定位许久以前 ARM 版本上的一个BUG…于是各种幺蛾子出现:

  • 仓库大小分分钟超过10G
  • 用户常常下错安装包
  • 模型中某个配置项的默认值设置出错
  • 打包时忘了改某个配置参数
  • 只能在某台服务器上可以编译打包,换个环境配置半天都不一定搞得定

我们突然发现,如果不解决工程性问题的话,加人不一定有效,甚至可能会越来越乱。我们需要重新梳理整个工程结构,所以就有了后文的内容。

1 目标

首先,我们要明确痛点和需求,才能更好的进行改进。根据实际需求和现存问题,我们认为新的工程结构应该具备如下特点:

  • 需求 1:尽可能的减少打包操作,让开发有更多时间在开发
  • 需求 2:方便多人协同工作
  • 需求 3:支持本地编译的基础上,支持代码提交后自动化编译出包

2 核心方案:

为了满足上述需求,我们最终采用的是下述两个思路来实现

  • 标准化:用来搞定需求 1 和需求 2
  • 自动化:用来搞定需求 3

2.1 标准化

标准化具体来说有以下两点:

改进方向:通过合理划分工作以及对应代码仓库来实现

在这里插入图片描述

  • 原始方案:
    • 代码仓库:Application 仓库(内含指定版本的 SDK 及 Models)
    • 发布仓库:Application 仓库(内含指定版本的 SDK 及 Models)
    • 获取方式:找到 Application 版本后直接下载
    • 维护方式:
      • (低频)修改业务逻辑(如:新增 rpc 接口):重新构建整个工程并发布
      • (中频)切换SDK版本(如:将x86版本换为嵌入式arm版本):重新构建整个工程并发布
      • (高频)切换模型版本(如:将单模型更新为混合(多)模型):重新构建整个工程并发布,或者在已经打包完成的二进制包中手动更新模型文件及配置文件
  • 改进方案:
    • 代码仓库:Application 仓库、SDK 仓库、Models 仓库
    • 发布仓库:Application 仓库(内含指定版本的 SDK)、Models 仓库
    • 获取方式:找到 Application 版本并选定需要的模型,服务器后台经过文件拼接后生成下载链接以供下载
    • 维护方式:
      • (低频)修改业务逻辑(如:新增 rpc 接口):重新打包 Application 仓库并发布
      • (中频)切换SDK版本(如:将x86版本换为嵌入式arm版本):重新打包 Application 仓库并发布
      • (高频)切换模型版本(如:将单模型更新为混合(多)模型):无维护工作

依赖工具:QT(qmake) + Conan

  • QT(qmake):C++ 工程管理工具,方便按需生成 makefile
  • Conan:去中心化包管理工具,主要作用是用“包”的形式管理 C++ 或其他二进制文件的依赖关系
  • Artifactory:Conan 官方提供的支持私有化部署的仓库,用于持久化存储 Conan 中的“包”以及编译出来的最终二进制文件

注:这里选用 QT 进行 C++ 代码管理,主要是因为延续项目历史配置的原因。其实选择任何 Conan 支持的高级工程管理工具都是可行的,例如 CMake

收益

  1. 仓库拆分后,Application 仓库中的内容更清晰简洁,仓库大小从 Gb 级别降至 Mb 级别,方便管理
  2. 通过仓库的拆分,将打包过程分为两个阶段并使得 Application (内含指定版本的 SDK)与 Models 解耦,支持按需生成用户实际需要的安装包,降低打包工作量
  3. C++ 中的依赖库被 Conan 整体接管,编译选项被 QT + Conan 整体接管,多人协助基础技术障碍基本扫清
  4. SDK 内部配置、Models内部配置统一使用仓库配置,降低因误操作导致默认配置被改的概率

2.2 自动化

由于代码托管在 Gitlab 上,所以整体方案比较直接:Gitlab-ci + Kubernetes + Docker:

  • gitlab-ci:Gitlab 内置 CI/CD 工具,在Gitlab 体系中使用非常方便
  • Kubernetes + Docker:将编译环境制作为容器并支持在私有云动态部署,这里主要作为 gitlab-ci 的 runner 使用

收益

  1. 通过自动化打包,实现统一的出包策略,降低打包过程中的低级错误发生的可能性
  2. 无缝对接后续的自动化测试等流程

3. (附录)实现过程中的主要坑点:

3.1 团队协作

  • 在团队中设置专人负责除 Application 以外的所有基础库的维护工作;

3.2 Conan + Artifactory

  • 安装完 Conan 后,可以通过修改 conan.conf 中的 path 字段来改变“包”的实际存储位置;
  • 不允许覆盖、删除所有上传至 Artifactory 的 stable 包;
  • Conan 中 Settings 系列字段(OS、Architecture、Build Type、Compiler等)无法支持自定义值,因此如果有嵌入式编译需求时,建议在所有项目中增加默认的 Option 字段(类似 shared)用于标识目标机的具体定义(如:Redhat6.4,3559A等);
  • 建议在私有 Artifactory 中维护项目所需的所有基础包,并在使用时移除 conan-center 公共远程仓库,控制外部环境(主要是网络)对编译过程的影响;
  • conanfile.py 完全覆盖 conanfile.txt 的功能,建议所有 Conan 工程仅维护 conanfile.py 即可;
  • 使用在 Conan 的 Option 扩展“包”的可配置性时要慎之又慎,如无必要勿增实体,要知道每多一个 Option 后续打包的时候都要多一个麻烦;
  • conanfile.py 的 configure()、config_options()、requirements() 中尽可能的将 requirements 与 options 配置明确,降低实际使用时的复杂程度;
  • 使用 Conan 提供的 cpt.packager 工具来管理同时编译多个版本的任务;

3.3 gitlab-ci

  • 打包时版本号的后缀部分根据构建时间自动生成,如:v1.0.0-20181212080808;
  • 打包时将 commit id 的 sha 值随二进制包一同输出;
  • feature分支仅提供artifacts下载试用,仅主干分支支持上传至 Artifactory 仓库;
  • 若有多个版本,建议在使用 gitlab-ci 实现时以并行任务的方式同时进行构建,以避免频繁修改自动化配置;(.gitlab-ci.yaml文件)

3.4 Kubernetes + Docker

  • 自己制作拿来即用的 Docker 镜像,加快构建速度并控制风险;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值