Skip to content

test(torchwave): Barrier for a transposed leaf of the same kernel (#18863) - #18863

Open
oerling wants to merge 5 commits into
facebookincubator:mainfrom
oerling:export-D118061299
Open

test(torchwave): Barrier for a transposed leaf of the same kernel (#18863)#18863
oerling wants to merge 5 commits into
facebookincubator:mainfrom
oerling:export-D118061299

Conversation

@oerling

@oerling oerling commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary:

A fused pair whose second op reads a transposed view of what the first
wrote has to carry an intra-kernel barrier. The contrast case -- the same
pair with the intermediate consumed straight through, which keeps it in a
register -- must not, so the test still fails if every fused elementwise
pair starts carrying one.

Reviewed By: Yuhta

Differential Revision: D118061299

@netlify

netlify Bot commented Sep 5, 2026

Copy link
Copy Markdown

Deploy Preview for meta-velox canceled.

Name Link
🔨 Latest commit e4391d2
🔍 Latest deploy log https://app.netlify.com/projects/meta-velox/deploys/6a9c5ace36daa90008a3ed13

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 5, 2026
@meta-codesync

meta-codesync Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

@oerling has exported this pull request. If you are a Meta employee, you can view the originating Diff in D118061299.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

CI Failure Analysis

Auto-generated by the CI Failure Analysis workflow. This comment is updated in place each time CI fails on a new commit, so it always reflects the latest run — re-pushing or re-running CI will refresh the analysis below. Last updated 2026-09-05 08:31:19 UTC from workflow run 33954346132.

🟡 Presto Fuzzer — FUZZER Failure View logs

Fuzzer crash: 2 of 4 instances failed.

Instance 1 (seed=31842952) — Result verification mismatch:

FuzzerToolkit.cpp:149 — Different values at idx '3': 'false' vs. 'true'
Expression: left->equalValueAt(right.get(), row, row)

Stack trace (key frames):
  velox/expression/fuzzer/FuzzerToolkit.cpp:149      compareVectors
  velox/expression/tests/ExpressionVerifier.cpp:586  ExpressionVerifier::verify
  velox/expression/fuzzer/ExpressionFuzzerVerifier.cpp:439  go()
  velox/expression/fuzzer/FuzzerRunner.cpp:291       runFromGtest

Instance 3 (seed=279323501) — F14 hash table assertion failure during rehash:

Assertion failure: hp.second == srcChunk->tag(srcI)
File: /deps/include/folly/container/detail/F14Table.h
Line: 2487
Function: rehashImpl

🟡 Expression Fuzzer with Presto SOT — FUZZER Failure View logs

Fuzzer crash: 1 of 4 instances failed.

Instance 2 (seed=635187378) — Velox vs Presto result mismatch:

ExpressionVerifier.cpp:480 — Velox and reference DB results don't match

QueryAssertions.cpp:1167: Failure
Expected 100, got 100
1 extra rows, 1 missing rows
1 of extra rows:
	null | 39

1 of missing rows:
	"rBw:p30uU+eNiUG*,Yfl[yqnlc+l3v$j@5x1" | 39

Unexpected results

The mismatch shows Velox returned null for a column where Presto returned a string value, suggesting a function evaluation produced null instead of the correct string result.


Correlation with PR changes:

The PR (test(torchwave): Barrier for a transposed leaf of the same kernel) modifies files exclusively under velox/experimental/torchwave/ and velox/experimental/wave/. The failing fuzzer jobs test the Presto expression evaluation engine (velox/expression/), which is entirely unrelated to the torchwave/GPU compute code. These failures are not caused by this PR.

Known issues:

  • The Presto Fuzzer and Expression Fuzzer with Presto SOT jobs are known to be flaky. The most recent 4 Fuzzer Jobs runs on main all passed, so this specific seed-dependent failure is not a persistent regression, but fuzzer failures with random seeds are inherently non-deterministic.
  • The F14Table rehash assertion (hp.second == srcChunk->tag(srcI)) is a known intermittent issue in folly's hash table, typically triggered by memory corruption or hash-map misuse under specific random inputs.
  • The result mismatch pattern (Velox returning null vs Presto returning a string) is similar to previously reported issues: #14335, #14422.

Reproduce locally:

# Presto Fuzzer — Instance 1 (result verification mismatch)
./_build/debug/velox/expression/fuzzer/velox_expression_fuzzer_test \
    --seed 31842952 \
    --enable_variadic_signatures \
    --velox_fuzzer_enable_complex_types \
    --velox_fuzzer_enable_decimal_type \
    --lazy_vector_generation_ratio 0.2 \
    --common_dictionary_wraps_generation_ratio=0.3 \
    --velox_fuzzer_enable_column_reuse \
    --velox_fuzzer_enable_expression_reuse \
    --max_expression_trees_per_step 2 \
    --retry_with_try \
    --special_forms="and,or,cast,coalesce" \
    --enable_dereference \
    --duration_sec 300

# Presto Fuzzer — Instance 3 (F14Table rehash assertion)
./_build/debug/velox/expression/fuzzer/velox_expression_fuzzer_test \
    --seed 279323501 \
    --enable_variadic_signatures \
    --velox_fuzzer_enable_complex_types \
    --velox_fuzzer_enable_decimal_type \
    --lazy_vector_generation_ratio 0.2 \
    --common_dictionary_wraps_generation_ratio=0.3 \
    --velox_fuzzer_enable_column_reuse \
    --velox_fuzzer_enable_expression_reuse \
    --max_expression_trees_per_step 2 \
    --retry_with_try \
    --special_forms="and,or,cast,coalesce" \
    --enable_dereference \
    --duration_sec 300

# Expression Fuzzer with Presto SOT — Instance 2 (Velox vs Presto mismatch)
./_build/debug/velox/expression/fuzzer/velox_expression_fuzzer_test \
    --seed 635187378 \
    --enable_variadic_signatures \
    --velox_fuzzer_enable_complex_types \
    --lazy_vector_generation_ratio 0.2 \
    --common_dictionary_wraps_generation_ratio=0.3 \
    --velox_fuzzer_enable_column_reuse \
    --velox_fuzzer_enable_expression_reuse \
    --enable_dereference \
    --special_forms="cast,coalesce,if" \
    --velox_fuzzer_max_level_of_nesting=1 \
    --presto_url=http://127.0.0.1:8080 \
    --duration_sec 300

Recommended fix: No action needed on this PR. These are pre-existing flaky fuzzer failures unrelated to the torchwave changes. Safe to re-run CI or merge if other checks pass.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Selective Build Plan

Linux release with adapters is running a full build (PR has a standing approval). See the CI workflows README for what this means.


Selective build plan

Summary:
Hooks the IG-ranking ROO preproc benchmark to the TorchWave GPU engine via a new
`--torchwave` arm, using the whole-graph AOT path (the AOTInductor alternative
from D107956042): the entire preproc graph is exported once and handed to
TorchWave as a single unit. TorchWave fuses what it can and falls back to
nativert kernels for the rest, so there is no `torch.compile` and no automatic
graph breaks.

- `roo_wave_runner.py` (new): `WholeGraphWaveModel` exports the eager preproc
  with `export_preproc_pt2`, packages it whole via `compile_and_package(...,
  decompose=False)` (the export already ran the ROO-safe
  `run_decompositions(decomp_table={})`), and loads it with `torchwave.load`.
  Inputs are flattened with `fx_pytree.tree_flatten_spec` against the export
  in_spec (so KJT string keys are absorbed into the spec, not passed as leaves)
  and moved to CUDA; outputs are compared against eager position-by-position via
  the out_spec. `set_kernel_cache_dir` points the compiler at an on-disk cubin
  cache.
- `roo_train_profile_benchmark.py`: adds `--torchwave` (checks parity vs eager,
  then times warmup + repeats; requires `--on-gpu`) and `--kernel-cache-dir` /
  `--kernel_cache_dir`.
- `pybind.cpp`: exposes `WaveConfig.kernel_cache_dir` to Python. The on-disk
  cubin cache (`KernelFsCache`) is already wired in C++; this is the missing
  setter so a repeated `--torchwave` run skips NVRTC compilation.
- `tests/test_roo_preproc_wave_parity.py` (new): GPU `python_unittest` that
  builds the eager module from the model config (no Hive) and asserts
  TorchWave-vs-eager parity on a sample batch. Skips when CUDA or the batch is
  absent; the batch path can be overridden with `ROO_SAMPLE_BATCH`.

- `Executor.h` / `Executor.cpp`: `runInputsReuse(inputs)` holds ONE execution frame across calls with weights and constants resident, refilling only the user inputs, instead of the per-call getFrame / clear / returnFrame cycle. That is how the benchmark's wave arm is timed, and it matches what the C++ executor test measures. Non-persistent values are still cleared each call, since successive batches have data-dependent shapes and a stale-sized intermediate would otherwise be reused. `TW_TIME_EXEC=1` prints the per-call phase breakdown (clear/getFrame, fillUserInputs, exec+sync).
- `Executor.h` / `Executor.cpp` also add TU-local setters -- `setWaveTrace`, `setFreeIntermediates`, `setAutoAdjustCost`, `setIsCg`, `setKernelCacheDir`, `setAllStandalone`, `setEnableReuse`, `setBlockSize` -- so a caller in another translation unit (the pybind extensions) reaches the exact `WaveConfig` singleton the executor reads. A plain `wave_config()` assignment from another TU can land on a duplicated inline-static instance and silently not take effect, which is the failure this removes.
- `torchwave_meta/WaveExecutorPy.cpp` and the two `.pyi` stubs expose those setters plus `last_perf_report()` on the fused `_torchwave_meta` module, so the benchmark can configure the same engine instance it runs.
- `roo_train_pt2_export_benchmark.py`: a `--torchwave` arm that validates TorchWave against the SIGMOID INTERPRETER (and the PT2 module) rather than eager. Both execute the same exported graph, so this is the apples-to-apples engine comparison; a failure in one executor is reported without aborting the others.
- `GraphPrep`: `rewriteMergeAndDedupToTw` retargets the sparsenn merge-and-dedup nodes to their fused TorchWave `_tw` CUDA equivalents, but only when those ops are actually registered in the dispatcher. When they are absent it is a no-op and the base op runs as a nativert standalone, so one `load()` path serves both the base engine and the fused-ops build.

Differential Revision: D113334660
Summary:
Explore Triton as a torchwave codegen target by porting the
`tw.masked_select_jagged` op to standalone Triton kernels. This does NOT
touch torchwave proper (no WaveGraph, no op registration); it adds a new
self-contained package under `torchwave_meta/tests/triton/`.

Two variants parallel the two CUDA forms in
`minimal_viable_ai/torchwave_meta/Select.cuh`:
- multikernel: staged launches (per-chunk count -> scan of partials ->
  write exclusive prefix -> scatter -> new segment lengths), mirroring
  torchwave's multi-kernel execution mode.
- cg: a single persistent kernel carrying the same three stages across
  device-wide atomic barriers -- the Triton analogue of the CUDA
  `opBarrier` / grid.sync (`num_programs` kept co-resident so the
  spin-barrier cannot deadlock; programs grid-stride the chunks).

The standalone test parallels the CUDA gtest
`minimal_viable_ai/torchwave_meta/tests/KernelTest.cu` (SelectKernelTest):
same input generation, the same per-segment CPU reference, and the same
sizes; it validates the Triton kernels against the CPU reference and also
covers the grouped mixed-dtype path.

Authored with Claude.

Differential Revision: D114379433
Summary:
Fuses `torch.ops.fb.batch_flip_and_truncate_sparse` on wave by splitting it, in `maybeReplace`, into `tw.flip_and_truncate_head` and `tw.flip_and_truncate_final` (device functions in `Flip.cuh`). The head processes one feature per block: it computes each row's post-truncation length, inclusive-scans those into new offsets and the original lengths into old offsets, and after an op barrier accumulates the per-feature output totals. The final partitions the concatenated output into fixed 8192-element tiles and grid-strides over them, binary-searching once per tile for the owning feature and row, then walking rows to the end of the tile. Fixed tiles keep every block's work equal even though rows span roughly 300 to 10000 elements after truncation. Each thread resolves the same per-row copy descriptor, which hoists the flip-mode branch out of the element loop and leaves coalesced writes and forward or reversed coalesced reads. It matches the eager kernel across the four flip modes (none, flip_before, flip_after, flip_before_and_after), per-feature `max_lengths`, `pad_value`, and `adaptive_max_len` with a per-row `adaptive_mask`. On the four flip instances of an internal preproc graph the final reaches 75-76% of an A100's nominal bandwidth, against 89% for a `cudaMemcpy` of the same size and 53-66% for the eager kernel's thread mapping.

The final stages its per-feature constants (the offset, value and output pointers, row count, cap and mode) in shared memory rather than holding them in registers across the row loop. That is worth an occupancy step: the register version needs 64 registers and fits 4 blocks per SM, the shared version needs 48 and fits 5, and on a bandwidth-bound copy the extra block is worth about 3%. Sizing that array needs the feature count as a compile-time constant, which is where `Metadata::templateParamFuncs` comes in: a list of functions run on the node at codegen time to append extra template arguments to the device function call, empty for every other op. `Metadata::gridSizeSumsInputs` is the second new hook. An op whose work spans a whole tensor list was sized by its largest member, so the head got `ceil(768 / blockSize)` blocks no matter how many features it had, and most features queued onto three blocks; the flag switches that op to the sum instead. Neither hook changes any op that does not set it.

Three wave-core fixes in `Compile.cpp` support this and correct general bugs. First, an elementwise producer of a `TensorList` element that a fused op reads is now materialized to that element's memory buffer in all modes, so a list built inside the op (for example values from an add) is written where the consumer reads it. Second, a fused op that reads such a list emits a barrier when a same-op element producer is not already behind one, independent of the input's `randomAccess` flag. Third, subgraph deduplication now hashes int-list (ScalarList) attributes, whose element values are baked into the generated code; without this two otherwise-identical ops that differ only in such an attribute (for example `adaptive_mask`) would share a wrong kernel.

Differential Revision: D111315788
…rnel wrote

Summary:
An elementwise op's operands are all marked `isRegister` by
`registerElementwise`, and `callNeedsBarrier` skips register operands on
the grounds that they flow inline rather than through memory. That holds
for a value fused into the expression tree; it does not hold for a leaf,
which is a load like any other. The test was that the two are the same
thing, and they are not, so no pure-elementwise op ever asked for a
barrier.

What that costs is visible whenever the leaf does not map index to index.
The ROO preproc graph fuses

    (%3274)  = where.self(...)
    (%11769) = transpose.int(%3274, dim0=0, dim1=1)
    (%11800) = mul(%11771, %11769)

into one kernel with nothing but `__syncthreads()` between the write and
the read. Through the transpose the element thread i of the `mul` wants
was written by a different block, so `%11800` came out part stale and
part uninitialized -- and only in the multi-block modes, which is what
made it look like a miscompile rather than a race.

The operand test `callNeedsBarrier` already had is now
`valueNeedsBarrier`, and `generateElementwise` runs it over each
subgraph's own memory leaves.

Differential Revision: D118061298
…cebookincubator#18863)

Summary:
Pull Request resolved: facebookincubator#18863

A fused pair whose second op reads a transposed view of what the first
wrote has to carry an intra-kernel barrier. The contrast case -- the same
pair with the intermediate consumed straight through, which keeps it in a
register -- must not, so the test still fails if every fused elementwise
pair starts carrying one.

Reviewed By: Yuhta

Differential Revision: D118061299
@meta-codesync meta-codesync Bot changed the title test(torchwave): Barrier for a transposed leaf of the same kernel test(torchwave): Barrier for a transposed leaf of the same kernel (#18863) Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants