deepinfer-decode
deepinfer/csrc/engine/scheduler.hpp · a679bb8 · L556–659 · 固定 commit 上游
这是固定版本源码的只读摘录;可在线阅读,也可通过上游链接查看原文件。
556 /// decode(completion)调度:先把 prompted 队列中的 seq 移入 running,
557 /// 再按 LCFS 挑选本轮 decode 的 seq,block 不足时抢占 output 最短的 seq。
558 /// @note 需要在持有 BlockManagerMutexAcquire 的情况下调用。
559 std::unique_ptr<SchedulerOutputs> _schedule_decode(BlockManagerMutexAcquire &acquire) {
560 (void)acquire; // 仅作为持锁证明,decode 内部直接使用 *_nolock 接口
561
562 std::vector<SequencePtr> finished_seqs = free_finished_seqs();
563
564 // waiting 里已终结的 seq(abort / VE 失败入队)只有调度器能弹出,原本由
565 // _schedule_prefill 顺手清理。现在 prefill 只在本 rank want=true 时才走到,
566 // 某 rank 的 waiting 只剩这类 seq 时 want 恒为 false,它们会永远滞留:
567 // has_unfinished 恒真拖着全组空转,ALL 投票也被这个 rank 永久压死。
568 // 所以在 decode 分支兜底清理;abort 可命中队列中间,需要扫全队列。
569 std::vector<SequencePtr> ignored_seqs;
570 for (auto it = waiting.begin(); it != waiting.end();) {
571 if ((*it)->is_finished()) {
572 ignored_seqs.push_back(std::move(*it));
573 it = waiting.erase(it);
574 } else {
575 ++it;
576 }
577 }
578
579 const size_t max_seqs = max_num_seqs;
580
581 // prompted 队列头部的 seq 依次转入 running(或已结束的转入 finished),
582 // forked seq 需要先克隆 state 和最后一个(可能写了一半的)block。
583 auto admit = [&](SequencePtr &&seq) {
584 if (seq->schedule.status == SequenceStatus::WAITING) {
585 seq->schedule.status = SequenceStatus::RUNNING;
586 allocate_mtp_info_slot(*seq);
587 running.push_back(std::move(seq));
588 } else {
589 TORCH_CHECK(seq->is_finished(), "prompted 队列中的 seq 状态为 ", int(seq->schedule.status), " ,不应该");
590 finished_seqs.push_back(std::move(seq));
591 }
592 prompted.pop_front();
593 };
594
595 while (!prompted.empty() && running.size() < max_seqs) {
596 auto &&[seq, forked] = prompted.front();
597 if (seq->schedule.status == SequenceStatus::FINISHED_ABORTED) { // 进 CE 之前就 abort 了
598 finished_seqs.push_back(std::move(seq));
599 prompted.pop_front();
600 continue;
601 }
602 if (forked) {
603 if ((state_manager && !state_manager->can_allocate_single_nolock()) || !block_manager->can_allocate_single_nolock()) {
604 break;
605 }
606 _clone_state(*seq);
607 int new_block = block_manager->allocate_single_nolock();
608 int num_blocks = (seq->cache.num_tokens + seq->cache.block_size - 1) / seq->cache.block_size;
609 TORCH_CHECK_LE(num_blocks, static_cast<int>(seq->cache.block_table.size()));
610 clone_blocks.emplace_back(seq->cache.block_table[num_blocks - 1]);
611 clone_blocks.emplace_back(new_block);
612 for (size_t i = num_blocks; i < seq->cache.block_table.size(); ++i) {
613 clone_blocks.emplace_back(seq->cache.block_table[i]);
614 clone_blocks.emplace_back(0);
615 }
616 seq->cache.block_table[num_blocks - 1] = new_block;
617 seq->cache.block_table.erase(seq->cache.block_table.begin() + num_blocks, seq->cache.block_table.end());
618 }
619 admit(std::move(seq));
620 }
621
622 // LCFS
623 std::vector<SequencePtr> scheduled;
624 std::sort(running.begin(), running.end(),
625 [](const SequencePtr &a, const SequencePtr &b) { return a->get_output_len() > b->get_output_len(); });
626 scheduled.reserve(max_seqs);
627 size_t l = 0, r = running.size();
628 while (l < r && scheduled.size() < max_seqs) {
629 auto &seq = running[r - 1];
630 while (!block_manager->can_allocate_single_nolock() && r - l > 1) {
631 auto victim_seq = running[l];
632 _preempt(victim_seq, ignored_seqs);
633 l += 1;
634 }
635 if (block_manager->can_allocate_single_nolock()) {
636 _append_slot(*seq);
637 scheduled.push_back(std::move(seq));
638 } else {
639 _preempt(seq, ignored_seqs);
640 }
641
642 r -= 1;
643 }
644
645 TORCH_CHECK(l == r, "decode 时 running 有排队,已经调度 ", scheduled.size(), "个,还剩 ", r - l, " 个,不应该");
646 running.clear();
647
648 std::sort(
649 scheduled.begin(), scheduled.end(), [](const SequencePtr &a, const SequencePtr &b) { return a->get_len() > b->get_len(); });
650
651 std::vector<int> seqlens_q;
652 _mtp_varlen.schedule(scheduled, seqlens_q);
653
654 int num_batched_tokens = 0;
655 for (int seqlen_q : seqlens_q) {
656 num_batched_tokens += seqlen_q;
657 }
658 return _make_outputs(
659 std::move(scheduled), false, num_batched_tokens, std::move(finished_seqs), std::move(ignored_seqs), std::move(seqlens_q), 0);