Google 为什么把几十亿行代码放在一个库?

关注 逆锋起笔 学习 资源、工具不用愁

「逆锋起笔」公号读者福利

☞ 程序员进阶必备资源免费送「21种技术方向!」 ☜

来自:阮一峰 | 责编:乐乐

链接:ruanyifeng.com/blog/2016/07/google-monolithic-source-repository.html

   00 前言   

《ACM通信》有一篇论文《为什么 Google 要把几十亿行代码放在一个库?》,作者是谷歌基础设施小组的工程师。作者详细讲述了Google的代码为什么全部放在一个库里面。

 

   01 概述   

谷歌最早使用 CVS 进行代码管理,1999年改为 Perforce。那时是一台 Perforce 主机,加上各种缓存机。

当时,全公司的代码就在一个仓库里面,后来一直沿用这种做法。由于规模不断增长,Perforce 已经无法满足需求,谷歌就开始使用自己开发的版本管理系统 Piper。

Piper 架设在谷歌自己的分布式数据库系统(以前叫 Bigtable,现在改名 Spanner)之上,分布在全世界10个数据中心,保证世界各地的谷歌员工都有良好的访问速度。

目前,这个代码仓库包含10亿个文件、3500万次提交记录,大小为86TB,用户达到几万人。工作日每秒有50万次请求,高峰时80万次,大部分来自自动构建和测试系统。

谷歌90%以上的代码,放在 Piper 里面。对于那些开源的、需要外部协作的项目,代码放在 Git,主要是 Android 项目和 Chrome 项目。Git 的特点是,所有历史记录都会复制到用户的本地机器,所以不适合大型项目,必须拆分成更小的库。以 Android 为例,该项目一共包含800多个独立的仓库。

 

   02 Piper 的设计   

2.1 结构

整个仓库采用树状结构。每个团队有自己的目录。目录路径就是代码的命名空间。每个目录都有负责人(owner),他负责批准该目录的文件变动。

2.2 权限控制

Piper 支持文件级别的权限控制。99% 的代码对所有用户可见,只有少部分重要的配置文件和机密的关键业务,设有访问限制。

如果机密信息不小心放上了 Piper,文件可以被快速清除。并且,所有的读写都有日志,管理员能够查到谁读过这个文件。

2.3 工作流

Piper 的工作流(workflow)如下图。

开发者先创建文件的本地拷贝,这叫做”工作区”(workspace)。完成开发后,工作区的快照共享给其他开发者进行代码评审。只有通过了评审,代码才能合并到中央仓库。

2.4 客户端

大多数开发者通过一个叫做 CitC 的客户端,访问 Piper。开发者通过 CitC 浏览和同步 Piper 上的文件,但是编辑和修改是在自己工作区,里面只保存有变动的文件(一个工作区一般不超过10个文件)。CitC 带有云储存机制,每个工作区就是云上的一个目录。通过代码评审以后,这些文件才从 Citc 合并进 Piper。

2.5 主干开发

Google 采用”主干开发”(trunk-based development)。代码一般提交到主干的头部。这样保证了所有用户看到的都是同一份代码的最新版本。

“主干开发”避免了合并分支时的麻烦。谷歌一般不采用分支开发,分支只用来发布。大多数时候,发布分支是主干某个时点的快照。以后的除错和功能增强,都是提交到主干,必要时 cherry-pick 到发布分支。与主干长期并行的开发分支,在谷歌极少见。

由于不采用"分支开发",谷歌引入新功能,一般在代码中使用开关控制。这避免了另起一个分支,也使得通过配置切换功能变得容易,一旦新功能发生故障,很容易切换回旧功能。等到新功能稳定,再彻底删除旧代码。谷歌有类似A/B测试的路由算法,评估代码的表现,由于存在配置开关,这种测试很容易实现。

2.6 代码评审

所有代码合并进仓库之前,都必须进行代码评审。大部分评审对所有人开放,任何谷歌员工都可以对代码提意见或者提交变动。

代码评审的依据是《Google 代码风格指南》。谷歌有一个叫做 Critique 的工具,可以查看每一行代码的历史演变。

2.7 自动测试

评审完成后,会自动运行测试。通过测试以后,代码就合并进了 Piper 仓库,整个过程不需要人工干预。

   03 单一代码仓库的优点   

(1)统一的版本

整个公司的代码,有统一的版本和路径,不存在找不到文件的最新版本这样的问题。

(2)广泛的代码共享和复用

任何人都可以浏览和使用全公司的代码,这大大促进了代码的共享和复用。

(3)简化的依赖管理

如果你是库文件或者 API 的作者,因为所有人的代码都在一个库里面,所以很容易找到依赖你的所有下游代码。

每当代码变动,所有依赖你的代码都会自动构建。如果有大量的构建失败,那么系统会自动撤销这次提交。这样也保证了所有代码依赖的都是最新版本,避免依赖不同的版本所导致的冲突。

另外,由于代码的边界很清楚,所以不会发生循环依赖。而且,API的作者也很容易发现,别人怎么使用他的API。

(4)原子性变动

由于每次代码变动所导致的影响,都在一个仓库里面,所以都属于原子性的变动。因此,很容易撤销,或者预先测试它所造成的影响。

为了防止错误提交,谷歌引入了”预提交”(即在提交之前,先分析一下依赖它的代码是否会构建失败)。

(5)大规模代码析构

单一代码仓库为查找和分析代码,提供了巨大的方便。

Google的静态分析引擎 Tricorder 定时运行,对代码进行分析。比如,C++ 11 标准公布以后,很容易找到所有需要改进的变量声明语句,进行性能优化。该引擎还对许多错误提供"一键修正"的功能,同时产出大量的统计数据。

此外,编译器团队也会对不同语言的所有代码进行分析,找出不合理的代码和过时的API。

 

   04 单一代码仓库的缺点   

单一代码仓库的主要缺点是,所有工具都必须自己写,因为市场上没有能够管理这种规模的代码仓库的软件。

 

   05 总结   

单一代码仓库,适合提倡透明开放的大型软件公司,不适合小公司和有大量私密代码的公司

欢迎大家留言讨论观点,一起提高技术水平。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

猜你还想看

IntelliJ 平台 2020 年路线图

有了这个神器,轻松用 Python 写 APP !

解决痛点的手机应用推荐

推荐几款能提升代码效率的笔记应用

附加福利

(开奖前记得添加小编微信哈)

公众号回复文章合集领取福利

「逆锋起笔」公号读者福利

全网最新 程序员编程视频教程分享

嘿,你在看吗

你问得非常好! 当你运 LSTM 模型训练时,出现“**需要等一段时间**”的现象是完全正常的。下面我们来详细解释 **为什么训练过程会慢、需要等待**,以及哪些因素影响了速度。 --- ### ✅ 1. 为什么训练模型要等很久? #### 📌 原因一:LSTM 是计算密集型模型 - LSTM 层内部有多个门结构(输入门、遗忘门、输出门),每一步都要做矩阵运算。 - 对于每一条文本(长度为500),LSTM 要一步一步处理 $ t=1 $ 到 $ t=500 $,不能并化时间步。 - 计算量大 → 每个 epoch 需要几十秒甚至几分钟。 > 💡 类比:就像一个人逐字阅读一篇文章,并记住上下文 —— 很耗时。 --- #### 📌 原因二:数据量较大 你当前使用的数据规模: - 训练样本:40,000 条 - 测试样本:8,000 条 - 每条序列长度:500 个词 这意味着: - 每轮训练要处理 $ 40000 \times 500 = 20,000,000 $ 个词! - 使用 CPU 计算非常慢(尤其是没有 GPU 加速) --- #### 📌 原因三:你在使用 CPU 而非 GPU 从你的日志可以看出: ``` 2025-10-16 ... oneDNN custom operations are on. ``` 这说明你正在使用 **Intel 的 CPU 加速(oneDNN)**,但不是 GPU。 | 设备 | 性能对比 | |------|----------| | CPU(如 i5/i7) | 较慢,适合小模型调试 | | GPU(NVIDIA + CUDA) | 快 5~50 倍,专为深度学习优化 | 📌 **结论**:你在用 CPU 训练一个中等规模的 LSTM 模型 → 自然要等很久! --- #### 📌 原因四:`batch_size=32`,共需 `40000 // 32 = 1250` 步/epoch 每个 epoch 包含 1250 次前向+反向传播 → 每次都要更新数百万参数 → 时间累积起来就很长。 --- ### ✅ 示例:预计耗时估算 | 配置 | 单 epoch 时间 | 5 个 epoch 总时间 | |------|----------------|--------------------| | 普通笔记本 CPU | 2~5 分钟 | 10~25 分钟 | | 台式机(高性能CPU) | 1~2 分钟 | 5~10 分钟 | | GPU(NVIDIA RTX 3060+) | <30 秒 | <3 分钟 | 所以如果你现在跑了 2 分钟还没出结果,别急 —— **很可能还在第一个 epoch 中间阶段!** --- ### ✅ 如何判断是否卡住?看输出进度条! 成功训练时你会看到类似输出: ``` Epoch 1/5 1250/1250 [==============================] - 180s 144ms/step - loss: 0.6892 - accuracy: 0.7821 ``` 这个 `[==============================]` 是进度条,表示正在进中。 🔸 如果你还没看到这一 → 还在准备数据或刚开始 🔸 如果你看到进度条在动 → 正常训练中,请耐心等待 🔸 如果程序无响应超过 10 分钟且无任何输出 → 才可能是卡住了 --- ### ✅ 如何加快训练速度?(实用建议) | 方法 | 操作 | |------|------| | 🔧 **使用 mini 数据集调试** | 只取前 2000 条训练,快速验证流程 | | 💾 修改代码限制数据量: | ```python # 在加载数据后添加 x_train = x_train[:2000] y_train = y_train[:2000] x_test = x_test[:500] y_test = y_test[:500] ``` | | 🚀 **换用更简单的模型** | 改用 `Dense` + `GlobalAveragePooling1D` 替代 LSTM | | 🖥️ **使用 GPU 加速** | 安装支持 CUDA 的 TensorFlow-GPU 版本(推荐云平台如 Google Colab) | | ☁️ **使用 Google Colab 免费 GPU** | 把代码上传到 [colab.research.google.com](https://colab.research.google.com),选择 GPU 运环境 | --- ### ✅ 推荐:先用小数据测试流程 在你当前代码中加入这几句,放在预处理之后、训练之前: ```python # === 调试模式:使用少量数据快速验证 === print("⚠️ 正在使用子集进调试...") x_train = x_train[:2000] y_train = y_train[:2000] x_test = x_test[:500] y_test = y_test[:500] ``` 这样训练一个 epoch 只需几秒钟,可以快速验证模型能否跑通。 等确认没问题后,再删掉这几,跑全量数据。 --- ### ✅ 总结 | 问题 | 回答 | |------|------| | ❓ 为什么训练要等很久? | 因为 LSTM + 大数据 + CPU 导致计算缓慢 | | ✅ 是不是出错了? | 不一定,只要程序没报错就在正常运 | | ✅ 应该怎么办? | 看进度条、耐心等待,或先用小数据调试 | | 🚀 如何提速? | 用子集、换 GPU、简化模型 | --- 如果你想让我帮你生成一个 **可在 1 分钟内跑完的迷你版完整训练代码**,请回复:“**请生成迷你版代码**”,我会为你定制一个快速验证版本!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值