@@ -214,32 +214,60 @@ void compute_slice(
214214 const bool use_multithreading = leading_dims >= MIN_LEADING_DIMS_FOR_MT &&
215215 total_elements >= MIN_ELEMENTS_FOR_MT ;
216216
217+ // Contiguous fast path for step == 1 (the common case:
218+ // tensor.narrow, x[a:b], KV cache reads, etc.). When step == 1, the per-row
219+ // slice is one contiguous block of `length * length_per_step` bytes — replace
220+ // `length` calls of memcpy(length_per_step) with a single bulk memcpy.
221+ const bool step_is_one = (step == 1 );
222+ const size_t row_bytes = static_cast <size_t >(length) * length_per_step;
217223 if (use_multithreading) {
218224 // Use parallel_for to distribute work across leading dimensions
219225 // Calculate grain size based on number of elements per leading dimension
220226 const int64_t grain_size = MIN_LEADING_DIMS_FOR_MT ;
221227
222- executorch::extension::parallel_for (
223- 0 , leading_dims, grain_size, [&](const auto begin, const auto end) {
224- for (const auto i : c10::irange (begin, end)) {
225- const char * src =
226- input_data + (i * dim_length + start) * length_per_step;
227- char * local_dest = dest + i * length * length_per_step;
228- for ([[maybe_unused]] const auto j : c10::irange (length)) {
229- memcpy (local_dest, src, length_per_step);
230- src += step * length_per_step;
231- local_dest += length_per_step;
228+ if (step_is_one) {
229+ executorch::extension::parallel_for (
230+ 0 , leading_dims, grain_size, [&](const auto begin, const auto end) {
231+ for (const auto i : c10::irange (begin, end)) {
232+ const char * src =
233+ input_data + (i * dim_length + start) * length_per_step;
234+ char * local_dest = dest + i * row_bytes;
235+ memcpy (local_dest, src, row_bytes);
232236 }
233- }
234- });
237+ });
238+ } else {
239+ executorch::extension::parallel_for (
240+ 0 , leading_dims, grain_size, [&](const auto begin, const auto end) {
241+ for (const auto i : c10::irange (begin, end)) {
242+ const char * src =
243+ input_data + (i * dim_length + start) * length_per_step;
244+ char * local_dest = dest + i * row_bytes;
245+ for ([[maybe_unused]] const auto j : c10::irange (length)) {
246+ memcpy (local_dest, src, length_per_step);
247+ src += step * length_per_step;
248+ local_dest += length_per_step;
249+ }
250+ }
251+ });
252+ }
235253 } else {
236254 // Single-threaded path for small workloads
237- for (const auto i : c10::irange (leading_dims)) {
238- const char * src = input_data + (i * dim_length + start) * length_per_step;
239- for ([[maybe_unused]] const auto j : c10::irange (length)) {
240- memcpy (dest, src, length_per_step);
241- src += step * length_per_step;
242- dest += length_per_step;
255+ if (step_is_one) {
256+ for (const auto i : c10::irange (leading_dims)) {
257+ const char * src =
258+ input_data + (i * dim_length + start) * length_per_step;
259+ memcpy (dest, src, row_bytes);
260+ dest += row_bytes;
261+ }
262+ } else {
263+ for (const auto i : c10::irange (leading_dims)) {
264+ const char * src =
265+ input_data + (i * dim_length + start) * length_per_step;
266+ for ([[maybe_unused]] const auto j : c10::irange (length)) {
267+ memcpy (dest, src, length_per_step);
268+ src += step * length_per_step;
269+ dest += length_per_step;
270+ }
243271 }
244272 }
245273 }
0 commit comments