|
| 1 | +import math |
1 | 2 | import os |
2 | 3 | import random |
3 | 4 | import time |
@@ -35,6 +36,7 @@ def generate( |
35 | 36 | do_sample: bool = True, |
36 | 37 | num_beams: int = 1, |
37 | 38 | use_cache: bool = False, |
| 39 | + prefill_chunk_size: int = 0, |
38 | 40 | eos_token_id: Optional[int] = None, |
39 | 41 | timing: str = "", |
40 | 42 | post_iteration_hook: Optional[ |
@@ -117,6 +119,10 @@ def generate( |
117 | 119 | max_possible_context_length = input_ids.size(1) + max_new_tokens |
118 | 120 |
|
119 | 121 | BLOCK_SIZE = 64 |
| 122 | + if prefill_chunk_size > 0: |
| 123 | + assert prefill_chunk_size % BLOCK_SIZE == 0, ( |
| 124 | + "Chunk size must be a multiple of the page size" |
| 125 | + ) |
120 | 126 |
|
121 | 127 | # these variables are guaranteed to be set in another location (inference.py, test_decoders.py, etc.) |
122 | 128 | # if we set these variables here, we run the risk of warming up and generating with different sizes |
@@ -229,7 +235,6 @@ def generate( |
229 | 235 | for pos_i in range(input_ids.shape[1] - seq_tkv, input_ids.shape[1]): |
230 | 236 | # we may have already popped a block, so index to the proper block |
231 | 237 | block_number = block_table_i[pos_i // BLOCK_SIZE] |
232 | | - |
233 | 238 | block_offset = pos_i % BLOCK_SIZE |
234 | 239 | slot = block_number * BLOCK_SIZE + block_offset |
235 | 240 | slot_mapping_i.append(slot) |
@@ -264,57 +269,149 @@ def generate( |
264 | 269 | # remove extra pads from the input_ids, slot_mapping, position_ids, mask to account for empty pages |
265 | 270 | # each input should be padded to its smallest multiple of BLOCK_SIZE (64) |
266 | 271 | # we need to clone these tensors to ensure the pointer offset is 0 |
267 | | - input_ids_i = input_ids[seq_i][-current_tkv:].unsqueeze(0).clone() |
268 | | - slot_mapping_i = ( |
| 272 | + input_ids_seq = input_ids[seq_i][-current_tkv:].unsqueeze(0).clone() |
| 273 | + slot_mapping_seq = ( |
269 | 274 | torch.tensor(slot_mapping[seq_i][-current_tkv:], dtype=torch.int64) |
270 | 275 | .unsqueeze(0) |
271 | 276 | .clone() |
272 | 277 | ) |
273 | | - position_ids_i = ( |
| 278 | + position_ids_seq = ( |
274 | 279 | kwargs["position_ids"][seq_i][-current_tkv:].unsqueeze(0).clone() |
275 | 280 | ) |
276 | 281 |
|
277 | 282 | # This view will result in a discontiguous tensor (creates a new graph during compile) |
278 | 283 | # For this reason, we must explicitly make contiguous |
279 | | - mask_i = ( |
| 284 | + mask_seq = ( |
280 | 285 | kwargs["mask"][seq_i][:, -current_tkv:, -current_tkv:] |
281 | 286 | .unsqueeze(0) |
282 | 287 | .contiguous() |
283 | 288 | ) |
284 | 289 |
|
285 | | - # batch dynamic |
286 | | - torch._dynamo.mark_static(input_ids_i, 0) |
287 | | - torch._dynamo.mark_static(slot_mapping_i, 0) |
288 | | - torch._dynamo.mark_static(position_ids_i, 0) |
289 | | - torch._dynamo.mark_static(mask_i, 0) |
290 | | - |
291 | | - # seq dynamic |
292 | | - torch._dynamo.mark_dynamic(input_ids_i, 1) |
293 | | - torch._dynamo.mark_dynamic(slot_mapping_i, 1) |
294 | | - torch._dynamo.mark_dynamic(position_ids_i, 1) |
295 | | - torch._dynamo.mark_dynamic(mask_i, 2) |
296 | | - torch._dynamo.mark_dynamic(mask_i, 3) |
297 | | - |
298 | 290 | # FP8 per-sentence scale handling |
299 | 291 | if "fp8" in kwargs["attn_name"]: |
300 | 292 | for layer_idx, (t1, t2) in enumerate(current_kv_cache): |
301 | 293 | t1._scale = current_kv_scales[layer_idx][0][seq_i].reshape(-1) |
302 | 294 | t2._scale = current_kv_scales[layer_idx][1][seq_i].reshape(-1) |
303 | 295 |
|
304 | 296 | last_n_tokens = kwargs.get("last_n_tokens", 0) |
305 | | - output, current_kv_cache = model( |
306 | | - input_ids_i, |
307 | | - slot_mapping=slot_mapping_i, |
308 | | - position_ids=position_ids_i, |
309 | | - mask=mask_i, |
310 | | - past_key_value_states=current_kv_cache, |
311 | | - use_cache=kwargs["use_cache"], |
312 | | - last_n_tokens=last_n_tokens, |
313 | | - attn_name=kwargs["attn_name"], |
314 | | - ) |
315 | 297 |
|
316 | | - # only last token must be handled here to properly stack the tensors |
317 | | - output = output[:, -1, :] |
| 298 | + if prefill_chunk_size > 0: |
| 299 | + left_padded_prompt_mask_seq_chunk = None |
| 300 | + # Chunked prefill |
| 301 | + for chunk_j in range(math.ceil(current_tkv / prefill_chunk_size)): |
| 302 | + chunk_start = -current_tkv + chunk_j * prefill_chunk_size |
| 303 | + chunk_end = -current_tkv + min( |
| 304 | + (chunk_j + 1) * prefill_chunk_size, current_tkv |
| 305 | + ) |
| 306 | + |
| 307 | + ids_length = input_ids[seq_i].shape[0] |
| 308 | + input_ids_seq_chunk = ( |
| 309 | + input_ids[seq_i][ |
| 310 | + chunk_start + ids_length : chunk_end + ids_length |
| 311 | + ] |
| 312 | + .unsqueeze(0) |
| 313 | + .clone() |
| 314 | + ) |
| 315 | + slots_length = len(slot_mapping[seq_i]) |
| 316 | + slot_mapping_seq_chunk = ( |
| 317 | + torch.tensor( |
| 318 | + slot_mapping[seq_i][ |
| 319 | + chunk_start + slots_length : chunk_end |
| 320 | + + slots_length |
| 321 | + ], |
| 322 | + dtype=torch.int64, |
| 323 | + ) |
| 324 | + .unsqueeze(0) |
| 325 | + .clone() |
| 326 | + ) |
| 327 | + pids_length = kwargs["position_ids"][seq_i].shape[0] |
| 328 | + position_ids_seq_chunk = ( |
| 329 | + kwargs["position_ids"][seq_i][ |
| 330 | + chunk_start + pids_length : chunk_end + pids_length |
| 331 | + ] |
| 332 | + .unsqueeze(0) |
| 333 | + .clone() |
| 334 | + ) |
| 335 | + |
| 336 | + # This view will result in a discontiguous tensor (creates a new graph during compile) |
| 337 | + # For this reason, we must explicitly make contiguous |
| 338 | + if left_padded_prompt_mask_seq_chunk is None: |
| 339 | + left_padded_prompt_mask_seq_chunk = ( |
| 340 | + position_ids_seq_chunk == 0 |
| 341 | + ).sum(dim=1) - 1 |
| 342 | + current_tkv_mask_seq_chunk = torch.min( |
| 343 | + torch.tensor( |
| 344 | + (chunk_j + 1) * prefill_chunk_size, dtype=torch.int64 |
| 345 | + ), |
| 346 | + current_tkv, |
| 347 | + ).unsqueeze(0) |
| 348 | + |
| 349 | + table_length = len(block_table[seq_i]) |
| 350 | + block_start = -current_tkv // BLOCK_SIZE + table_length |
| 351 | + block_end = chunk_end // BLOCK_SIZE + table_length |
| 352 | + block_table_seq_chunk = torch.tensor( |
| 353 | + block_table[seq_i][block_start:block_end], dtype=torch.int64 |
| 354 | + ).unsqueeze(0) |
| 355 | + |
| 356 | + chunked_kwargs = { |
| 357 | + "slot_mapping": slot_mapping_seq_chunk, |
| 358 | + "position_ids": position_ids_seq_chunk, |
| 359 | + "past_key_value_states": current_kv_cache, |
| 360 | + "use_cache": kwargs["use_cache"], |
| 361 | + "last_n_tokens": kwargs["last_n_tokens"], |
| 362 | + "attn_name": kwargs["attn_name"], |
| 363 | + "left_padded_prompt_mask": left_padded_prompt_mask_seq_chunk, |
| 364 | + "current_tkv_mask": current_tkv_mask_seq_chunk, |
| 365 | + "block_table": block_table_seq_chunk, |
| 366 | + } |
| 367 | + |
| 368 | + # batch static |
| 369 | + torch._dynamo.mark_static(input_ids_seq_chunk, 0) |
| 370 | + torch._dynamo.mark_static(slot_mapping_seq_chunk, 0) |
| 371 | + torch._dynamo.mark_static(position_ids_seq_chunk, 0) |
| 372 | + torch._dynamo.mark_static(block_table_seq_chunk, 0) |
| 373 | + |
| 374 | + # seq dynamic |
| 375 | + torch._dynamo.mark_dynamic(input_ids_seq_chunk, 1) |
| 376 | + torch._dynamo.mark_dynamic(slot_mapping_seq_chunk, 1) |
| 377 | + torch._dynamo.mark_dynamic(position_ids_seq_chunk, 1) |
| 378 | + torch._dynamo.mark_dynamic(block_table_seq_chunk, 1) |
| 379 | + |
| 380 | + logits, current_kv_cache = model( |
| 381 | + input_ids_seq_chunk, **chunked_kwargs |
| 382 | + ) |
| 383 | + |
| 384 | + # only last token must be handled here to properly stack the tensors |
| 385 | + logits = logits[:, -1, :] |
| 386 | + |
| 387 | + output = (logits, current_kv_cache) |
| 388 | + |
| 389 | + else: |
| 390 | + # batch static |
| 391 | + torch._dynamo.mark_static(input_ids_seq, 0) |
| 392 | + torch._dynamo.mark_static(slot_mapping_seq, 0) |
| 393 | + torch._dynamo.mark_static(position_ids_seq, 0) |
| 394 | + torch._dynamo.mark_static(mask_seq, 0) |
| 395 | + |
| 396 | + # seq dynamic |
| 397 | + torch._dynamo.mark_dynamic(input_ids_seq, 1) |
| 398 | + torch._dynamo.mark_dynamic(slot_mapping_seq, 1) |
| 399 | + torch._dynamo.mark_dynamic(position_ids_seq, 1) |
| 400 | + torch._dynamo.mark_dynamic(mask_seq, 2) |
| 401 | + torch._dynamo.mark_dynamic(mask_seq, 3) |
| 402 | + output, current_kv_cache = model( |
| 403 | + input_ids_seq, |
| 404 | + slot_mapping=slot_mapping_seq, |
| 405 | + position_ids=position_ids_seq, |
| 406 | + mask=mask_seq, |
| 407 | + past_key_value_states=current_kv_cache, |
| 408 | + use_cache=kwargs["use_cache"], |
| 409 | + last_n_tokens=last_n_tokens, |
| 410 | + attn_name=kwargs["attn_name"], |
| 411 | + ) |
| 412 | + |
| 413 | + # only last token must be handled here to properly stack the tensors |
| 414 | + output = output[:, -1, :] |
318 | 415 |
|
319 | 416 | # TODO: Figure out how to do this cleanly |
320 | 417 | if "fp8" in kwargs["attn_name"]: |
|
0 commit comments