Ninja: Ninja 消耗所有可用内存

创建于 2018-05-27  ·  20评论  ·  资料来源: ninja-build/ninja

我一直在调查在我的系统上进行交换的原因,并偶然发现了忍者代码中的这个片段: https ://github.com/ninja-build/ninja/blob/03df526e07b62c0c5dfe61720cf9263ae4fb808b/src/ninja.cc#L223 -L233

Android构建系统使用Ninja,由于我正在编译大量Android代码,它的性能强烈影响我系统的可用性。

我的工作 PC 有 4 核 CPU,最多 8 个线程,而家用 PC 有 8 核 CPU,最多 16 (!!) 线程。 两者都有 8Gb 的 RAM。

不用说,忍者编译会迅速囤积所有可用内存并导致大量交换。

现在 ninja 默认分配 CPU+2 线程,如果可用内存量与 CPU 数量不“匹配”,这很容易耗尽操作系统资源。 很少有其他程序具有这种默认设置,但其中大多数是游戏,它们经过优化以处理固定资产和节省内存。 Ninja 处理外部数据——软件源代码——其中一些非常占用内存(例如 C++)。 这绝对不行。 如果当前的 CPU 趋势继续下去,我们很快就会看到具有 64 个以上内核的面向消费者的计算机。 如果当前的 RAM 趋势继续下去,那么这些计算机中的大多数将不会有匹配数量的 RAM。

我已经看到一些关于通过动态监视内存使用来节省编译使用的内存的讨论。 我个人并不关心这一点——我的大多数项目都有可预测的编译足迹。

相反,我希望 ninja 执行一些基本的健全性检查并根据可用的系统内存限制它的最大并行度。 如果某些已安装的 CPU 每个没有至少 1Gb 的 RAM,则不要将这些 CPU 计入默认并行设置。 这将使大多数具有 <4 个 CPU 的系统以及企业 Xeon 构建服务器的并行作业数量大致相同,同时为 RAM 量低于标准的系统提供更合理的默认值。

最有用的评论

@jimon

我明白,你想帮忙,但这不是重点。

我的观点:

1) make具有安全的默认值,并允许您通过环境变量切换到更高的并行度。
2) ninja 有不安全的默认值。 我一直担心,构建一些程序会使我的系统挂起(我不想在构建之前检查每个随机 AUR 软件使用什么构建系统!)我不知道如何全局更改默认值忍者。

所有20条评论

ninja 不知道它运行的进程,而且我认为进程使用一个 cpu 核心的模型似乎最适合大多数建筑物。
但是命令之间的内存消耗有很大的不同(例如,一个简单的python脚本与大对象链接),并且很难对这些命令的内存使用有共同的假设。

如果您想控制内存消耗过程的并行性,最好在您了解构建的内存占用情况时指定较低的 -j 或使用池功能。
https://ninja-build.org/manual.html#ref_pool

我认为进程使用一个 cpu 核心的模型似乎最适合大多数建筑物。

我相信,还应该考虑 RAM 的总量。 切换到硬盘驱动器当然无助于实际性能。

如果要控制内存消耗进程的并行度,最好指定较低的 -j 或使用池功能

我不是 Android NDK 构建系统的开发人员,所以我不会将命令行参数传递给 ninja 命令,——Android 构建系统会。

我已经设置了环境变量 MAKEFLAGS="-j4" (大多数基于make的构建系统都遵守它),但 ninja 似乎没有使用它。

我推测您可以直接通过 gradle 中的 cmake 向 ninja 提供参数。 据我所知,gradle 中的 cmake 集成arguments字段。 也许这会起作用?

@jimon

我明白,你想帮忙,但这不是重点。

我的观点:

1) make具有安全的默认值,并允许您通过环境变量切换到更高的并行度。
2) ninja 有不安全的默认值。 我一直担心,构建一些程序会使我的系统挂起(我不想在构建之前检查每个随机 AUR 软件使用什么构建系统!)我不知道如何全局更改默认值忍者。

@Alexander-- 假设您有一个可执行文件(编译器或类似文件),它分配的 RAM 比您拥有的更多(通过交换文件或其他方式)。 您有一个仅执行该应用程序和 viola 的构建系统 - 您的系统没有响应。 通过这个心理例子,很明显这个问题不是绝对可以解决的。 现在让我们尝试想象这样一种情况,可执行文件会随着时间的推移逐渐分配内存,忍者对内存使用情况一无所知,只有当 RAM 实际消失后才会变得明显,此时无事可做。 这个问题不是很容易解决的,我认为它不应该在项目的范围内。

至于 make 有更安全的默认值 - 我无法想象有多少开发人员的时间被浪费了,因为开发人员要么不知道,要么无法在 make 中设置并行性。 我经常看到我的同事浪费了几分钟甚至几小时,因为他们不知道默认情况下 make 构建在一个线程中。

我不想反对解决您的问题,也不想听起来很苛刻:) 但我认为最理想的解决方案就是覆盖 gradle 行为并继续前进。 因为它非常特定于您的项目/计算机,并且可能不会以更大的规模出现。

您使用的是哪个 Android 构建系统? 使用 NDK 构建 Android 应用程序,还是构建整个平台 (ROM)?

忍者 + Gradle + 杰克 = 地狱。 (它至少占用 16GB RAM)
我不知道问题究竟出在哪里。 但我想主要问题是来自使用jack-server的java构建。 更重要的是 Ninja(来自 Soong)改变了所有遗留的东西并引入了这么多的小软件东西,因此我们迷失在构建系统中,找不到问题和解决方案。
我想念老Make。 请让我摆脱这个地狱般的建筑。
我应该回去解决这个“没有 Jack 服务器正在运行。尝试 'jack-admin start-server'”

换个说法@atetubou :如果您的 CPU 有 64 个内核,那么假设它能够并行执行 64 个程序是合理的。 这个假设对您来说是个问题的原因是您的编译任务似乎比普通程序大。 Ninja 有一种通过“池”功能传达此信息的机制。 如果您阅读该文档部分,您会发现它正是针对这个问题而设计的。 https://ninja-build.org/manual.html#ref_pool

我很同情你的问题,但我没有看到忍者解决它的简单方法。 你说它应该“考虑” RAM 的总量,但是我们可以使用什么样的公式呢?

@av930 jack 将在 P 中消失,并且可以在 O 版本中禁用。 我从 O 构建中删除了对 jack 的使用,我的 8 核和 12 gig 内存在 16+ 线程上运行没有问题。 您还可以更改 ninja 在构建期间使用的参数。 问题是杰克不是忍者。

此更改也可能对您有所帮助: https ://github.com/ninja-build/ninja/pull/1399

我的天啊! 谢谢,它有效。 最后我在 16GRAM 机器上完成了 AOSP 完整构建。
插孔禁用:ANDROID_COMPILE_WITH_JACK := 在 build/make/core/javac.mk 中为 false

这是日志。
[ 99% 101694/101695] 安装系统 fs 映像:out/target/product/taimen/system.img
out/target/product/taimen/system.img+ maxsize=2740531200 blocksize=135168 total=1076289888 Reserve=27709440
[100% 101695/101695] 目标 vbmeta 图像:out/target/product/taimen/vbmeta.img

构建成功完成 (04:43:31 (hh:mm:ss))

我认为这可能可以通过告诉操作系统对于一组n正在运行的进程可以在内存不足的情况下挂起最多n-1进程来解决。 有人知道这是否可以通过 Linux 上的 cgroups 来实现?

实际上有两个 PR 实现了内存限制:#1354 和 #660。 后者的讨论很有趣。

这听起来像是 Android 版本没有将千斤顶放入池中而使忍者错了。

@nico

您能否指出解决此问题的特定 Ninja 版本? 每当我从 AUR 构建某些东西时,我仍然看到 Ninja 默认使用过多的并行任务。 看起来 Ninja 仍在尝试在具有 16 个内核的机器上创建 18 个线程,而不管可用的 RAM 量如何。

Ninja 在这里不做任何事情,由生成器负责确保需要大量 RAM(或类似内容)的东西位于适当大小的池中。

由生成器来确保需要大量 RAM(或类似)的东西位于适当大小的池中

这是您的个人意见,还是忍者开发人员的官方立场? 文档从来没有提到过这样的责任,我也没有听说过实际上会干扰 Ninja 并行性的生成器。

无论哪种方式,生成器编写者都不会比您选择运行多少并行进程更好。 这最好由 buildserver 管理员决定。

您是说,没有(也不应该)降低 Ninja 资源使用率的通用方法,无论使用哪种生成器都有效。 我不同意。 有具有该功能的构建工具(例如 GNU Make),因此此类功能已经有先例。

文档从来没有提到过这样的责任,我也没有听说过实际上会干扰 Ninja 并行性的生成器。

没有明确关于 RAM,但是:

“构建的构建时自定义。选项属于生成忍者文件的程序。”

“Ninja 几乎没有任何功能;只有那些在生成 ninja 输入文件时最复杂的构建正确所需的功能。”

您是说,没有(也不应该)降低 Ninja 资源使用率的通用方法,无论使用哪种生成器都有效。

有两种方法: -j-l标志。

每当我从 AUR 构建某些东西时,我仍然看到 Ninja 默认使用过多的并行任务。

我不确切知道 AUR 是如何工作的,但是否会将以下脚本放入/usr/local/bin并通过chmod +x /usr/local/bin/ninja使其可执行?

#!/usr/bin/env python3

import sys
import subprocess

try:
    subprocess.check_call(['/usr/bin/ninja', '-j1'] + sys.argv[1:])
except subprocess.CalledProcessError as e:
    exit(e.returncode)

-j1可以像在第一个回复中一样被提及。
我有这个项目,随着时间的推移,它已经获得了很多内部的 external_projects。 也就是说,它们是静态构建的,而不是常见的 makefile 使用的动态构建,因此它们每个都有自己的标志和构建源。
今天,在这台较旧的笔记本电脑上,(我以前没有在这个系统上使用过忍者)它只是一个 4GB 的 windows 7 系统,核心 i7(8 个线程)。 当我开始构建时,整个系统在与磁盘交换时爬行了半个小时。
它导致了baizaare错误

C:/general/build/mingw64-x64/sack/RelWithDebInfo_out/core/include/SACK/stdhdrs.h:259:24: fatal error: C:/general/build/mingw64-x64/sack/RelWithDebInfo_out/core/include/SACK/loadsock.h: Invalid argument
compilation terminated.

为什么包含文件是无效参数? 文件存在....
所以整个构建完成了,但没有成功构建很多目标......
即使我现在使用

ninja -j1 -v install 

内部忍者进程仍然使用 -j8

构建最终是 -j8 在第一个启动了 8 个外部项目,每个项目都使用 -j8,所以 -j64 内存达到峰值,直到没有......

在这种情况下,Jobserver 支持可能会有所帮助:#1139

独立的 Ninja 构建语言重新实现https://github.com/michaelforney/samurai通过环境变量 SAMUFLAGS 支持这一点。 只需在您使用 ninja 的任何地方使用 samu,或将 samu 安装为您的系统 /usr/bin/ninja(一些 linux 发行版可以选择将此竞争实现作为默认安装或仅安装 ninja)。

我建议更新使用 ninja 构建的 AUR 包,改为使用 community/samurai。

此页面是否有帮助?
0 / 5 - 0 等级