实战:解决C++ AI引擎代码仓库难以维护的问题
0 背景我们团队目前主要负责研发 AI 引擎(主要用 C++ 实现),简单说来就是将算法 SDK 及模型进行封装,对外提供统一的接口方便后续的应用开发。相信有这类工作的经历的人都会明显感觉到有如下特点: 对外接口基本不变 算法 SDK 版本多:CPU/GPU,X86/ARM… 算法模型更多: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 收益: 仓库拆分后,Application 仓库中的内容更清晰简洁,仓库大小从 Gb 级别降至 Mb 级别,方便管理 通过仓库的拆分,将打包过程分为两个阶段并使得 Application (内含指定版本的 SDK)与 Models 解耦,支持按需生成用户实际需要的安装包,降低打包工作量 C++ 中的依赖库被 Conan 整体接管,编译选项被 QT + Conan 整体接管,多人协助基础技术障碍基本扫清 SDK 内部配置、Models内部配置统一使用仓库配置,降低因误操作导致默认配置被改的概率 2.2 自动化由于代码托管在 Gitlab 上,所以整体方案比较直接:Gitlab-ci + Kubernetes + Docker: gitlab-ci:Gitlab 内置 CI/CD 工具,在Gitlab 体系中使用非常方便 Kubernetes + Docker:将编译环境制作为容器并支持在私有云动态部署,这里主要作为 gitlab-ci 的 runner 使用 收益: 通过自动化打包,实现统一的出包策略,降低打包过程中的低级错误发生的可能性 无缝对接后续的自动化测试等流程 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 镜像,加快构建速度并控制风险; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- ajax或者jQuery的ajaxSubmit出现请求根本没有发出的问题
- Qt XML 使用QxmlStreamReader的使用的步骤和方法
- 八、TexturePacker命令行压缩图片
- ruby-on-rails – Capybara选择(“单选按钮”)无效
- PostgreSQL分页查询引发的思考:OutOfMemoryError
- xcode – 链接被抬起..什么是-rpath? MacOS X.
- ruby-on-rails – 如何限制Rails路由文件中的资源格式
- c – std :: span.size()vs数组/向量大小
- Oracle的RBO和CBO详细介绍和优化模式设置方法
- swift – 如何为alamofire Post请求中的一个参数传递一个ni