main
sh 243 lines 8.09 KB
Raw
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../../.." && pwd)"
5
6 model="ibm-research/PowerMoE-3b"
7 port=8000
8 num_decode_tokens=128
9 server_pid=
10 server_log=
11
12 # XPU MoE kernels may not safely handle the -1 routes emitted for padding.
13 export VLLM_MOE_SKIP_PADDING=0
14
15 cd "$repo_root"
16
17 cleanup_server() {
18 local attempt
19
20 if [[ -n "$server_pid" ]] && kill -0 "$server_pid" 2>/dev/null; then
21 kill "$server_pid" 2>/dev/null || true
22
23 for ((attempt = 0; attempt < 30; attempt++)); do
24 if ! kill -0 "$server_pid" 2>/dev/null; then
25 break
26 fi
27 sleep 1
28 done
29
30 if kill -0 "$server_pid" 2>/dev/null; then
31 echo "vLLM did not stop after 30 seconds; sending SIGKILL" >&2
32 kill -KILL "$server_pid" 2>/dev/null || true
33 fi
34
35 wait "$server_pid" 2>/dev/null || true
36 fi
37
38 server_pid=
39 }
40
41 on_exit() {
42 local status=$?
43
44 cleanup_server
45 if [[ "$status" -ne 0 && -n "$server_log" && -f "$server_log" ]]; then
46 if command -v buildkite-agent >/dev/null 2>&1; then
47 buildkite-agent artifact upload "$server_log" ||
48 echo "Failed to upload $server_log as a Buildkite artifact" >&2
49 fi
50 fi
51 [[ -z "$server_log" ]] || rm -f "$server_log"
52 exit "$status"
53 }
54 trap on_exit EXIT
55
56 wait_for_server() {
57 for _ in $(seq 1 600); do
58 if curl -sf "http://127.0.0.1:$port/health" >/dev/null; then
59 return
60 fi
61 if ! kill -0 "$server_pid" 2>/dev/null; then
62 echo "vLLM exited before becoming healthy; see the server log above." >&2
63 return 1
64 fi
65 sleep 1
66 done
67 echo "Timed out waiting for vLLM server health check" >&2
68 return 1
69 }
70
71 request_outputs() {
72 local prompts_json=$1
73 local expected_choices=$2
74 local response
75
76 response=$(printf \
77 '{"model":"%s","prompt":%s,"temperature":0.7,"top_p":0.95,"seed":0,"max_tokens":%s,"logprobs":64,"ignore_eos":true,"return_token_ids":true}' \
78 "$model" "$prompts_json" "$num_decode_tokens" |
79 curl --fail --silent --show-error \
80 --max-time 1200 \
81 "http://127.0.0.1:$port/v1/completions" \
82 -H "Content-Type: application/json" \
83 --data-binary @-)
84
85 local -a token_id_arrays logprob_arrays
86 mapfile -t token_id_arrays < <(
87 grep -o '"token_ids":\[[^]]*\]' <<<"$response" |
88 sed 's/^"token_ids":\[//; s/\]$//'
89 )
90 mapfile -t logprob_arrays < <(
91 grep -o '"token_logprobs":\[[^]]*\]' <<<"$response" |
92 sed 's/^"token_logprobs":\[//; s/\]$//'
93 )
94 if [[ "${#token_id_arrays[@]}" -ne "$expected_choices" ]] ||
95 [[ "${#logprob_arrays[@]}" -ne "$expected_choices" ]]; then
96 echo "Expected $expected_choices choices with token IDs and logits" >&2
97 return 1
98 fi
99 local index
100 local array
101 local value
102 local -a values
103 for index in "${!logprob_arrays[@]}"; do
104 array="${logprob_arrays[index]}"
105 IFS=, read -r -a values <<<"$array"
106 if [[ "${#values[@]}" -ne "$num_decode_tokens" ]]; then
107 echo "Completion returned an unexpected number of decode steps" >&2
108 return 1
109 fi
110 IFS=, read -r -a values <<<"${token_id_arrays[index]}"
111 if [[ "${#values[@]}" -ne "$num_decode_tokens" ]]; then
112 echo "Completion returned an unexpected number of generated tokens" >&2
113 return 1
114 fi
115 for value in "${values[@]}"; do
116 if [[ ! "$value" =~ ^[0-9]+$ ]]; then
117 echo "Completion returned an invalid token ID" >&2
118 return 1
119 fi
120 done
121 IFS=, read -r -a values <<<"$array"
122 for value in "${values[@]}"; do
123 if [[ ! "$value" =~ ^-?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?$ ]]; then
124 echo "Completion returned non-finite raw logits" >&2
125 return 1
126 fi
127 done
128 printf '%s|%s\n' "${token_id_arrays[index]}" "${logprob_arrays[index]}"
129 done
130 }
131
132 make_prompt() {
133 local length=$1
134 local offset=${2:-0}
135 local i
136 local prompt="Use the following system-design context to answer the question. Scenario $offset:"
137 for ((i = 0; i < length; i++)); do
138 prompt+=" context"
139 done
140 prompt+="\\n\\nQuestion: How should batch-invariant inference behave when this same request is decoded alongside unrelated requests?\\nAnswer:"
141 printf '"%s"' "$prompt"
142 }
143
144 first_difference() {
145 local actual=$1
146 local expected=$2
147 local position=$3
148 local actual_tokens=${actual%%|*}
149 local expected_tokens=${expected%%|*}
150 local actual_logits=${actual#*|}
151 local expected_logits=${expected#*|}
152 local -a actual_token_values
153 local -a expected_token_values
154 local -a actual_logit_values
155 local -a expected_logit_values
156 local step
157
158 IFS=, read -r -a actual_token_values <<<"$actual_tokens"
159 IFS=, read -r -a expected_token_values <<<"$expected_tokens"
160 IFS=, read -r -a actual_logit_values <<<"$actual_logits"
161 IFS=, read -r -a expected_logit_values <<<"$expected_logits"
162 for ((step = 0; step < num_decode_tokens; step++)); do
163 if [[ "${actual_token_values[step]}" != "${expected_token_values[step]}" ]]; then
164 printf 'batch position %s: step=%s, baseline token=%s, actual token=%s' \
165 "$position" \
166 "$step" \
167 "${expected_token_values[step]}" \
168 "${actual_token_values[step]}"
169 return
170 fi
171 if [[ "${actual_logit_values[step]}" != "${expected_logit_values[step]}" ]]; then
172 printf 'batch position %s: step=%s, token=%s, baseline logit=%s, actual logit=%s' \
173 "$position" \
174 "$step" \
175 "${expected_token_values[step]}" \
176 "${expected_logit_values[step]}" \
177 "${actual_logit_values[step]}"
178 return
179 fi
180 done
181 }
182
183 run_e2e() {
184 local needle
185 local batch
186 local baseline_output
187 local batch_response
188 local difference
189 local position
190 local -a fillers
191 local -a batch_outputs
192 local -a differences=()
193
194 echo "Testing PowerMoE-3b: TP=2, VLLM_BATCH_INVARIANT=1, VLLM_MOE_SKIP_PADDING=0"
195 server_log=$(mktemp "${TMPDIR:-/tmp}/xpu-batch-invariance.XXXXXX.log")
196 VLLM_BATCH_INVARIANT=1 vllm serve "$model" \
197 --tensor-parallel-size 2 \
198 --max-model-len 1024 \
199 --max-num-seqs 16 \
200 --max-num-batched-tokens 512 \
201 --enable-chunked-prefill \
202 --no-enable-prefix-caching \
203 --enforce-eager \
204 --seed 0 \
205 --max-logprobs 64 \
206 --logprobs-mode raw_logits \
207 --port "$port" > >(tee "$server_log") 2>&1 &
208 server_pid=$!
209 wait_for_server
210
211 needle=$(make_prompt 257)
212 local filler_lengths=(7 31 63 95 127 159 191 223 255 287 319 383 447)
213 for position in "${!filler_lengths[@]}"; do
214 fillers+=( "$(make_prompt "${filler_lengths[position]}" "$((position + 1))")" )
215 done
216 batch="[$needle,${fillers[0]},${fillers[1]},${fillers[2]},${fillers[3]},${fillers[4]},${fillers[5]},$needle,${fillers[6]},${fillers[7]},${fillers[8]},${fillers[9]},${fillers[10]},${fillers[11]},${fillers[12]},$needle]"
217
218 echo "Requesting BS=1 baseline ($num_decode_tokens decode tokens)..."
219 baseline_output=$(request_outputs "$needle" 1)
220 echo "Received BS=1 baseline."
221 echo "Requesting BS=16 batch ($((16 * num_decode_tokens)) decode tokens)..."
222 batch_response=$(request_outputs "$batch" 16)
223 mapfile -t batch_outputs <<<"$batch_response"
224 echo "Received BS=16 batch; comparing generated tokens and logits..."
225
226 for position in 0 7 15; do
227 difference=$(first_difference "${batch_outputs[position]}" "$baseline_output" "$position")
228 if [[ -n "$difference" ]]; then
229 differences+=("$difference")
230 fi
231 done
232
233 if [[ "${#differences[@]}" -ne 0 ]]; then
234 echo "Batch-invariant PowerMoE-3b output changed: ${differences[*]}" >&2
235 return 1
236 fi
237 echo "PowerMoE-3b batch invariance check passed"
238 cleanup_server
239 rm -f "$server_log"
240 server_log=
241 }
242
243 run_e2e