[Excutor] Experiment Feature-Support Prefill in cudagraph#3459
[Excutor] Experiment Feature-Support Prefill in cudagraph#3459Jiang-Jia-Jun merged 27 commits intoPaddlePaddle:developfrom
Conversation
|
Thanks for your contribution! |
fastdeploy/model_executor/layers/attention/append_attn_backend.py
Outdated
Show resolved
Hide resolved
fastdeploy/model_executor/layers/attention/append_attn_backend.py
Outdated
Show resolved
Hide resolved
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #3459 +/- ##
==========================================
Coverage ? 49.16%
==========================================
Files ? 9
Lines ? 120
Branches ? 8
==========================================
Hits ? 59
Misses ? 57
Partials ? 4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| for num_tokens in sorted(capture_sizes, reverse=True): | ||
| self._dummy_run( | ||
| num_tokens=num_tokens, | ||
| batch_size=self.parallel_config.max_num_seqs, |
There was a problem hiding this comment.
batch size 的入参可以删掉了,prefill、decode、spec decode 统一把 token_num 映射为 cpature_size
There was a problem hiding this comment.
考虑到SOT也要使用dummy_run,并且对batch数有要求,删除batch字段应该不可行
| self.proposer.insert_prefill_inputs(req_dicts, num_running_requests) | ||
|
|
||
| def _dummy_prefill_inputs(self, num_tokens: int, batch_size: int, expected_decode_len: int): | ||
| def _dummy_prefill_inputs( |
There was a problem hiding this comment.
num_tokens 在 prefill 和decode 阶段含义不一样,需要明确一下。
这里可以定位为 prefill 的 token 数
| input_length_list = [] | ||
| if num_tokens < batch_size: | ||
| input_length_list = [1] * num_tokens | ||
| else: | ||
| input_length_list = [1] * (batch_size - 1) | ||
| input_length_list.append(num_tokens - batch_size + 1) | ||
| return input_length_list | ||
|
|
There was a problem hiding this comment.
decode 的时候 input_length_list 应该为 self.parallel_config.max_num_batched_tokens - input_length_list
There was a problem hiding this comment.
需要跑一下deepseek的模型,确认下 mla attention 能跑prefill 捕获吗
| # A common pattern for launching CUDA kernels is to set the kernel's grids.x dimension | ||
| # using a `num_blocks` variable, and then map each thread block to a specific batch and | ||
| # data tile using `batch_ids` and `tile_ids_per_batch`. | ||
| # | ||
| # The variable names below follow this pattern, using a common prefix (e.g., `encoder_`, `decoder_`, `kv_`) | ||
| # for variables that are logically grouped together. The mapping works as follows: | ||
| # | ||
| # Usage: `my_kernel<<<grids, ...>>>(..., batch_ids, tile_ids, ...)` | ||
| # `grids.x` = `num_blocks_cpu` | ||
| # `batch_id` = `batch_ids[blockIdx.x]` | ||
| # `tile_id` = `tile_ids[blockIdx.x]` |




目前支持Prefill-Only的batch进cudagraph。在确定graph可以共用之前,只能选择要么capture decode-only的,要么capture prefill-only。
1.如何开启
想要开启,需要使用以下参数启动,重点是use_cudagraph和cudagraph_only_prefill都设为True
2.如何多prefill进图
在当前动态插入的背景下,假设发送4个80 tokens的prompt,那么seq_lens_this_time第一轮是[80],第二轮是[1, 80, 80, 80],很明显只有第一轮是纯P,可以进cudagraph,第二轮就是MIX了,进不了cudagraph,可以通过修改fastdeploy/engine/engine.py中的函数_insert_task_to_worker中
改为
这样就是不开启动态插入的逻辑,需要等待8个prompt来(数字可更改),这8个prompt才会一起进入prefill(多个prompt纯P加速),一起进入decode。task不为0时,不更改超过0.75s,那么没满也会调度,方便调试。时间可以改短一些,不然起服务会要很久。
存在的BUG1:当按照上述方法将调度策略改为非动态插入时,如果同时开启了chunked preffill,那么由于fastdeploy/engine/engine.py中的变量self.cfg.max_num_partial_prefills默认为1,导致列表self.partial_chunked_tokens长度为2,使得在函数update_requests_chunk_size中对self.partial_chunked_tokens的访问容易越界(把上面的数字8改成任意大于1的数字时就一定越界,chunk_request_num可以说就是更改后的数字,也就是一轮规划中的request数目)。
解决方法:应该让动态插入也能做到第一轮塞满,但长久来看让调度器倾向于调度纯P会跟有利于cudagraph加速纯P但是这样影响解码速度。而开chunked prefill的场景(大prompt,大于8k)和cudagraph加速prefill(小prompt,小于1k)没有重叠,所以没有必要做兼容,但是为了稳定性考量,应该访问之前做越界检查。
存在的BUG2:对于算子get_block_shape_and_split_kv_block中的函数split_q_block。
当传入seq_lens_q为[160,0]时,计算出来的num_blocks_x为dive_up(160×5,64)+dive_up(0×5,64)=13+0=13

当传入seq_lens_q为[80,80]时,计算出来的num_blocks_x为dive_up(80×5,64)+dive_up(80×5,64)=7+7=14
dummy_run capture时,是第一种情况,160的token_num,对应的cuda graph的num_blocks_x为13。
当发送两个长为80的prompt时,是第二种情况,这个160的token_num,对应的cuda graph的num_blocks_x应该为14,但是在使用num_blocks_x为13的cuda graph在推理,这会导致最后一个请求结果乱码。经过验证,14的图可以推理13的。
问题的本质是一组请求中每个请求的token数不为1时,那么这组请求和另外一组有同样的总token_num的请求对应的encoder_num_blocks_x_cpu可能不一样,导致对应的graph不一样,具体的,指multi_query_append_attention_kernel的griddim.x
同时其他变量可能也有这种情况。MTP场景可能也有这种问题。解决方法依然是按最大的起,再加上提前退出可以解决。问题抽象为N(max_num_seq)个正整数,它们的和是M,现对每个正整数Ni的diveup(Ni × a,b)求和,其最大值就是应该起的num_blocks_x,M,N,a和b应该是确定了的。解法很简单,每个seq都正好token数为1,最后一个seq包揽剩下的所有token,由此去构造纯P时的dummy_run即可。
目前已在_dummy_prefill_inputs中解决,但是launch kernel会多launch,目测只能通过传递参数进去让其提前退出,虽然目前并不影响。
3.修改图的大小
在fastdeploy/config.py的init_with_cudagrpah_size中,512为capture prefill时最大capture size,可以手动更改。