Skip to content

Instantly share code, notes, and snippets.

@VladislavSmolyanoy
Created May 6, 2025 17:02
Show Gist options
  • Select an option

  • Save VladislavSmolyanoy/57435bcdcb4358927e35dffd9faae706 to your computer and use it in GitHub Desktop.

Select an option

Save VladislavSmolyanoy/57435bcdcb4358927e35dffd9faae706 to your computer and use it in GitHub Desktop.
Zip Archiving Utilities Benchmark (incl. password)
#!/usr/bin/env bash
# zip_benchmark.sh — v2025‑05‑06  (quiet‑safe)
#
# Same purpose as previous version but now detects whether `zipcloak`
# supports `-q` (quiet). On macOSʼs default Info‑ZIP 3.0 the flag does
# **not** exist, so we omit it to avoid exit‑16 errors.
#
# ‣ Speed ranking
# ‣ Size ranking
# ‣ Efficiency (MiB × s)
#
# Usage:
# ./zip_benchmark.sh <SOURCE> [PASSWORD|-] [THREADS]
#
# ----------------------------------------------------------------------
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <SOURCE> [PASSWORD|-] [THREADS]" >&2
exit 1
fi
SRC="$1"
PASS="${2:-}"
THREADS="${3:-0}"
WITHOUT_PW=false
[[ -z "$PASS" || "$PASS" == "-" ]] && WITHOUT_PW=true
[[ -d "$SRC" || -f "$SRC" ]] || { echo "Source not found: $SRC" >&2; exit 1; }
have() { command -v "$1" >/dev/null 2>&1; }
# Detect if zipcloak supports -q
ZIPCLOAK_Q=""
if have zipcloak; then
if zipcloak -h 2>&1 | grep -q -- ' -q'; then
ZIPCLOAK_Q="-q"
fi
fi
RESULTS=() # label seconds bytes
measure() {
local label="$1"; shift
local outfile="$1"; shift
rm -f "$outfile"
echo "▶ $label ..."
local tmp_out
tmp_out=$(mktemp)
set +e
/usr/bin/time -p "$@" 2> >(tee "$tmp_out")
local exit_code=$?
set -e
local secs=""
if grep -q '^real ' "$tmp_out"; then
secs=$(awk '/^real /{print $2}' "$tmp_out")
fi
rm -f "$tmp_out"
if [[ $exit_code -ne 0 ]]; then
echo " ⚠️ ${label} failed (exit $exit_code) – skipped"
else
local bytes
bytes=$(wc -c < "$outfile")
RESULTS+=("$label $secs $bytes")
printf " ↳ %s finished in %ss, size %.2f MiB\n\n" \
"$label" "$secs" "$(echo "$bytes / 1048576" | bc -l)"
fi
}
###########################################################################
# 1. zip
###########################################################################
if have zip; then
for lvl in 1 6 9; do
if $WITHOUT_PW; then
measure "zip-$lvl" "zip_l${lvl}.zip" \
zip -qr -${lvl} "zip_l${lvl}.zip" "$SRC"
else
measure "zip-$lvl" "zip_l${lvl}.zip" \
zip -qr -${lvl} -P "$PASS" "zip_l${lvl}.zip" "$SRC"
fi
done
else
echo "zip not found – skipped"
fi
###########################################################################
# 2. p7zip
###########################################################################
if have 7z; then
mtflag=""
[[ "$THREADS" != "0" ]] && mtflag="-mmt=$THREADS"
for lvl in 1 5 9; do
if $WITHOUT_PW; then
measure "p7zip-$lvl" "p7zip_l${lvl}.zip" \
7z a -tzip "p7zip_l${lvl}.zip" "$SRC" $mtflag -mx=${lvl}
else
measure "p7zip-$lvl" "p7zip_l${lvl}.zip" \
7z a -tzip "p7zip_l${lvl}.zip" "$SRC" -p"$PASS" -mem=AES256 $mtflag -mx=${lvl}
fi
done
else
echo "p7zip not found – skipped"
fi
###########################################################################
# 3. pigz + zip(/zipcloak)
###########################################################################
if have pigz && have zip && have gzip; then
pigz_threads=""
[[ "$THREADS" != "0" ]] && pigz_threads="-p $THREADS"
for lvl in 1 6 9; do
if $WITHOUT_PW; then
measure "pigz-$lvl" "pigz_l${lvl}.zip" bash -c '
tar cf - "'"$SRC"'" | pigz '"$pigz_threads"' -'"$lvl"' > tmp_pigz.tar.gz && \
gzip -dc tmp_pigz.tar.gz | zip -q -j -0 pigz_l'"${lvl}"'.zip - && \
rm tmp_pigz.tar.gz'
else
have zipcloak || { echo "zipcloak not found – skipping pigz+zipcloak"; continue; }
measure "pigz+zipcloak-$lvl" "pigz_l${lvl}.zip" bash -c '
tar cf - "'"$SRC"'" | pigz '"$pigz_threads"' -'"$lvl"' > tmp_pigz.tar.gz && \
gzip -dc tmp_pigz.tar.gz | zip -q -j -0 pigz_l'"${lvl}"'.zip - && \
zipcloak '"$ZIPCLOAK_Q"' -P "'"$PASS"'" pigz_l'"${lvl}"'.zip >/dev/null && \
rm tmp_pigz.tar.gz'
fi
done
else
echo "pigz and/or zip not found – skipped"
fi
###########################################################################
# 4. zstd + (optional) gpg
###########################################################################
if have zstd; then
zstd_threads="-T$THREADS"
[[ "$THREADS" == "0" ]] && zstd_threads="-T0"
for tag in 1 3 19; do
case $tag in
1) zlvl="-1" ;; 3) zlvl="-3" ;; 19) zlvl="-19" ;;
esac
if $WITHOUT_PW; then
measure "zstd-$tag" "zstd_l${tag}.tar.zst" bash -c '
tar cf - "'"$SRC"'" | zstd '"$zstd_threads"' '"$zlvl"' -q -o zstd_l'"${tag}"'.tar.zst'
else
have gpg || { echo "gpg not found – skipping zstd+gpg"; continue; }
measure "zstd+gpg-$tag" "zstd_l${tag}.tar.zst.gpg" bash -c '
tar cf - "'"$SRC"'" | zstd '"$zstd_threads"' '"$zlvl"' -q | \
gpg --batch --yes --passphrase "'"$PASS"'" --symmetric \
--cipher-algo AES256 -o zstd_l'"${tag}"'.tar.zst.gpg'
fi
done
else
echo "zstd not found – skipped"
fi
###########################################################################
# Summaries
###########################################################################
if [[ ${#RESULTS[@]} -eq 0 ]]; then
echo "No successful runs."
exit 0
fi
report_file=$(mktemp)
for entry in "${RESULTS[@]}"; do
echo "$entry" >> "$report_file"
done
echo
echo "==================== SPEED (s) ===================="
printf "%-25s %10s\n" "METHOD" "SECONDS"
sort -k2 -n "$report_file" | awk '{printf "%-25s %10s\n",$1,$2}'
echo
echo "==================== SIZE (MiB) ===================="
printf "%-25s %10s\n" "METHOD" "MiB"
sort -k3 -n "$report_file" | awk '{printf "%-25s %10.2f\n",$1,$3/1048576}'
echo
echo "============ EFFICIENCY (MiB × s) ==============="
printf "%-25s %10s\n" "METHOD" "MiB*s"
awk '{eff=$2*($3/1048576); printf "%-25s %10.2f\n",$1,eff}' "$report_file" | sort -k2 -n
rm -f "$report_file"
@VladislavSmolyanoy
Copy link
Author

VladislavSmolyanoy commented May 6, 2025

Benchmark results

Date: 2025-05-06
OS: macOS Sequoia 15.5 Beta (24F5042g)
Machine: M1 Pro (10c) 32GB RAM 1TB SSD
Test Subject: 199.5 MB folder with binaries, text files, images.

Lower number → Better

SPEED (s)

Method Seconds
p7zip-1 1.30
zstd+gpg-3 1.65
zstd+gpg-1 1.78
zip-1 2.60
zip-6 5.40
p7zip-5 5.96
zstd+gpg-19 13.49
zip-9 16.23
p7zip-9 48.23

SIZE (MiB)

Method MiB
zstd+gpg-19 47.97
zstd+gpg-3 58.36
p7zip-9 61.57
zstd+gpg-1 62.26
p7zip-5 62.29
zip-9 63.21
zip-6 63.44
p7zip-1 64.45
zip-1 68.20

EFFICIENCY (MiB × s)

Method MiB × s
p7zip-1 83.79
zstd+gpg-3 96.29
zstd+gpg-1 110.83
zip-1 177.33
zip-6 342.56
p7zip-5 371.22
zstd+gpg-19 647.10
zip-9 1 025.89
p7zip-9 2 969.67

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment