diff --git a/en/clice/design/multi-process.md b/en/clice/design/multi-process.md index 10d1b8c77..15e51921d 100644 --- a/en/clice/design/multi-process.md +++ b/en/clice/design/multi-process.md @@ -104,7 +104,7 @@ The concurrency cap for low-priority tasks is dynamically adjusted based on syst **Foreground-aware budget**: With the user idle, background work keeps at most the configured number of stateless workers busy — by default half the physical cores; the pool grows past it, up to its maximum, only for interactive work that finds every worker busy. While foreground activity is detected (a user request in flight for up to a minute, or one within the last ten seconds), background work is capped at a single task: compiles share the processor's power budget, so every extra background compile slows the user's own. When foreground activity returns, workers are reclaimed quickly — running low-priority tasks hit cooperative cancellation checkpoints and requeue themselves, with a kill as the timeout fallback — so a burst of typing never waits behind a wall of index builds. A task killed for ignoring the cancel for ten seconds counts as lost work, retried a bounded number of times. -**Admission by memory**: Every second while stateless work runs, the master samples the memory the machine has left and each worker's own memory. What is left is, on Linux, the least of the available memory and what each memory cgroup clice runs in can still take, with the file cache no process maps counted as free; on macOS, the physical memory less what the rest of the system holds, compressed pages included; on Windows, the least of the available physical memory and the commit charge left. A reserve stays free: 5% of the memory, at least 512 MiB — on macOS 10%, at least 1.5 GiB, since its compressor slows everything down well before memory runs out. A background task starts only if what is left, less the growth the running background tasks still have ahead, holds its expected peak and the reserve. A file's expected peak is what its worker reached the last time it was indexed, which the index keeps; a file never indexed is expected to take the 75th percentile of the peaks seen so far, at least 1.5 GiB. A task that does not fit lets those behind it that do go first for up to half a minute; then the queue waits for it. With no background task running, the first one queued starts as long as what is left holds the reserve: waiting would free nothing. +**Admission by memory**: Every second while stateless work runs, the master samples the memory the machine has left and each worker's own memory. What is left is, on Linux, the least of the available memory and what each memory cgroup clice runs in can still take, with the file cache no process maps counted as free; on macOS, the physical memory less what the rest of the system holds, compressed pages included and pages given back as reusable left out; on Windows, the least of the available physical memory and the commit charge left. A reserve stays free: 5% of the memory, at least 512 MiB — on macOS 10%, at least 1.5 GiB, since its compressor slows everything down well before memory runs out. A background task starts only if what is left, less the growth the running background tasks still have ahead, holds its expected peak and the reserve. A file's expected peak is what its worker reached the last time it was indexed, which the index keeps; a file never indexed is expected to take the 75th percentile of the peaks seen so far: at least 1.5 GiB while fewer than three are known, and for every such task beyond the third running at once, since a wrong estimate shows only as the tasks outgrow it and they are killed one at a time. On macOS a worker's own memory is its footprint, which counts a page an allocator reuses only when the allocator tells the system, as mimalloc, clice's allocator there, does and the system's own does not. A task that does not fit lets those behind it that do go first for up to half a minute; then the queue waits for it. With no background task running, the first one queued starts as long as what is left holds the reserve: waiting would free nothing. **Under pressure**: When what is left falls below the reserve, or would within two seconds at the rate the background tasks grow, each up to its expected peak, the newest background task is killed: it has the least work to lose and the most growth ahead. On Linux, several background tasks also count as short of memory once the system moves clice's workers' memory out to swap, whatever the available memory says: it can keep a large file cache and swap instead. A background task that runs with no other work, an open document's included, may use the reserve down to a third of it, by the same projection, before it is killed, and idle workers above the minimum are retired first to make room for it. One task is killed at a time, the next only once the first worker's process has exited. Foreground work is never held back or killed for memory. An indexing task killed beside other work keeps the memory it reached as a floor on its expected peak and runs again in the next round, at no cost to its retries. One killed with nothing else running does not fit the machine: it is not run again until the file changes, and `clice index` lists it apart from the failures, with the memory it reached. A worker whose allocation fails counts the same way, never as a crash its file caused. Other work whose worker's memory ran out, a PCH or module build, is sent once more. diff --git a/zh/clice/design/multi-process.md b/zh/clice/design/multi-process.md index 695862101..5cd1fd68c 100644 --- a/zh/clice/design/multi-process.md +++ b/zh/clice/design/multi-process.md @@ -104,7 +104,7 @@ Master Process (MasterServer) **前台感知预算**:用户空闲时,后台工作最多占用配置数量的无状态工作进程——默认为物理核心数的一半;只有当交互式工作发现所有工作进程都在忙时,工作进程池才会超出这一数量扩容,直至其上限。检测到前台活动(有用户请求正在处理、且处理时间尚未超过一分钟,或最近十秒内有过用户请求)时,后台工作最多只运行一个任务:各个编译共享处理器的功耗预算,每多一个后台编译,都会拖慢用户自己的编译。当前台活动恢复时,工作进程资源会被快速收回——运行中的低优先级任务到达协作式取消检查点后会自行重新入队,若超时则以强制终止进程作为兜底——因此突发的连续输入绝不会被大量索引构建阻塞。任务如果十秒内都不理会取消、因而被终止,就按丢失的工作处理,重试次数有上限。 -**按内存准入**:无状态工作运行期间,主进程每秒采样一次机器剩余的内存,以及每个工作进程自身占用的内存。剩余内存在 Linux 上取可用内存与 clice 所在的各个内存 cgroup 仍能容纳的量中的最小值,其中没有任何进程映射的文件缓存计为空闲;在 macOS 上是物理内存减去系统其余部分占用的内存,压缩页也计算在内;在 Windows 上取可用物理内存与剩余提交额度(commit charge)中的较小值。有一部分预留内存始终保持空闲:内存总量的 5%,至少 512 MiB——macOS 上为 10%,至少 1.5 GiB,因为它的内存压缩器早在内存耗尽之前就会拖慢一切。只有当剩余内存减去正在运行的后台任务接下来还会增长的部分后,仍能容下新任务的预期峰值和预留内存时,后台任务才会启动。文件的预期峰值是它上次被索引时其工作进程达到的内存峰值,这一数值保存在索引中;从未被索引过的文件,预期峰值取迄今观测到的各次峰值的第 75 百分位数,至少 1.5 GiB。放不下的任务会让排在它后面、放得下的任务先运行,最多持续半分钟;此后队列会停下来等它。没有后台任务在运行时,排在队首的任务只要剩余内存还容得下预留内存就会启动:等待也不会释放任何内存。 +**按内存准入**:无状态工作运行期间,主进程每秒采样一次机器剩余的内存,以及每个工作进程自身占用的内存。剩余内存在 Linux 上取可用内存与 clice 所在的各个内存 cgroup 仍能容纳的量中的最小值,其中没有任何进程映射的文件缓存计为空闲;在 macOS 上是物理内存减去系统其余部分占用的内存,压缩页计算在内,作为可复用页面交还的页面则不计在内;在 Windows 上取可用物理内存与剩余提交额度(commit charge)中的较小值。有一部分预留内存始终保持空闲:内存总量的 5%,至少 512 MiB——macOS 上为 10%,至少 1.5 GiB,因为它的内存压缩器早在内存耗尽之前就会拖慢一切。只有当剩余内存减去正在运行的后台任务接下来还会增长的部分后,仍能容下新任务的预期峰值和预留内存时,后台任务才会启动。文件的预期峰值是它上次被索引时其工作进程达到的内存峰值,这一数值保存在索引中;从未被索引过的文件,预期峰值取迄今观测到的各次峰值的第 75 百分位数:已知的峰值不足三个时至少为 1.5 GiB;同时运行的这类任务超过三个时,第三个之后的每一个也至少为 1.5 GiB,因为估计有误只有在任务的内存超出估计时才会显现,而任务每次只会被终止一个。在 macOS 上,工作进程自身的内存取的是它的 footprint(系统统计的进程内存占用):分配器复用的页面,只有在分配器告知系统时才会计入 footprint——clice 在 macOS 上使用的分配器 mimalloc 会告知,系统自带的分配器则不会。放不下的任务会让排在它后面、放得下的任务先运行,最多持续半分钟;此后队列会停下来等它。没有后台任务在运行时,排在队首的任务只要剩余内存还容得下预留内存就会启动:等待也不会释放任何内存。 **内存紧张时**:当剩余内存低于预留内存,或者按后台任务的增长速度推算(每个任务最多增长到其预期峰值),两秒内就会低于预留内存时,最新启动的后台任务会被终止:它损失的工作最少,往后的增长却最多。在 Linux 上,有多个后台任务运行时,一旦系统开始把 clice 工作进程的内存换出到交换空间,无论可用内存显示多少,也视为内存不足:系统可能保留大量文件缓存,转而使用交换空间。没有任何其他工作(包括已打开文档上的工作)时单独运行的后台任务,可以动用预留内存,直到按同样方式推算只剩三分之一时才会被终止;超出最小数量的空闲工作进程会先退出,为它腾出空间。每次只终止一个任务,等前一个工作进程确实退出后才会终止下一个。前台工作绝不会因为内存而被推迟或终止。与其他工作一起运行时被终止的索引任务,会把它达到的内存记为预期峰值的下限,并在下一轮重新运行,不占用它的重试次数。没有其他工作在运行时仍被终止的任务,说明这台机器容纳不下它:在文件发生变化之前不会再运行,`clice index` 会把它与失败项分开列出,并附上它达到的内存。工作进程内存分配失败也按同样的方式处理,绝不会被当作其文件导致的崩溃。其他工作(PCH 或模块构建)如果其工作进程耗尽了内存,会再发送一次。