mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-07-17 16:47:50 +00:00
contrib: Benchmark SGLang compression
Process-only measurements do not show how compressed pages affect a containerized inference server, where Podman configuration, model startup, and service readiness are part of the restore path. SGLang also needs content validation so a quick restore cannot hide damaged state. Add a CPU and GPU SGLang benchmark that checkpoints through Podman. Compare uncompressed, per-page LZ4, and region LZ4 images. Send the same deterministic chat request before and after restore, compare response hashes, measure archive and operation costs, and verify the compression mode and region size recorded in the exported inventory. Derive the local serving URL from the selected port unless one is supplied. Podman passes CRIU options through the host-wide runc.conf. Serialize updates with a canonical lock and crash-recovery journal, preserve file metadata and xattrs, refuse to overwrite external changes, and restore the original configuration on success and during error or signal cleanup. Isolate CRIU from ambient defaults, keep access tokens out of arguments and diagnostics, detect stopped containers during health checks, and preserve failed artifacts. Add a bounded CPU test runner. Assisted-by: Codex:GPT-5 Assisted-by: Claude:claude-fable-5 Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
This commit is contained in:
parent
ec4a43b42e
commit
52d5baecc8
4 changed files with 1552 additions and 0 deletions
172
contrib/compression-benchmark/podman-sglang.py
Executable file
172
contrib/compression-benchmark/podman-sglang.py
Executable file
|
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Podman SGLang Checkpoint/Restore Benchmark
|
||||
|
||||
Starts an SGLang container with Podman, validates inference, checkpoints it with
|
||||
Podman, removes it, restores it with Podman, and validates inference again.
|
||||
|
||||
This benchmarks Podman's container checkpoint/restore path while varying CRIU
|
||||
memory-page compression through /etc/criu/runc.conf. Podman's own checkpoint
|
||||
archive compression is kept at "none" by default so the reported archive size
|
||||
reflects CRIU image size rather than tar-level gzip/zstd compression.
|
||||
|
||||
Example:
|
||||
sudo HF_TOKEN=... python3 contrib/compression-benchmark/podman-sglang.py \\
|
||||
--model Qwen/Qwen3-0.6B -n 3 \\
|
||||
--modes uncompressed lz4-page lz4-region
|
||||
|
||||
CPU-only example (requires an SGLang CPU image):
|
||||
sudo python3 contrib/compression-benchmark/podman-sglang.py \\
|
||||
--accelerator cpu --image sglang-cpu:latest
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
_BENCHMARK_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
if _BENCHMARK_DIR not in sys.path:
|
||||
sys.path.insert(0, _BENCHMARK_DIR)
|
||||
|
||||
import podman_common as common # noqa: E402
|
||||
|
||||
|
||||
class SglangAdapter:
|
||||
key = "sglang"
|
||||
display_name = "SGLang"
|
||||
heading = "SGLANG"
|
||||
default_container_name = "sglang-criu-bench"
|
||||
temp_prefix = "podman-sglang-bench-"
|
||||
|
||||
@staticmethod
|
||||
def add_image_arguments(parser):
|
||||
parser.add_argument(
|
||||
"--image",
|
||||
help="Container image (default: lmsysorg/sglang:latest for GPU; "
|
||||
"required for CPU)",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def add_model_arguments(parser):
|
||||
parser.add_argument(
|
||||
"--sglang-model-arg",
|
||||
choices=["model", "model-path"],
|
||||
default="model-path",
|
||||
help="SGLang launch flag used for the model path",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def add_resource_arguments(parser):
|
||||
parser.add_argument("--mem-fraction-static", type=float, default=0.35)
|
||||
parser.add_argument("--max-total-tokens", type=int, default=8192)
|
||||
parser.add_argument("--context-length", type=int, default=8192)
|
||||
parser.add_argument(
|
||||
"--tensor-parallel-size",
|
||||
type=int,
|
||||
default=1,
|
||||
help="SGLang tensor-parallel ranks (default: 1)",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def add_request_arguments(parser):
|
||||
parser.add_argument(
|
||||
"--enable-thinking",
|
||||
action="store_true",
|
||||
help="Do not add chat_template_kwargs.enable_thinking=false",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def add_server_arguments(parser):
|
||||
parser.add_argument(
|
||||
"--sglang-arg",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Extra raw argument appended to sglang.launch_server",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def normalize_args(parser, args):
|
||||
if args.image is not None:
|
||||
return
|
||||
if args.accelerator == "cpu":
|
||||
parser.error(
|
||||
"--image is required for CPU mode; build the SGLang "
|
||||
"docker/xeon.Dockerfile image first"
|
||||
)
|
||||
args.image = "lmsysorg/sglang:latest"
|
||||
|
||||
@staticmethod
|
||||
def prepare_args(args):
|
||||
if not args.enable_thinking:
|
||||
extra = dict(args.chat_extra_json or {})
|
||||
extra.setdefault(
|
||||
"chat_template_kwargs", {"enable_thinking": False}
|
||||
)
|
||||
args.chat_extra_json = extra
|
||||
|
||||
@staticmethod
|
||||
def cpu_podman_args(args):
|
||||
return [
|
||||
"--security-opt", "seccomp=unconfined",
|
||||
"--cap-add", "SYS_NICE",
|
||||
"--env", "SGLANG_USE_CPU_ENGINE=1",
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def server_argv(args):
|
||||
command = [
|
||||
args.image,
|
||||
"python3", "-m", "sglang.launch_server",
|
||||
f"--{args.sglang_model_arg}", args.model,
|
||||
"--host", "0.0.0.0",
|
||||
"--port", str(args.port),
|
||||
"--max-total-tokens", str(args.max_total_tokens),
|
||||
"--context-length", str(args.context_length),
|
||||
"--tp", str(args.tensor_parallel_size),
|
||||
]
|
||||
if args.accelerator == "gpu":
|
||||
command += ["--mem-fraction-static", str(args.mem_fraction_static)]
|
||||
else:
|
||||
command += ["--device", "cpu", "--disable-overlap-schedule"]
|
||||
command += args.sglang_arg
|
||||
return command
|
||||
|
||||
@staticmethod
|
||||
def server_summary(args):
|
||||
return f"model-arg={args.sglang_model_arg}"
|
||||
|
||||
|
||||
_benchmark = common.ServingBenchmark(SglangAdapter(), __doc__)
|
||||
|
||||
# Keep useful import-level helpers available to callers and focused tests.
|
||||
format_bytes = common.format_bytes
|
||||
format_duration = common.format_duration
|
||||
inventory_bytes_from_archive = common.inventory_bytes_from_archive
|
||||
inventory_entry_from_archive = common.inventory_entry_from_archive
|
||||
verify_archive_compression = common.verify_archive_compression
|
||||
strip_compression_runc_options = common.strip_compression_runc_options
|
||||
compression_config_lines = common.compression_config_lines
|
||||
json_config = common.json_config
|
||||
format_cmd = common.format_cmd
|
||||
run_arg_sets_environment = common.run_arg_sets_environment
|
||||
write_file = common.write_file
|
||||
os = common.os
|
||||
shutil = common.shutil
|
||||
signal = common.signal
|
||||
subprocess = common.subprocess
|
||||
tempfile = common.tempfile
|
||||
urllib = common.urllib
|
||||
|
||||
main = _benchmark.main
|
||||
podman_env = _benchmark.podman_env
|
||||
set_runc_conf_for_cfg = _benchmark.set_runc_conf_for_cfg
|
||||
restore_runc_conf = _benchmark.restore_runc_conf
|
||||
build_container_cmd = _benchmark.build_container_cmd
|
||||
run_trial = _benchmark.run_trial
|
||||
cleanup = _benchmark.cleanup
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except (OSError, RuntimeError) as error:
|
||||
sys.exit(f"Error: {error}")
|
||||
1347
contrib/compression-benchmark/podman_common.py
Normal file
1347
contrib/compression-benchmark/podman_common.py
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue