SinclairSchneider commited on
Commit
41ce93e
·
verified ·
1 Parent(s): a72139f

Update setup_deepseek_v4_sm89.sh

Browse files
Files changed (1) hide show
  1. setup_deepseek_v4_sm89.sh +144 -12
setup_deepseek_v4_sm89.sh CHANGED
@@ -63,12 +63,38 @@ SERVED_NAME="DeepSeek-V4-Flash" # name exposed on the OpenAI-compatible API
63
  PORT=8002
64
  GPUS="0,1,2,3"
65
  TP_SIZE=4
66
- MAX_MODEL_LEN=262144
67
- GPU_MEM_UTIL=0.96
68
- MAX_NUM_SEQS=4
69
- MAX_NUM_BATCHED_TOKENS=2048 # author's spec-decode recommendation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  NUM_SPEC_TOKENS=1 # MTP draft depth (1 = validated config)
71
- REASONING_EFFORT="high" # default chat-template reasoning effort
 
 
 
 
72
  SERVICE_USER="$(id -un)" # user baked into the generated systemd unit
73
 
74
  PYTHON_BIN="python3.12" # venv interpreter (wheel is cp312)
@@ -344,11 +370,35 @@ cat > "$PROJECT_DIR/serve.sh" <<SERVE_EOF
344
  # letting hub ref resolution run under HF_HUB_OFFLINE: an interrupted
345
  # online check can leave refs/main pointing at a snapshot that was never
346
  # downloaded (LocalEntryNotFoundError at boot).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347
  # * MTP measured at 115-133 tok/s (~1.5-1.65x over the ~80 tok/s baseline),
348
  # 96.6-100% draft acceptance. To run WITHOUT speculative decoding, delete
349
  # the --speculative-config line (and optionally --max-num-batched-tokens).
350
- # * First requests JIT-compile ~a dozen Triton kernels (one-time latency
351
- # spikes); throughput settles after the warm-up traffic.
 
352
  set -euo pipefail
353
  cd "\$(dirname "\$0")"
354
 
@@ -396,6 +446,7 @@ exec env -u VLLM_TRITON_MLA_SPARSE_HEAD_BLOCK_SIZE \\
396
  PATH="$CUDA_HOME_DIR/bin:\$PATH" \\
397
  FLASHINFER_DISABLE_VERSION_CHECK=1 \\
398
  VLLM_USE_FLASHINFER_SAMPLER=0 \\
 
399
  HF_HUB_OFFLINE=1 \\
400
  CUDA_VISIBLE_DEVICES=$GPUS \\
401
  "\$(dirname "\$0")/.venv/bin/vllm" serve "\$MODEL_PATH" \\
@@ -429,6 +480,69 @@ echo
429
  CANARY_EOF
430
  chmod +x "$PROJECT_DIR/canary.sh"
431
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  # ----------------------------- GENERATE systemd unit -------------------------
433
  log "Writing $PROJECT_DIR/deepseek-v4-flash.service"
434
  cat > "$PROJECT_DIR/deepseek-v4-flash.service" <<UNIT_EOF
@@ -438,6 +552,13 @@ cat > "$PROJECT_DIR/deepseek-v4-flash.service" <<UNIT_EOF
438
  # supervises it. systemd's clean service environment additionally guarantees
439
  # no stale shell-profile exports can leak in.
440
  #
 
 
 
 
 
 
 
441
  # Install:
442
  # sudo cp $PROJECT_DIR/deepseek-v4-flash.service /etc/systemd/system/
443
  # sudo systemctl daemon-reload
@@ -466,7 +587,16 @@ ExecStartPre=/usr/bin/nvidia-smi
466
 
467
  ExecStart=$PROJECT_DIR/serve.sh
468
 
469
- Restart=on-failure
 
 
 
 
 
 
 
 
 
470
  # Weight load alone is ~80 s (+ ~5 s MTP drafter); don't hammer restarts.
471
  RestartSec=15
472
  # Give the engine time to tear down 4 TP workers and NCCL cleanly on stop.
@@ -491,13 +621,15 @@ Next steps:
491
  run once:
492
  $PROJECT_DIR/.venv/bin/huggingface-cli download "$MODEL"
493
  2. Start the server: $PROJECT_DIR/serve.sh
494
- Expect ~80 s of weight loading (+ ~5 s for the MTP drafter), brief
495
- Triton JIT compiles during warmup and the first requests, then
496
  'Application startup complete'.
497
- 3. Smoke test: $PROJECT_DIR/canary.sh
 
 
 
498
  A correct one-sentence answer naming Muenchen = numerics are sound.
499
  Watch the SpecDecoding metrics lines: draft acceptance should sit
500
  around 95-100% with mean acceptance length near 2.00.
501
- 4. Run as a service: see header of
502
  $PROJECT_DIR/deepseek-v4-flash.service
503
  NEXT_EOF
 
63
  PORT=8002
64
  GPUS="0,1,2,3"
65
  TP_SIZE=4
66
+ # MEMORY/THROUGHPUT: validated corner from a day of SWE-bench Verified pilots
67
+ # (2026-07-23). KV is the thin slice on this model -- weights + activations +
68
+ # cudagraph pools eat almost the whole 48 GB card. The values below trade
69
+ # against each other; see the generated serve.sh header for the full story.
70
+ # Short version:
71
+ # * util 0.97 leaves ~1.68 GiB/GPU free at idle, ~0.5-0.8 GiB under a long
72
+ # trajectory -- THIN. It is above the Triton JIT-OOM line only because
73
+ # warmup.sh pre-compiles the kernel shapes at startup and the systemd unit
74
+ # auto-restarts+re-warms. For an UNATTENDED or shared deployment, prefer
75
+ # GPU_MEM_UTIL=0.95 (restores ~2.4 GiB headroom; identical throughput at
76
+ # seq=1). See serve.sh header.
77
+ # * max-model-len 98304 fits KV and covers agentic trajectories (~30-100k);
78
+ # 262144 will NOT boot (KV-fit ValueError). Prompt + max_tokens must be
79
+ # <= this, so keep the harness's requested output tokens modest.
80
+ # * max-num-seqs 1: at seq>1, multiple 30-100k trajectories don't fit KV at
81
+ # once and vLLM preempts + RE-PREFILLS from scratch (prefix-cache hit
82
+ # collapses <40%, ~10x wall-clock blowup). One resident stream (prefix hit
83
+ # ~85-91%) wins decisively here.
84
+ # * batched-tokens 4096: raising it toggles chunked prefill, whose buffers
85
+ # come out of the KV pool -- not free on this card.
86
+ # Pilot results (50-instance SWE-bench Verified, error_instances=0):
87
+ # 64k/8192-out=56% | 96k/8192-out=60% (best) | 96k/high-effort=60% (no gain)
88
+ MAX_MODEL_LEN=98304
89
+ GPU_MEM_UTIL=0.97
90
+ MAX_NUM_SEQS=1
91
+ MAX_NUM_BATCHED_TOKENS=4096 # 4096 tested; raising toggles chunked prefill (eats KV)
92
  NUM_SPEC_TOKENS=1 # MTP draft depth (1 = validated config)
93
+ REASONING_EFFORT="medium" # default chat-template reasoning effort.
94
+ # high effort showed NO resolved-rate gain over
95
+ # medium on the pilots (60% both) at ~40% more
96
+ # wall-clock and more step-limit failures.
97
+ WARMUP_WAIT_MAX=300 # warmup.sh: seconds to wait for the API before giving up
98
  SERVICE_USER="$(id -un)" # user baked into the generated systemd unit
99
 
100
  PYTHON_BIN="python3.12" # venv interpreter (wheel is cp312)
 
370
  # letting hub ref resolution run under HF_HUB_OFFLINE: an interrupted
371
  # online check can leave refs/main pointing at a snapshot that was never
372
  # downloaded (LocalEntryNotFoundError at boot).
373
+ # * MEMORY IS THE KNIFE-EDGE on this model (incident 2026-07-23). KV is the
374
+ # thin slice after weights/activations/cudagraphs; two failure modes bound
375
+ # the tuning window:
376
+ # - JIT-OOM (too little headroom): Triton compiles a kernel per prompt
377
+ # shape and loading the cubin (cuModuleLoadData) needs driver-level free
378
+ # VRAM outside the torch pool. At high util a never-seen shape mid-run
379
+ # OOM'd the load in _tf32_hc_prenorm_gemm and killed a worker, taking a
380
+ # 2h benchmark down. Mitigated by warmup.sh (pre-compiles shapes at
381
+ # startup) + systemd auto-restart+re-warm.
382
+ # - KV-fit refusal (too little KV): boot fails if one full max-model-len
383
+ # sequence won't fit the pool (262144 needs ~1.84 GiB/GPU -> NO BOOT).
384
+ # Current values: util 0.97, max-model-len 98304, max-num-seqs 1. MEASURED
385
+ # ~1.68 GiB/GPU free at idle, ~0.5-0.8 GiB under a long trajectory -- thin.
386
+ # For UNATTENDED/shared serving, drop util to 0.95 (restores ~2.4 GiB
387
+ # headroom, same throughput at seq=1). Do NOT raise max-model-len toward
388
+ # 262k without lowering util -- that reopens the JIT-OOM hole.
389
+ # * max-num-seqs 1: at seq>1 the KV pool can't hold multiple 30-100k agentic
390
+ # trajectories, so vLLM preempts and RE-PREFILLS evicted sequences (prefix
391
+ # hit <40%, ~10x wall-clock blowup). seq=1 keeps one context resident
392
+ # (prefix hit ~85-91%) decoding continuously; it wins decisively here.
393
+ # * PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True keeps the torch pool from
394
+ # overgrowing past its profiled peak on long prefills and eating the JIT
395
+ # headroom. (If graph capture ever rejects it on a future wheel, drop it.)
396
  # * MTP measured at 115-133 tok/s (~1.5-1.65x over the ~80 tok/s baseline),
397
  # 96.6-100% draft acceptance. To run WITHOUT speculative decoding, delete
398
  # the --speculative-config line (and optionally --max-num-batched-tokens).
399
+ # * First requests JIT-compile Triton kernels per shape (one-time latency
400
+ # spikes). Run ./warmup.sh once after every server start so the shapes
401
+ # compile at minute 0, not minute 100 of an unattended run.
402
  set -euo pipefail
403
  cd "\$(dirname "\$0")"
404
 
 
446
  PATH="$CUDA_HOME_DIR/bin:\$PATH" \\
447
  FLASHINFER_DISABLE_VERSION_CHECK=1 \\
448
  VLLM_USE_FLASHINFER_SAMPLER=0 \\
449
+ PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \\
450
  HF_HUB_OFFLINE=1 \\
451
  CUDA_VISIBLE_DEVICES=$GPUS \\
452
  "\$(dirname "\$0")/.venv/bin/vllm" serve "\$MODEL_PATH" \\
 
480
  CANARY_EOF
481
  chmod +x "$PROJECT_DIR/canary.sh"
482
 
483
+ # ----------------------------- GENERATE warmup.sh ----------------------------
484
+ # Forces the Triton per-shape specializations to JIT-compile at startup, in
485
+ # fresh VRAM headroom and under supervision, instead of lazily mid-run into a
486
+ # thin pool (incident 2026-07-23). Wired into the systemd unit's ExecStartPost.
487
+ log "Writing $PROJECT_DIR/warmup.sh"
488
+ cat > "$PROJECT_DIR/warmup.sh" <<WARMUP_EOF
489
+ #!/usr/bin/env bash
490
+ # Triton-specialization warmup for the DeepSeek-V4-Flash server.
491
+ # -- generated by setup_deepseek_v4_sm89.sh
492
+ #
493
+ # The wait-for-server loop is BOUNDED (WAIT_MAX, default ${WARMUP_WAIT_MAX}s):
494
+ # as ExecStartPost, an unbounded loop would pin the unit in
495
+ # 'activating (start-post)' with a dead main PID if the engine dies during
496
+ # init. Bail fast so systemd can fail and Restart= act.
497
+ #
498
+ # Usage: ./warmup.sh [host] [port] (defaults: 127.0.0.1 $PORT)
499
+ # WAIT_MAX=<seconds> overrides the server-wait bound.
500
+ set -euo pipefail
501
+ HOST="\${1:-127.0.0.1}"
502
+ PORT="\${2:-$PORT}"
503
+ URL="http://\$HOST:\$PORT/v1/chat/completions"
504
+ MODEL="$SERVED_NAME"
505
+ WAIT_MAX="\${WAIT_MAX:-$WARMUP_WAIT_MAX}"
506
+ MAXLEN="$MAX_MODEL_LEN"
507
+
508
+ echo "==> waiting for server at \$HOST:\$PORT (up to \${WAIT_MAX}s) ..."
509
+ deadline=\$(( \$(date +%s) + WAIT_MAX ))
510
+ until curl -sf "http://\$HOST:\$PORT/v1/models" >/dev/null 2>&1; do
511
+ if [ "\$(date +%s)" -ge "\$deadline" ]; then
512
+ echo "XX server not reachable after \${WAIT_MAX}s -- engine likely died during init; giving up" >&2
513
+ exit 1
514
+ fi
515
+ sleep 5
516
+ done
517
+ echo "==> server is up"
518
+
519
+ req() { # req <approx_token_count>
520
+ local n="\$1"
521
+ local prompt payload
522
+ prompt="\$(yes 'the' | head -n "\$n" | tr '\n' ' ')"
523
+ # Build JSON via printf to avoid nested-quote escaping hazards.
524
+ payload="\$(printf '{"model":"%s","messages":[{"role":"user","content":"%s"}],"max_tokens":16,"temperature":0}' "\$MODEL" "\$prompt")"
525
+ curl -s -o /dev/null -w " HTTP %{http_code} in %{time_total}s (n=\$n)\n" \\
526
+ "\$URL" -H 'Content-Type: application/json' --data "\$payload"
527
+ }
528
+
529
+ # Sweep shapes up to (but not over) --max-model-len; an over-cap request just
530
+ # 400s and compiles nothing useful. Top entry sits just under MAXLEN.
531
+ echo "==> sequential shape sweep (long ones take a while; that is the point)"
532
+ top=\$(( MAXLEN > 4096 ? MAXLEN - 4096 : MAXLEN / 2 ))
533
+ for n in 16 100 511 1024 2000 4096 8192 16384 32768 65536 "\$top"; do
534
+ [ "\$n" -le "\$MAXLEN" ] && req "\$n"
535
+ done
536
+
537
+ echo "==> warmup done. Driver-level headroom per GPU (want >= ~1 GiB free):"
538
+ if command -v nvidia-smi >/dev/null 2>&1; then
539
+ nvidia-smi --query-gpu=index,memory.used,memory.total --format=csv
540
+ else
541
+ echo " (nvidia-smi not on PATH here; check on the host)"
542
+ fi
543
+ WARMUP_EOF
544
+ chmod +x "$PROJECT_DIR/warmup.sh"
545
+
546
  # ----------------------------- GENERATE systemd unit -------------------------
547
  log "Writing $PROJECT_DIR/deepseek-v4-flash.service"
548
  cat > "$PROJECT_DIR/deepseek-v4-flash.service" <<UNIT_EOF
 
552
  # supervises it. systemd's clean service environment additionally guarantees
553
  # no stale shell-profile exports can leak in.
554
  #
555
+ # Incident 2026-07-23: a Triton JIT module load OOM'd and killed a worker
556
+ # mid-run; the API server then exited *cleanly* (main PID rc 0), so the old
557
+ # Restart=on-failure never fired and the port stayed dead. Hence Restart=always
558
+ # below -- for an inference server a crash that looks like a clean exit is
559
+ # still a crash; only 'systemctl stop' should keep it down. ExecStartPost runs
560
+ # warmup.sh after every (re)start so Triton specializations compile up front.
561
+ #
562
  # Install:
563
  # sudo cp $PROJECT_DIR/deepseek-v4-flash.service /etc/systemd/system/
564
  # sudo systemctl daemon-reload
 
587
 
588
  ExecStart=$PROJECT_DIR/serve.sh
589
 
590
+ # Warm the Triton shape specializations after EVERY start (incl. auto-restart).
591
+ # warmup.sh waits for the API itself and is bounded, so it cannot hang the
592
+ # unit; '-' prefix + timeout mean a wedged warmup never fails the start.
593
+ ExecStartPost=-/usr/bin/timeout 900 $PROJECT_DIR/warmup.sh
594
+ # Budget must cover ExecStartPre + weight load + bounded warmup.
595
+ TimeoutStartSec=1200
596
+
597
+ # Restart=always, NOT on-failure: a JIT-OOM crash can present as a clean exit
598
+ # (see incident note above); only an explicit 'systemctl stop' should end it.
599
+ Restart=always
600
  # Weight load alone is ~80 s (+ ~5 s MTP drafter); don't hammer restarts.
601
  RestartSec=15
602
  # Give the engine time to tear down 4 TP workers and NCCL cleanly on stop.
 
621
  run once:
622
  $PROJECT_DIR/.venv/bin/huggingface-cli download "$MODEL"
623
  2. Start the server: $PROJECT_DIR/serve.sh
624
+ Expect ~80 s of weight loading (+ ~5 s for the MTP drafter), then
 
625
  'Application startup complete'.
626
+ 3. Warm up (once/start): $PROJECT_DIR/warmup.sh
627
+ Compiles Triton shapes up front and prints free VRAM/GPU (want >= ~1 GiB).
628
+ Runs automatically via ExecStartPost under the systemd unit.
629
+ 4. Smoke test: $PROJECT_DIR/canary.sh
630
  A correct one-sentence answer naming Muenchen = numerics are sound.
631
  Watch the SpecDecoding metrics lines: draft acceptance should sit
632
  around 95-100% with mean acceptance length near 2.00.
633
+ 5. Run as a service: see header of
634
  $PROJECT_DIR/deepseek-v4-flash.service
635
  NEXT_EOF