|
10 | 10 | #include "fbgemm_gpu/embedding_backward_template_helpers.cuh" |
11 | 11 | #include "fbgemm_gpu/utils/tensor_accessor_builder.h" |
12 | 12 | #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> |
13 | 18 |
|
14 | 19 | using Tensor = at::Tensor; |
15 | 20 |
|
@@ -248,4 +253,177 @@ void grad_mean{{ vdesc }}_kernel |
248 | 253 |
|
249 | 254 | } |
250 | 255 |
|
| 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 | + |
251 | 429 | // clang-format on |
0 commit comments