Skip to content

Commit 5e24e8f

Browse files
committed
image: cap xz memory with --memlimit-compress, lower thread cap to 16
Hand xz a hard memory ceiling via --memlimit-compress so it scales its own thread count down instead of risking OOM when several image builds compress in parallel on a shared runner. Default 10 GiB, lowered to MemAvailable on smaller hosts; override with IMAGE_XZ_MEMLIMIT. Lower the default thread cap 32 -> 16 (COMPRESS_MAX_THREADS) to match: at -9 (~674MB/thread) 16 threads ~= 10.5GB, so -T16 is rarely trimmed by the limit while keeping xz's sublinear speedup in its sweet spot.
1 parent 1bc1a1e commit 5e24e8f

1 file changed

Lines changed: 19 additions & 9 deletions

File tree

lib/functions/image/compress-checksum.sh

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,17 @@ function output_images_compress_and_checksum() {
3131
declare host_threads active_jobs compress_threads mem_avail_mb mem_budget_mb max_threads
3232
host_threads=$(nproc)
3333
# Cap threads: xz speedup is sublinear and its ratio slightly worsens past
34-
# ~16-32 threads (more independent blocks), while peak memory grows linearly
35-
# (~674MB/thread at -9). On a 128-core box an image would otherwise grab ~91
36-
# threads and reserve ~61GB for marginal wall-time gain. Override per-build
37-
# with COMPRESS_MAX_THREADS (set it >= nproc to disable the cap).
38-
max_threads="${COMPRESS_MAX_THREADS:-32}"
34+
# ~16 threads (more independent blocks), while peak memory grows linearly
35+
# (~674MB/thread at -9). 16 also matches the 10 GiB xz memlimit below
36+
# (16*674MB ~= 10.5GB), so -T rarely gets trimmed by the limit. On a
37+
# 128-core box an image would otherwise grab ~91 threads and reserve ~61GB
38+
# for marginal wall-time gain. Override per-build with COMPRESS_MAX_THREADS
39+
# (set it >= nproc to disable the cap).
40+
max_threads="${COMPRESS_MAX_THREADS:-16}"
3941
# Guard against a non-numeric or zero override: bash arithmetic reads
4042
# either as 0, which would set compress_threads=0 (xz/zstd -T0 = "use all
41-
# cores") and silently collapse the cap. Fall back to the default 32.
42-
[[ "${max_threads}" =~ ^[0-9]+$ ]] && (( max_threads >= 1 )) || max_threads=32
43+
# cores") and silently collapse the cap. Fall back to the default 16.
44+
[[ "${max_threads}" =~ ^[0-9]+$ ]] && (( max_threads >= 1 )) || max_threads=16
4345
compress_threads=$host_threads
4446
(( compress_threads > max_threads )) && compress_threads=$max_threads
4547
active_jobs=$(pgrep -cx 'xz|zstd|zstdmt' 2>/dev/null || true)
@@ -48,6 +50,14 @@ function output_images_compress_and_checksum() {
4850
mem_avail_mb=$(awk '/^MemAvailable:/ {print int($2 / 1024)}' /proc/meminfo)
4951
mem_budget_mb=$(( mem_avail_mb * 6 / 10 / (active_jobs + 1) ))
5052

53+
# Hard memory ceiling handed to `xz --memlimit-compress`: a backstop so the
54+
# encoder scales its own thread count down rather than risking OOM. Default
55+
# 10 GiB, lowered only when the host has less memory available than that.
56+
# Override explicitly with IMAGE_XZ_MEMLIMIT (e.g. "4GiB").
57+
declare xz_memlimit_mb=10240
58+
(( mem_avail_mb < xz_memlimit_mb )) && xz_memlimit_mb=$mem_avail_mb
59+
declare xz_memlimit="${IMAGE_XZ_MEMLIMIT:-${xz_memlimit_mb}MiB}"
60+
5161
# Pick the strongest xz preset that fits BOTH memory and CPU class. Each
5262
# entry is "level:per-thread-MB:min-threads" — the min-threads floor
5363
# protects weak ARM hosts (2-4 cores) where higher levels would be
@@ -159,8 +169,8 @@ function output_images_compress_and_checksum() {
159169
t_start=$SECONDS
160170

161171
if [[ $COMPRESS_OUTPUTIMAGE == *xz* ]]; then
162-
display_alert "Compressing with xz" "${uncompressed_file_basename}.xz (-${xz_compression_ratio_image}, threads: ${file_threads}/${compress_threads}, size: ${file_size_mb}MB, block: ${xz_block_mb}MB, peak: ~${peak_mem_mb}MB)" "info"
163-
xz -T "${file_threads}" "-${xz_compression_ratio_image}" "${uncompressed_file}" # "If xz is provided with input but no output, it will delete the input"
172+
display_alert "Compressing with xz" "${uncompressed_file_basename}.xz (-${xz_compression_ratio_image}, threads: ${file_threads}/${compress_threads}, size: ${file_size_mb}MB, block: ${xz_block_mb}MB, peak: ~${peak_mem_mb}MB, memlimit: ${xz_memlimit})" "info"
173+
xz --memlimit-compress="${xz_memlimit}" -T "${file_threads}" "-${xz_compression_ratio_image}" "${uncompressed_file}" # "If xz is provided with input but no output, it will delete the input"
164174
compression_type=".xz"
165175
elif [[ $COMPRESS_OUTPUTIMAGE == *zst* ]]; then
166176
# zstd auto-scales workers to input size, so no explicit cap needed here.

0 commit comments

Comments
 (0)