Skip to content

Commit ad52be9

Browse files
authored
step==1 contiguous fast path in portable compute_slice (#19606)
Differential Revision: D105241644 Pull Request resolved: #19606
1 parent 430b73d commit ad52be9

1 file changed

Lines changed: 46 additions & 18 deletions

File tree

kernels/portable/cpu/util/slice_util.cpp

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)