Skip to content

Commit c16de23

Browse files
gchalumpfacebook-github-bot
authored andcommitted
Add tbe_bwd_indices_preproc op + reference-impl unit test (pytorch#6222)
Summary: X-link: https://github.com/facebookresearch/FBGEMM/pull/3107 Add the standalone `tbe_bwd_indices_preproc` CUDA op that runs the two grad-independent index-preprocessing steps -- transpose_embedding_input (linearize -> radix-sort -> RLE + cumsum) and find_long_segments (segment partition) -- and returns them as a 12-tensor bundle in the driver's preproc_tensors[0..11] unpack order. Folded into embedding_backward_split_grad_template.cu so it shares the split_embedding_backward_codegen_find_long_segments __global__ defined there -- no separate build target/file. CUDA, common path (bagged, non-index-select); max_segment_length_per_cta + use_deterministic_algorithms are derived internally to mirror the inline driver. Op-only base of the preproc-hoist stack: this diff only defines and registers the op. The consume/route wiring (backward driver + PT2 autograd) lands in D113624507 above; the forward-emit in D115771945. Differential Revision: D114645411
1 parent 7e4c505 commit c16de23

4 files changed

Lines changed: 373 additions & 2 deletions

File tree

fbgemm_gpu/codegen/training/backward/embedding_backward_split_grad_template.cu

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
#include "fbgemm_gpu/embedding_backward_template_helpers.cuh"
1111
#include "fbgemm_gpu/utils/tensor_accessor_builder.h"
1212
#include "fbgemm_gpu/split_embeddings_utils.cuh"
13+
#include "fbgemm_gpu/config/feature_gates.h"
14+
#include "fbgemm_gpu/utils/kernel_launcher.cuh"
15+
#include "fbgemm_gpu/utils/ops_utils.h"
16+
#include <ATen/cuda/CUDAContext.h>
17+
#include <torch/library.h>
1318

1419
using Tensor = at::Tensor;
1520

@@ -248,4 +253,177 @@ void grad_mean{{ vdesc }}_kernel
248253

249254
}
250255

256+
{% if not is_index_select %}
257+
// ===========================================================================
258+
// tbe_bwd_indices_preproc: combined index-preprocessing op for the TBE
259+
// backward. Folded into this single, non-optimizer-templated TU so it shares
260+
// the split_embedding_backward_codegen_find_long_segments __global__ defined
261+
// above (embedding_ops namespace) -- no forward-decl, compiled once, NO
262+
// separate build target/file. Wraps the two grad-independent steps
263+
// 1) transpose_embedding_input (linearize -> radix-sort -> RLE + cumsum)
264+
// 2) find_long_segments (segment partition)
265+
// so they can be hoisted OFF the backward critical path. CUDA, common path
266+
// (bagged, non-index-select). max_segment_length_per_cta +
267+
// use_deterministic_algorithms are derived internally, mirroring the driver.
268+
// The 12-tensor output order matches the driver's preproc_tensors[0..11]
269+
// unpack contract in embedding_backward_split_template.cu.
270+
// Design doc:
271+
// docs.google.com/document/d/1Z8_1zI_4WSF-gsaHKVLY3wUNPyAZSYRJLDSbDZfRE2o
272+
// ===========================================================================
273+
namespace fbgemm_gpu {
274+
275+
std::tuple<
276+
Tensor, // linear_indices
277+
Tensor, // linear_indices_sorted
278+
Tensor, // sorted_linear_indices_run
279+
Tensor, // sorted_linear_indices_run_lengths
280+
Tensor, // sorted_linear_indices_num_runs
281+
Tensor, // sorted_linear_indices_cumulative_run_lengths
282+
Tensor, // infos_sorted
283+
Tensor, // long_run_ids
284+
Tensor, // num_long_run_ids
285+
Tensor, // long_run_id_to_really_long_run_ids
286+
Tensor, // num_really_long_run_ids
287+
Tensor> // grad_accum_counter
288+
tbe_bwd_indices_preproc_cuda(
289+
const Tensor& hash_size_cumsum,
290+
const int64_t total_hash_size_bits,
291+
const Tensor& indices,
292+
const Tensor& offsets,
293+
const int64_t info_B_num_bits,
294+
const int64_t info_B_mask,
295+
const int64_t total_unique_indices,
296+
const std::optional<Tensor>& vbe_b_t_map,
297+
const bool nobag,
298+
const bool is_index_select) {
299+
CUDA_DEVICE_GUARD(indices);
300+
301+
// Observability: confirm the hoisted index-preproc op is actually launched at
302+
// runtime (vs. the inline backward path). Fires once per process to avoid
303+
// per-iteration log spam; the payload lets us verify the call context.
304+
TORCH_WARN_ONCE(
305+
"[tbe_bwd_indices_preproc] hoisted index-preproc op launched: ",
306+
"num_indices=", indices.numel(),
307+
", total_unique_indices=", total_unique_indices,
308+
", nobag=", nobag,
309+
", is_index_select=", is_index_select);
310+
311+
// ---- Part A: transpose_embedding_input ----------------------------------
312+
auto
313+
[linear_indices,
314+
linear_indices_sorted,
315+
infos_sorted,
316+
sorted_linear_indices_run,
317+
sorted_linear_indices_run_lengths,
318+
sorted_linear_indices_num_runs,
319+
sorted_linear_indices_cumulative_run_lengths] =
320+
transpose_embedding_input(
321+
hash_size_cumsum,
322+
total_hash_size_bits,
323+
indices,
324+
offsets,
325+
nobag,
326+
vbe_b_t_map,
327+
info_B_num_bits,
328+
info_B_mask,
329+
total_unique_indices,
330+
is_index_select);
331+
332+
// ---- Part B: find_long_segments -----------------------------------------
333+
// Grid bound: when total_unique_indices is unknown at call time (-1, e.g.
334+
// hoisted into the forward before the run count is available), fall back to
335+
// indices.numel() -- a safe upper bound on the number of runs. The kernel
336+
// bounds its real work by the device-side run count, so extra blocks are
337+
// no-ops; this only over-launches, it does not affect correctness.
338+
const auto num_unique =
339+
total_unique_indices >= 0 ? total_unique_indices : indices.numel();
340+
341+
auto long_run_ids =
342+
at::empty({indices.numel()}, sorted_linear_indices_run_lengths.options());
343+
auto num_long_run_ids = at::zeros({1}, indices.options().dtype(at::kInt));
344+
345+
const bool use_deterministic_algorithms =
346+
at::globalContext().deterministicAlgorithms();
347+
348+
// max_segment_length_per_warp is a fixed policy constant (warp/CTA routing
349+
// threshold), not a runtime input -- derived internally to mirror the driver.
350+
#ifdef USE_ROCM
351+
constexpr int32_t max_segment_length_per_warp = 16384;
352+
const int max_segment_length_per_cta =
353+
use_deterministic_algorithms ? INT_MAX : 4096;
354+
#else
355+
constexpr int32_t max_segment_length_per_warp = 32;
356+
const auto device_properties = at::cuda::getCurrentDeviceProperties();
357+
int default_segment_length = 1024;
358+
const bool b200_feature_enabled =
359+
(device_properties->major >= 10) &&
360+
fbgemm_gpu::config::is_feature_enabled(
361+
fbgemm_gpu::config::FeatureGateName::
362+
TBE_USE_TUNED_SEGMENT_LENGTHS_CTA_B200);
363+
if (b200_feature_enabled) {
364+
default_segment_length = 4096;
365+
}
366+
const int max_segment_length_per_cta =
367+
use_deterministic_algorithms ? INT_MAX : default_segment_length;
368+
#endif
369+
370+
Tensor long_run_id_to_really_long_run_ids;
371+
if (use_deterministic_algorithms) {
372+
long_run_id_to_really_long_run_ids =
373+
at::empty(0, sorted_linear_indices_run_lengths.options());
374+
} else {
375+
long_run_id_to_really_long_run_ids = at::empty(
376+
{indices.numel()}, sorted_linear_indices_run_lengths.options());
377+
}
378+
379+
auto num_really_long_run_ids =
380+
at::zeros({1}, indices.options().dtype(at::kInt));
381+
auto grad_accum_counter = at::empty(
382+
use_deterministic_algorithms
383+
? 0
384+
: (indices.numel() / max_segment_length_per_cta),
385+
indices.options().dtype(at::kInt));
386+
387+
constexpr auto fls_ctx = "find_long_segments";
388+
FBGEMM_LAUNCH_KERNEL(
389+
embedding_ops::split_embedding_backward_codegen_find_long_segments,
390+
div_round_up(num_unique, kMaxThreads),
391+
kMaxThreads,
392+
0,
393+
at::cuda::getCurrentCUDAStream(),
394+
PTA_B(sorted_linear_indices_num_runs, int32_t, 1, 32).build(fls_ctx),
395+
PTA_B(sorted_linear_indices_run_lengths, int32_t, 1, 32).build(fls_ctx),
396+
PTA_B(long_run_ids, int32_t, 1, 32).build(fls_ctx),
397+
PTA_B(num_long_run_ids, int32_t, 1, 32).build(fls_ctx),
398+
PTA_B(long_run_id_to_really_long_run_ids, int32_t, 1, 32).build(fls_ctx),
399+
PTA_B(num_really_long_run_ids, int32_t, 1, 32).build(fls_ctx),
400+
PTA_B(grad_accum_counter, int32_t, 1, 32).build(fls_ctx),
401+
max_segment_length_per_warp,
402+
max_segment_length_per_cta,
403+
use_deterministic_algorithms);
404+
405+
return {
406+
linear_indices,
407+
linear_indices_sorted,
408+
sorted_linear_indices_run,
409+
sorted_linear_indices_run_lengths,
410+
sorted_linear_indices_num_runs,
411+
sorted_linear_indices_cumulative_run_lengths,
412+
infos_sorted,
413+
long_run_ids,
414+
num_long_run_ids,
415+
long_run_id_to_really_long_run_ids,
416+
num_really_long_run_ids,
417+
grad_accum_counter};
418+
}
419+
420+
} // namespace fbgemm_gpu
421+
422+
// CUDA dispatch is registered here, co-located with the codegen definition so
423+
// the symbol links; the schema m.def lives in src/split_embeddings_utils/.
424+
TORCH_LIBRARY_FRAGMENT(fbgemm, m) {
425+
DISPATCH_TO_CUDA("tbe_bwd_indices_preproc", tbe_bwd_indices_preproc_cuda);
426+
}
427+
{% endif %}
428+
251429
// clang-format on

fbgemm_gpu/include/fbgemm_gpu/split_embeddings_utils.cuh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,43 @@ transpose_embedding_input(
4343
const int64_t fixed_L_per_warp = 0,
4444
const int64_t num_warps_per_feature = 0);
4545

46+
namespace fbgemm_gpu {
47+
48+
// Combined grad-independent index-preprocessing op for the TBE backward
49+
// (transpose_embedding_input + find_long_segments). Defined AND CUDA-dispatched
50+
// in the generated embedding_backward_split_grad.cu (dispatch must be
51+
// co-located with the definition to link); only the schema m.def lives in
52+
// src/split_embeddings_utils/. Do NOT give these params C++ default arguments:
53+
// the dispatcher always passes all args (Python-facing defaults come from the
54+
// schema string), and defaults baked into the function type break TORCH_FN in
55+
// the CUDA-compiled dispatch TU.
56+
std::tuple<
57+
at::Tensor, // linear_indices
58+
at::Tensor, // linear_indices_sorted
59+
at::Tensor, // sorted_linear_indices_run
60+
at::Tensor, // sorted_linear_indices_run_lengths
61+
at::Tensor, // sorted_linear_indices_num_runs
62+
at::Tensor, // sorted_linear_indices_cumulative_run_lengths
63+
at::Tensor, // infos_sorted
64+
at::Tensor, // long_run_ids
65+
at::Tensor, // num_long_run_ids
66+
at::Tensor, // long_run_id_to_really_long_run_ids
67+
at::Tensor, // num_really_long_run_ids
68+
at::Tensor> // grad_accum_counter
69+
tbe_bwd_indices_preproc_cuda(
70+
const at::Tensor& hash_size_cumsum,
71+
const int64_t total_hash_size_bits,
72+
const at::Tensor& indices,
73+
const at::Tensor& offsets,
74+
const int64_t info_B_num_bits,
75+
const int64_t info_B_mask,
76+
const int64_t total_unique_indices,
77+
const std::optional<at::Tensor>& vbe_b_t_map,
78+
const bool nobag,
79+
const bool is_index_select);
80+
81+
} // namespace fbgemm_gpu
82+
4683
// Use these functions instead of directly calling cub functions
4784
// to reduce code size and compilation time.
4885
// Arguments are the same as cub::DeviceRadixSort::SortPairs

fbgemm_gpu/src/split_embeddings_utils/split_embeddings_utils_cpu.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,20 @@ TORCH_LIBRARY_FRAGMENT(fbgemm, m) {
200200
" int fixed_L_per_warp=0, "
201201
" int num_warps_per_feature=0"
202202
") -> (Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor)");
203+
m.def(
204+
"tbe_bwd_indices_preproc("
205+
" Tensor hash_size_cumsum, "
206+
" int total_hash_size_bits, "
207+
" Tensor indices, "
208+
" Tensor offsets, "
209+
" int info_B_num_bits=26, "
210+
" int info_B_mask=0x2FFFFFF, "
211+
" int total_unique_indices=-1, "
212+
" Tensor? vbe_b_t_map=None, "
213+
" bool nobag=False, "
214+
" bool is_index_select=False"
215+
") -> (Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, "
216+
"Tensor, Tensor, Tensor, Tensor)");
203217
m.def("get_infos_metadata(Tensor unused, int B, int T) -> (int, int)");
204218
m.def(
205219
"generate_vbe_metadata("

0 commit comments

Comments
 (0)