@@ -143,6 +143,41 @@ std::optional<DraftPlan> pack_draft(
143143 return plan;
144144}
145145
146+ bool pad_to_static (
147+ DraftPlan& plan,
148+ int owners,
149+ int block,
150+ std::size_t row_bytes) {
151+ const int width = owners * block;
152+ const int queries = width;
153+ if (row_bytes == 0 || plan.hidden .size () % row_bytes != 0 ) {
154+ return false ;
155+ }
156+ const int context = static_cast <int >(plan.hidden .size () / row_bytes);
157+ if (context > width ||
158+ plan.positions .size () != static_cast <std::size_t >(context + queries) ||
159+ plan.kv_order .size () != plan.positions .size ()) {
160+ return false ;
161+ }
162+ for (const int count : plan.counts ) {
163+ if (count > block) {
164+ return false ;
165+ }
166+ }
167+ const int pad = width - context;
168+ plan.hidden .resize (static_cast <std::size_t >(width) * row_bytes, 0 );
169+ plan.positions .insert (plan.positions .begin () + context, pad, 0 );
170+ for (auto & index : plan.kv_order ) {
171+ if (index >= context) {
172+ index += pad;
173+ }
174+ }
175+ for (int row = context; row < width; ++row) {
176+ plan.kv_order .push_back (row);
177+ }
178+ return true ;
179+ }
180+
146181DeviceStaging::~DeviceStaging () {
147182 if (device_) {
148183 (void )cudaFree (device_);
@@ -443,6 +478,10 @@ Result<MuseGlimmerBackend> MuseGlimmerCudaDFlashExecutor::create(
443478 }
444479 target_config.max_sessions = config.max_sessions ;
445480 target_config.max_session_tokens = config.max_session_tokens ;
481+ for (int n = 1 ; n <= df::kOwnerLimit ; ++n) {
482+ target_config.options .additional_cuda_graph_methods .push_back (
483+ df::kStaticForwardPrefix + std::to_string (n));
484+ }
446485 ET_ASSIGN_OR_RETURN (target, Target::create (target_config));
447486 auto & module = target->module ();
448487 ET_ASSIGN_OR_RETURN (names, module .method_names ());
@@ -629,6 +668,43 @@ bool MuseGlimmerCudaDFlashExecutor::initialize() {
629668 " CUDA DFlash: could not initialize distinct target/draft caches" );
630669 return false ;
631670 }
671+ const auto names = module .method_names ();
672+ if (!names.ok ()) {
673+ ET_LOG (Error, " CUDA DFlash: could not list the program's methods" );
674+ return false ;
675+ }
676+ for (int n = 1 ; n <= max_owners_; ++n) {
677+ const auto name = df::kStaticForwardPrefix + std::to_string (n);
678+ if (!names->count (name)) {
679+ continue ;
680+ }
681+ const auto meta = module .method_meta (name);
682+ const int width = n * block_;
683+ auto shaped = [&](std::size_t index, bool input, std::vector<int > sizes) {
684+ auto tensor = input ? meta->input_tensor_meta (index)
685+ : meta->output_tensor_meta (index);
686+ return tensor.ok () &&
687+ std::equal (
688+ tensor->sizes ().begin (),
689+ tensor->sizes ().end (),
690+ sizes.begin (),
691+ sizes.end ());
692+ };
693+ if (!meta.ok () || meta->num_inputs () != 4 || meta->num_outputs () != 1 ||
694+ !shaped (0 , true , {n, block_}) ||
695+ !shaped (1 , true , {1 , width, hidden_width_}) ||
696+ !shaped (2 , true , {2 * width}) || !shaped (3 , true , {2 * width}) ||
697+ !shaped (0 , false , {1 , width, target_->vocab_size ()}) ||
698+ batching::load_method_with_cache (
699+ module ,
700+ name,
701+ ::executorch::backends::cuda::kCudaBackendId ,
702+ draft_install_) != Error::Ok) {
703+ ET_LOG (Error, " CUDA DFlash: invalid static draft %s" , name.c_str ());
704+ return false ;
705+ }
706+ static_drafts_[n] = true ;
707+ }
632708 initialized_ = true ;
633709 return true ;
634710}
@@ -1022,23 +1098,39 @@ bool MuseGlimmerCudaDFlashExecutor::draft(
10221098 }
10231099 const int q = static_cast <int >(owners.size ()) * block_;
10241100 const int c = static_cast <int >(plan->hidden .size () / row_bytes_);
1025- const int k = c + q;
1101+ // Decode contexts fit one block per owner; take the static, graph-captured
1102+ // draft. Longer contexts drain through the dynamic one.
1103+ const bool use_static = proposals && static_drafts_[owners.size ()] &&
1104+ std::all_of (plan->counts .begin (), plan->counts .end (), [this ](int n) {
1105+ return n <= block_;
1106+ });
1107+ if (use_static &&
1108+ !df::pad_to_static (
1109+ *plan, static_cast <int >(owners.size ()), block_, row_bytes_)) {
1110+ return false ;
1111+ }
1112+ const int padded = static_cast <int >(plan->hidden .size () / row_bytes_);
1113+ const int k = padded + q;
10261114 // Host-owned inputs are staged by the export's normal H2D boundary. Keep
10271115 // their backing alive through the forward and its stream-fenced argmax.
10281116 auto tokens = make_tensor_ptr (
10291117 {static_cast <int >(owners.size ()), block_}, std::move (plan->tokens ));
10301118 auto hidden = make_tensor_ptr (
1031- {1 , c , hidden_width_},
1119+ {1 , padded , hidden_width_},
10321120 std::move (plan->hidden ),
10331121 aten::ScalarType::BFloat16);
10341122 auto positions = make_tensor_ptr ({k}, std::move (plan->positions ));
10351123 auto order = make_tensor_ptr ({k}, std::move (plan->kv_order ));
1036- auto result = target_->module ().execute (
1037- df::kForward , {tokens, hidden, positions, order});
1124+ const std::string method = use_static
1125+ ? df::kStaticForwardPrefix + std::to_string (owners.size ())
1126+ : std::string (df::kForward );
1127+ auto result =
1128+ target_->module ().execute (method, {tokens, hidden, positions, order});
10381129 if (!result.ok () || result->size () != 1 || !result->at (0 ).isTensor ()) {
10391130 return false ;
10401131 }
10411132 ++stats_.draft_forwards ;
1133+ stats_.static_draft_forwards += use_static;
10421134 const auto logits = result->at (0 ).toTensor ();
10431135 if (logits.dim () < 2 || logits.scalar_type () != aten::ScalarType::Float ||
10441136 !dense (logits) ||
0 commit comments