feat(pd): PD 长上下文 adaptive concurrency bench(SLO 方案 A,并发 +16)

- matrix.json: 3 个 shape(64k/128, 16k/1k, 1k/4k)
- run_adaptive_concurrency_pd.sh: PD 专用 adaptive 脚本
  - 复用 adaptive_bench_lib.sh(并发搜索/SLO 停止/OOM 检测/完整产物)
  - server 生命周期函数 no-op(PD 服务常驻,不启停)
  - 加 --flush-cache(配合 --disable-radix-cache 测纯净 TTFT/TPOT)
  - 离线环境变量 + 本地 tokenizer(避免 HF 在线下载)
- adaptive_config.env: SEARCH_ADDEND=16 +16 递增,上限 64,回退 8/1
- config.env: 新增 get_ttft_slo_ms 分层 SLO(1k->4s, 16k->15s, 64k->30s)
This commit is contained in:
shishi 2026-08-11 14:45:36 +08:00
parent fe375e1307
commit c69831f258
4 changed files with 217 additions and 1 deletions

View File

@ -0,0 +1,49 @@
# PD 分离MoonCake RDMA长上下文 adaptive concurrency 配置。
#
# 并发搜索:从 C=16 起 +16 递增SEARCH_ADDEND=16上限 64。
# SEARCH_MULTIPLIER 为占位(库日志无条件引用它),实际走 SEARCH_ADDEND 加法递增。
# 若初始并发就违反 TTFT SLO回退 C=8 → C=1SEARCH_INITIAL_BACKOFF
# 若 C=1 仍严重超 SLO 或 OOM停止该 shape。
# SLO 方案 ATTFT P95 随 ISL 分层递增(见 config.env 的 get_ttft_slo_ms
SEARCH_MULTIPLIER="${SEARCH_MULTIPLIER:-2}"
SEARCH_START_CONCURRENCY="${SEARCH_START_CONCURRENCY:-16}"
SEARCH_ADDEND=16
SEARCH_MAX_CONCURRENCY="${SEARCH_MAX_CONCURRENCY:-64}"
# add16 初始探测违反 SLO 时回退16 -> 8 -> 1。
SEARCH_INITIAL_BACKOFF_CONCURRENCIES="8 1"
# 长上下文场景不用整组跳过64k 单并发 TTFT 可能就较高)。
# TTFT_GROUP_SKIP_MS 留空禁用。
# TTFT_SLO_MS 是 flat fallbackconfig.env 定义了 get_ttft_slo_ms() 时以函数为准。
TTFT_SLO_MS="${TTFT_SLO_MS:-4000}"
ENABLE_TTFT_SLO_STOP="${ENABLE_TTFT_SLO_STOP:-1}"
# 吞吐饱和判定(沿用仓库默认)。
TPS_MIN_GAIN_PCT="${TPS_MIN_GAIN_PCT:-2.0}"
PLATEAU_PATIENCE="${PLATEAU_PATIENCE:-2}"
# Warmup 请求数上限0=不限,按并发自适应)。长上下文大并发 warmup 昂贵,可设小上限。
BENCH_WARMUP_MAX_REQUESTS="${BENCH_WARMUP_MAX_REQUESTS:-0}"
# 每并发点的请求数 = concurrency * NUM_PROMPTS_MULTIPLIER。
NUM_PROMPTS_MULTIPLIER="${NUM_PROMPTS_MULTIPLIER:-5}"
# random 数据ShareGPT 抽样)或 random-ids整数 token。长上下文建议 random-ids
# 避免 ShareGPT 前缀干扰测量BENCH_DATASET_NAME 见 config.env。
RANDOM_RANGE_RATIO="${RANDOM_RANGE_RATIO:-1.0}"
# 长度容差与重试。
INPUT_LENGTH_TOLERANCE_PCT="${INPUT_LENGTH_TOLERANCE_PCT:-5.0}"
OUTPUT_LENGTH_TOLERANCE_PCT="${OUTPUT_LENGTH_TOLERANCE_PCT:-10.0}"
MAX_POINT_RETRIES="${MAX_POINT_RETRIES:-1}"
SCENARIO_TIMEOUT_S="${SCENARIO_TIMEOUT_S:-3600}"
GPU_MEM_SAMPLE_INTERVAL_S="${GPU_MEM_SAMPLE_INTERVAL_S:-1}"
# 过滤与限制smoke 用)。
TP_LIST="${TP_LIST:-}"
ISL_LIST="${ISL_LIST:-}"
OSL_LIST="${OSL_LIST:-}"
GRID_LIMIT="${GRID_LIMIT:-0}"
DRY_RUN="${DRY_RUN:-0}"

View File

@ -30,5 +30,30 @@ DOCKER_CLIENT_IMAGE="${DOCKER_CLIENT_IMAGE:-lmsysorg/sglang:kimi-k3}"
P_DEPLOY_PROFILE="${P_DEPLOY_PROFILE:-pro6000/kimi3_pro6000_pd_prefill}"
D_DEPLOY_PROFILE="${D_DEPLOY_PROFILE:-pro6000/kimi3_pro6000_pd_decode}"
# ---- Bench client 配置(供 run_adaptive_concurrency_pd.sh 使用)----
# 长上下文用 random-ids整数 token避免 ShareGPT 前缀干扰测量。
BENCH_DATASET_NAME="${BENCH_DATASET_NAME:-random-ids}"
DATASET_PATH="${DATASET_PATH:-}"
SGLANG_BENCH_MODULE="${SGLANG_BENCH_MODULE:-sglang.bench_serving}"
GPU_MEM_SAMPLE_INTERVAL_S="${GPU_MEM_SAMPLE_INTERVAL_S:-1}"
# Per-scenario timeout to avoid hangs (seconds).
SCENARIO_TIMEOUT_S="${SCENARIO_TIMEOUT_S:-3600}"
SCENARIO_TIMEOUT_S="${SCENARIO_TIMEOUT_S:-3600}"
# ---------------------------------------------------------------------------
# Tiered TTFT SLO by input sequence length (方案 A).
# 长上下文场景 SLO 随 ISL 递增prefill 有固有计算成本TTFT 必须随输入放宽。
# Kimi-K3 混合线性注意力在 RTX 6000D 上 prefill 较慢,阈值比 glm52 略宽。
# 对应三个 shape: 1k->4s, 16k->15s, 64k->30s。
# 由 adaptive_bench_lib.sh 的并发扫描调用ttft_p95 超阈值即停止该 shape。
# ---------------------------------------------------------------------------
get_ttft_slo_ms() {
local isl="$1"
if (( isl <= 2048 )); then echo 4000
elif (( isl <= 8192 )); then echo 8000
elif (( isl <= 32768 )); then echo 15000
elif (( isl <= 131072 )); then echo 30000
else echo 50000
fi
}
TTFT_SLO_TIERS_DESC="≤2k:4s, ≤8k:8s, ≤32k:15s, ≤128k:30s, >128k:50s"

View File

@ -0,0 +1,9 @@
{
"comment": "PD 分离MoonCake RDMA长上下文 bench并发 +16 递增搜索直到 TTFT SLO 或 OOM。ISL=64k/16k/1k。",
"mode": "Y",
"matrix": {
"65536": { "128": "Y" },
"16384": { "1024": "Y" },
"1024": { "4096": "Y" }
}
}

View File

@ -0,0 +1,133 @@
#!/usr/bin/env bash
# PD 分离MoonCake RDMA长上下文 adaptive concurrency 搜索。
# 并发 +16 递增SEARCH_ADDEND=16上限 64TTFT P95 超 SLO 或 OOM 即停止。
# PD 服务常驻deploy_pd.sh 管理),本脚本只发请求,不启停服务器。
#
# 用法:
# DRY_RUN=1 bash run_adaptive_concurrency_pd.sh # 预览 shape + 并发序列
# bash run_adaptive_concurrency_pd.sh # 正式跑
# ISL_LIST="1024" OSL_LIST="4096" bash ... # 只跑某 shape
#
# 产物: adaptive_results/<run_id>/ (points/shapes/summary JSONL + CSV),与单组一致。
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"
# shellcheck source=/dev/null
source "${SCRIPT_DIR}/adaptive_config.env"
# shellcheck source=/dev/null
source "${SCRIPT_DIR}/../../../scripts/common/adaptive_bench_lib.sh"
# shellcheck source=/dev/null
source "${SCRIPT_DIR}/../../../scripts/common/deploy_cli.sh"
# PD 服务由 deploy_pd.sh 常驻管理,本脚本不指定 TP/DP固定 TP32×EP32
TP="${TP:-32}"
DP="${DP:-1}"
ENGINE="sglang"
ENGINE_PORT="$SGLANG_PORT" # router 入口 31000
RESULT_BASE="${RESULT_BASE:-${SCRIPT_DIR}/adaptive_results}"
ACTIVE_ENGINE_SERVER_LOG=""
DOCKER_IMAGE="${DOCKER_IMAGE:-${DOCKER_CLIENT_IMAGE:-lmsysorg/sglang:kimi-k3}}"
if [[ -x "${VENV_CLIENT}/bin/python" ]]; then
PYTHON="${VENV_CLIENT}/bin/python"
else
PYTHON="$(command -v python3)"
fi
# ---------------------------------------------------------------------------
# PD 常驻服务server 生命周期函数全部 no-op只探活不启停
# adaptive_bench_lib.sh 的 engine_* 调用会被安全绕过,搜索/产物逻辑保留。
# ---------------------------------------------------------------------------
engine_is_healthy() {
curl --fail --silent --show-error --max-time 5 \
"http://127.0.0.1:${ENGINE_PORT}/health" >/dev/null 2>&1
}
engine_start_server() { return 0; }
engine_stop_server() { return 0; }
engine_build_server_args() { echo "PD server managed by deploy_pd.sh"; }
engine_restart_server() { return 0; }
engine_detect_oom() {
local detail_log="$1"
local pattern='CUDA out of memory|torch\.OutOfMemoryError|OutOfMemory|out of memory|OOM|RESOURCE_EXHAUSTED|Failed to allocate memory|error: [0-9]+: ERR_RESOURCE_STATE'
grep -Eiq "$pattern" "$detail_log" 2>/dev/null
}
engine_run_bench() {
local isl="$1"
local osl="$2"
local concurrency="$3"
local num_prompts="$4"
local output_file="$5"
local warmup_requests
warmup_requests="$(adaptive_warmup_request_count "$concurrency")"
local -a bench_args=(
--backend sglang
--host 127.0.0.1
--port "$ENGINE_PORT"
--tokenizer "$MODEL_PATH"
--dataset-name "$BENCH_DATASET_NAME"
--random-input-len "$isl"
--random-output-len "$osl"
--random-range-ratio "$RANDOM_RANGE_RATIO"
--num-prompts "$num_prompts"
--max-concurrency "$concurrency"
--request-rate 10000
--warmup-requests "$warmup_requests"
--output-file "$output_file"
--output-details
--disable-tqdm
--flush-cache
)
if [[ "$BENCH_DATASET_NAME" == "random" ]]; then
bench_args+=(--dataset-path "$DATASET_PATH")
elif [[ "$BENCH_DATASET_NAME" == "random-ids" ]]; then
: # random-ids 不需要 --tokenize-prompt
else
bench_args+=(--tokenize-prompt)
fi
if [[ "$USE_DOCKER_CLIENT" == "1" ]]; then
local -a volume_args=(-v "${MODEL_PATH}:${MODEL_PATH}:ro" -v "${RESULT_BASE}:${RESULT_BASE}")
if [[ "$BENCH_DATASET_NAME" == "random" ]]; then
volume_args+=(-v "${DATASET_PATH}:${DATASET_PATH}:ro")
fi
docker run --rm \
--network host \
"${volume_args[@]}" \
-e PYTHONUNBUFFERED=1 \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
-e HF_DATASETS_OFFLINE=1 \
--entrypoint python3 \
"$DOCKER_IMAGE" \
-m "$SGLANG_BENCH_MODULE" "${bench_args[@]}"
else
"$PYTHON" -m "$SGLANG_BENCH_MODULE" "${bench_args[@]}"
fi
}
export -f engine_run_bench
export ENGINE_PORT MODEL_PATH RESULT_BASE DOCKER_IMAGE USE_DOCKER_CLIENT
export BENCH_DATASET_NAME DATASET_PATH RANDOM_RANGE_RATIO BENCH_WARMUP_MAX_REQUESTS PYTHON SGLANG_BENCH_MODULE
PARALLEL_CONFIGS=("${TP} ${DP}")
export TP DP ENGINE PARALLEL_CONFIGS
# PD 长上下文C=16 起 +16 递增,上限 64超 SLO 回退 C=8 -> 1。
export SEARCH_START_CONCURRENCY=16
export SEARCH_ADDEND=16
export SEARCH_INITIAL_BACKOFF_CONCURRENCIES="8 1"
# 长上下文不用整组跳过64k 单并发 TTFT 可能就较高)。
export TTFT_GROUP_SKIP_MS="${TTFT_GROUP_SKIP_MS:-0}"
adaptive_main "$@"