Skip to content

Commit aefe4cb

Browse files
Gasoonjiafacebook-github-bot
authored andcommitted
skip h2d and d2h copies methods
Summary: This diff updates gemma4-31b export and runtime pipeline to skip the h2d and d2h copies between prefill and decode, and between previous round next round of decode as well. Differential Revision: D108661628
1 parent 78e0384 commit aefe4cb

2 files changed

Lines changed: 60 additions & 4 deletions

File tree

examples/models/gemma4_31b/export.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ def _export_cuda(
171171
)
172172
from executorch.exir.backend.compile_spec_schema import CompileSpec
173173
from executorch.exir.passes import MemoryPlanningPass
174+
from executorch.exir.passes.propagate_device_pass import PropagateDeviceConfig
174175
from torch.export import Dim, export
175176

176177
inductor_config.coordinate_descent_tuning = False
@@ -270,6 +271,14 @@ def _export_cuda(
270271
alloc_graph_input=False,
271272
),
272273
emit_mutable_buffer_names=True,
274+
# Keep method inputs/outputs device-resident so the CUDA backend
275+
# does not insert boundary H2D/D2H copies: the runner stages inputs
276+
# in CUDA memory and reads the sampled token back with a single
277+
# small D2H. CUDA-only (no effect on the MLX path).
278+
propagate_device_config=PropagateDeviceConfig(
279+
skip_h2d_for_method_inputs=True,
280+
skip_d2h_for_method_outputs=True,
281+
),
273282
),
274283
)
275284

examples/models/gemma4_31b/main.cpp

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
#include <executorch/extension/llm/sampler/util.h>
2424
#include <executorch/extension/module/module.h>
2525
#include <executorch/extension/tensor/tensor.h>
26+
#include <executorch/extension/tensor/tensor_ptr.h>
2627
#include <executorch/runtime/backend/interface.h>
2728
#include <executorch/runtime/backend/options.h>
29+
#include <executorch/runtime/core/portable_type/device.h>
30+
#include <executorch/runtime/platform/assert.h>
2831
#include <executorch/runtime/platform/log.h>
2932
#include <pytorch/tokenizers/hf_tokenizer.h>
3033

@@ -82,6 +85,9 @@ using ::executorch::extension::from_blob;
8285
using ::executorch::extension::Module;
8386
using ::executorch::runtime::Error;
8487
using ::executorch::runtime::EValue;
88+
#ifdef EXECUTORCH_BUILD_CUDA
89+
using ::executorch::extension::clone_tensor_ptr_to;
90+
#endif
8591

8692
using SizesType = executorch::aten::SizesType;
8793

@@ -181,6 +187,8 @@ int main(int argc, char** argv) {
181187
FLAGS_temperature <= 0.0 ? 1e-6f : static_cast<float>(FLAGS_temperature);
182188

183189
#ifdef EXECUTORCH_BUILD_CUDA
190+
const auto cuda_device =
191+
executorch::aten::Device(executorch::aten::DeviceType::CUDA, 0);
184192
if (FLAGS_cuda_graph) {
185193
executorch::runtime::BackendOptions<2> cuda_opts;
186194
cuda_opts.set_option("enable_cuda_graph_for_method", "decode");
@@ -217,8 +225,9 @@ int main(int argc, char** argv) {
217225
ET_LOG(Error, "Failed to load decode method");
218226
return 1;
219227
}
220-
auto temp_tensor =
221-
from_blob(&temp_val, {1}, executorch::aten::ScalarType::Float);
228+
auto temp_tensor = clone_tensor_ptr_to(
229+
from_blob(&temp_val, {1}, executorch::aten::ScalarType::Float),
230+
cuda_device);
222231
#else
223232
if (FLAGS_cuda_graph) {
224233
ET_LOG(Info, "--cuda_graph ignored on non-CUDA build");
@@ -304,6 +313,12 @@ int main(int argc, char** argv) {
304313
auto pos_tensor = from_blob(
305314
pos_data.data(), {S(chunk_len)}, executorch::aten::ScalarType::Long);
306315

316+
#ifdef EXECUTORCH_BUILD_CUDA
317+
// skip_h2d: prefill/decode method inputs must already live in CUDA memory.
318+
tokens_tensor = clone_tensor_ptr_to(tokens_tensor, cuda_device);
319+
pos_tensor = clone_tensor_ptr_to(pos_tensor, cuda_device);
320+
#endif
321+
307322
std::vector<EValue> inputs;
308323
inputs.push_back(EValue(tokens_tensor));
309324
inputs.push_back(EValue(pos_tensor));
@@ -356,17 +371,49 @@ int main(int argc, char** argv) {
356371
int64_t pos = num_prompt_tokens;
357372
std::vector<int64_t> decode_token_data = {static_cast<int64_t>(cur_token)};
358373
std::vector<int64_t> decode_pos_data = {pos};
359-
auto decode_tokens = from_blob(
374+
auto decode_tokens_cpu = from_blob(
360375
decode_token_data.data(), {1, 1}, executorch::aten::ScalarType::Long);
361-
auto decode_pos = from_blob(
376+
auto decode_pos_cpu = from_blob(
362377
decode_pos_data.data(), {1}, executorch::aten::ScalarType::Long);
378+
#ifdef EXECUTORCH_BUILD_CUDA
379+
// skip_h2d: keep fixed device-resident input buffers across decode steps
380+
// (seeded here with a one-time H2D). Their 8-byte contents are refreshed
381+
// each step from the host (see loop), since the sampled id round-trips to
382+
// the host for EOS detection.
383+
auto decode_tokens = clone_tensor_ptr_to(decode_tokens_cpu, cuda_device);
384+
auto decode_pos = clone_tensor_ptr_to(decode_pos_cpu, cuda_device);
385+
#else
386+
auto decode_tokens = decode_tokens_cpu;
387+
auto decode_pos = decode_pos_cpu;
388+
#endif
363389

364390
uint64_t prev_token = cur_token;
365391
bool hit_eos = eos_ids.find(cur_token) != eos_ids.end();
366392
for (int32_t step = 0; step < FLAGS_max_new_tokens && !hit_eos; step++) {
367393
decode_token_data[0] = static_cast<int64_t>(cur_token);
368394
decode_pos_data[0] = pos;
369395

396+
#ifdef EXECUTORCH_BUILD_CUDA
397+
// skip_h2d: refresh the device-resident token/pos buffers ourselves (the
398+
// backend no longer inserts the H2D copy). The prior step's sampled id
399+
// already came back to the host via read_token, so re-upload the 8-byte
400+
// token and position into the fixed device buffers.
401+
ET_CHECK_MSG(
402+
cudaMemcpy(
403+
decode_tokens->mutable_data_ptr(),
404+
decode_token_data.data(),
405+
sizeof(int64_t),
406+
cudaMemcpyHostToDevice) == cudaSuccess,
407+
"Failed to upload decode token H2D");
408+
ET_CHECK_MSG(
409+
cudaMemcpy(
410+
decode_pos->mutable_data_ptr(),
411+
decode_pos_data.data(),
412+
sizeof(int64_t),
413+
cudaMemcpyHostToDevice) == cudaSuccess,
414+
"Failed to upload decode position H2D");
415+
#endif
416+
370417
std::vector<EValue> inputs;
371418
inputs.push_back(EValue(decode_tokens));
372419
inputs.push_back(EValue(decode_pos));

0 commit comments

Comments
 (0)