#!/usr/bin/env bash # Max context length exploration for P800 SGLang INT8. set -Eeuo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" EXPERIMENT_NAME="$(basename "$SCRIPT_DIR")" # shellcheck source=/dev/null source "${SCRIPT_DIR}/../../../scripts/common/lib.sh" # shellcheck source=/dev/null source "${SCRIPT_DIR}/../../../scripts/common/platform.sh" # shellcheck source=/dev/null source "${SCRIPT_DIR}/config.env" RUN_ID="${RUN_ID:-$(date '+%Y%m%d-%H%M%S')}" RESULT_ROOT="${RESULT_ROOT:-${SCRIPT_DIR}/results/${RUN_ID}}" LOG_DIR="${RESULT_ROOT}/logs" RAW_DIR="${RESULT_ROOT}/raw_outputs" CONTAINER_NAME="${CONTAINER_NAME:-sglang-dsv4-flash}" CONTAINER_PYTHON="${CONTAINER_PYTHON:-/root/miniconda/envs/python310_torch25_cuda/bin/python}" ensure_result_root "$RESULT_ROOT" log_init "${LOG_DIR}/orchestrator.log" log "experiment=${EXPERIMENT_NAME}" log "run_id=${RUN_ID}" log "result_root=${RESULT_ROOT}" log "platform=${PLATFORM}" log "hardware=${HARDWARE}" log "model=${MODEL_PATH}" log "candidate_lengths=${CANDIDATE_LENS[*]}" # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- is_server_healthy() { curl --fail --silent --show-error --max-time 5 "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1 } stop_server() { log "stopping container ${CONTAINER_NAME}" docker rm -f "$CONTAINER_NAME" 2>/dev/null || true pkill -9 -f 'sglang.launch_server' 2>/dev/null || true sleep 2 } start_server() { local target_len="$1" log "starting server (target_len=${target_len})" bash "${SCRIPT_DIR}/start_server.sh" "$target_len" >> "${LOG_DIR}/start_server_${target_len}.log" 2>&1 } build_server_args() { local target_len="$1" local max_context_len=$(( target_len + CONTEXT_PAD )) echo "python -m sglang.launch_server --host 0.0.0.0 --port ${PORT} --model-path /models --attention-backend nsa --nsa-prefill klxdsa --nsa-decode klxdsa --trust-remote-code --disable-custom-all-reduce --chunked-prefill-size 8192 --page-size 64 --mem-fraction-static 0.8 --max-prefill-tokens ${max_context_len} --max-running-requests 64 --tensor-parallel-size 8 --ep-size 8 --disable-shared-experts-fusion --quantization w8a8_int8 --kv-cache-dtype float16 --disable-piecewise-cuda-graph --cuda-graph-max-bs 32 --watchdog-timeout 3000000 --tool-call-parser deepseekv4 --reasoning-parser deepseek-v4 --speculative-algorithm EAGLE --speculative-num-steps 3 --speculative-eagle-topk 1 --speculative-num-draft-tokens 4 --constrained-json-disable-any-whitespace --enable-metrics --enable-request-time-stats-logging --context-length ${max_context_len}" } append_scenario() { local scenario_json="$1" python3 - "$RESULT_JSON" "$scenario_json" <<'PY' import json import sys results_path, scenario_json = sys.argv[1], sys.argv[2] with open(results_path, "r", encoding="utf-8") as f: data = json.load(f) data["scenarios"].append(json.loads(scenario_json)) with open(results_path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) PY } # Build a scenario JSON blob safely with Python. build_scenario_json() { local target_len="$1" local max_context_len="$2" local success="$3" local server_args="$4" local error_msg="$5" local metrics_json="${6:-null}" python3 - "$target_len" "$max_context_len" "$success" "$server_args" "$error_msg" "$metrics_json" <<'PY' import json import sys target_len, max_context_len, success, server_args, error_msg, metrics_json = sys.argv[1:7] metrics = json.loads(metrics_json) if metrics_json not in ("null", "") else None print(json.dumps({ "backend": "sglang", "target_len": int(target_len), "max_context_len": int(max_context_len), "success": success == "true", "server_args": server_args, "metrics": metrics, "error": error_msg or None, }, ensure_ascii=False)) PY } run_length() { local target_len="$1" local max_context_len=$(( target_len + CONTEXT_PAD )) local output_file="${RAW_DIR}/sglang_ctx_${target_len}.jsonl" local container_output="/tmp/bench_outputs/sglang_ctx_${target_len}.jsonl" local detail_log="${LOG_DIR}/sglang_ctx_${target_len}.log" log "trying sglang target_len=${target_len} (context-length=${max_context_len})" stop_server local server_args server_args="$(build_server_args "$target_len")" if ! start_server "$target_len"; then local err="server failed to start" log "ERROR: ${err}" append_scenario "$(build_scenario_json "$target_len" "$max_context_len" "false" "$server_args" "$err")" return 1 fi docker exec "$CONTAINER_NAME" mkdir -p "$(dirname "$container_output")" local bench_rc=0 docker exec "$CONTAINER_NAME" \ env HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 HF_DATASETS_OFFLINE=1 \ "${CONTAINER_PYTHON}" -m sglang.bench_serving \ --backend sglang \ --host 127.0.0.1 \ --port "$PORT" \ --model "$MODEL_PATH" \ --dataset-name random \ --dataset-path /workspace/dummy_sharegpt.json \ --random-input-len "$target_len" \ --random-output-len "$OUTPUT_LEN" \ --random-range-ratio 1.0 \ --num-prompts "$NUM_PROMPTS" \ --max-concurrency "$MAX_CONCURRENCY" \ --request-rate "$REQUEST_RATE" \ --output-file "$container_output" \ --output-details \ > "$detail_log" 2>&1 || bench_rc=$? if [[ "$bench_rc" -ne 0 ]]; then local err="bench_serving exited with code ${bench_rc}; see ${detail_log}" log "ERROR: sglang target_len=${target_len} failed; ${err}" append_scenario "$(build_scenario_json "$target_len" "$max_context_len" "false" "$server_args" "$err")" stop_server return 1 fi docker cp "${CONTAINER_NAME}:${container_output}" "$output_file" || { local err="failed to copy output from container" log "ERROR: ${err}" append_scenario "$(build_scenario_json "$target_len" "$max_context_len" "false" "$server_args" "$err")" stop_server return 1 } # Verify the request actually completed. local completed completed="$(python3 -c " import json with open('${output_file}') as f: for line in f: data = json.loads(line) print(data.get('completed', 0)) break ")" if [[ "${completed:-0}" -lt "$NUM_PROMPTS" ]]; then local err="only ${completed}/${NUM_PROMPTS} requests completed" log "ERROR: sglang target_len=${target_len} ${err}" append_scenario "$(build_scenario_json "$target_len" "$max_context_len" "false" "$server_args" "$err")" stop_server return 1 fi local metrics_json metrics_json="$(python3 "${SCRIPT_DIR}/extract_metrics.py" "$output_file")" append_scenario "$(build_scenario_json "$target_len" "$max_context_len" "true" "$server_args" "" "$metrics_json")" log "sglang target_len=${target_len} succeeded" stop_server } # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- mkdir -p "$RAW_DIR" "$LOG_DIR" RESULT_JSON="${RESULT_ROOT}/results.json" write_metadata_json \ "$RESULT_JSON" \ "$EXPERIMENT_NAME" \ "$RUN_ID" \ "$MODEL_PATH" \ "sglang" \ "sglang-xpu" \ "$HARDWARE" \ "$ACCELERATOR" \ "$CHIP" \ "experiments/${EXPERIMENT_NAME}/run_bench.sh" \ "" \ "P800 max context length exploration for DeepSeek-V4-Flash-INT8" # Embed config. python3 - "$RESULT_JSON" <<'PY' import json import sys path = sys.argv[1] with open(path, "r", encoding="utf-8") as f: data = json.load(f) data["config"] = { "tp": 8, "ep": 8, "xpu_visible_devices": "0,1,2,3,4,5,6,7", "output_len": 1, "num_prompts": 1, "max_concurrency": 1, "request_rate": 10000, "context_pad": 1024, } with open(path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) PY stop_server for target_len in "${CANDIDATE_LENS[@]}"; do if ! run_length "$target_len"; then log "stopping exploration after first failure at target_len=${target_len}" break fi done log "parsing results" python3 "${SCRIPT_DIR}/parse_results.py" "$RESULT_ROOT" >> "${LOG_DIR}/parse.log" 2>&1 || { log "WARNING: parse_results.py failed; see ${LOG_DIR}/parse.log" } log "all results saved to ${RESULT_ROOT}"