Skip to content

flashdreams.accelerated API v0 - #486

Open
fangjunzhou-nv wants to merge 6 commits into
NVIDIA:mainfrom
fangjunzhou-nv:dev/fangjun/flashdreams-accelerated
Open

flashdreams.accelerated API v0#486
fangjunzhou-nv wants to merge 6 commits into
NVIDIA:mainfrom
fangjunzhou-nv:dev/fangjun/flashdreams-accelerated

Conversation

@fangjunzhou-nv

@fangjunzhou-nv fangjunzhou-nv commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Add FlashDreams accelerated primitives and integrate them with Omnidreams

Summary

Introduce reusable quantization and optimized multi-head attention primitives under flashdreams.accelerated, integrate them with Omnidreams, and add reproducible performance benchmarks and plotting tools.

1. flashdreams.accelerated

1.1 Quantization toolkit

  • Add tensor- and slice-granularity quantization for:
    • INT8
    • FP8 E4M3
    • FP8 E5M2
  • Provide Torch reference and Triton CUDA implementations for quantization and dequantization.
  • Add quantized non-persistent linear layers backed by CUDA integer/scaled GEMMs.
  • Support dynamically quantized and prequantized activations.
  • Keep derived quantized weights and scales out of state_dict, preserving source checkpoint compatibility.
  • Cover scale validation, empty inputs, output dtypes, round trips, and quantized GEMMs with CPU and GPU tests.

1.2 Optimized MHA

  • Add a shared multi-head attention interface for streaming self-attention and static cross-attention.
  • Add a Torch reference implementation and an optimized CUDA implementation.
  • Support:
    • cuDNN SDPA
    • Triton FlashAttention 2
    • optional TMA FlashAttention 2 kernels
    • full QKV fusion, fused KV, or unfused projections
    • optional INT8/FP8 projection quantization
    • optional FP8 SDPA and KV caches
    • head- or inner-scoped Q/K normalization
    • interleaved or split RoPE before or after KV-cache storage
  • Reuse BlockKVCache and preserve caller-managed cache lifecycles.
  • Preserve checkpoint-native projection names by keeping fused and quantized derived weights non-persistent.
  • Require CUDA FP16/BF16 inputs and compute capability 9.0 or newer for the optimized path.
  • Add numerical parity tests across attention types, SDPA backends, fusion policies, RoPE policies, quantization modes, and TMA/non-TMA kernels.

2. Integration: Omnidreams

  • Add independently configurable self- and cross-attention backends while retaining the existing Omnidreams implementation as the default.
  • Adapt Omnidreams attention modules to the shared optimized MHA interface without changing checkpoint keys or cache behavior.
  • Thread optimized attention policies through the block and network configurations.
  • Register new runner presets:
    • omnidreams-triton-fa2
    • omnidreams-cuda-cudnn
    • omnidreams-cuda-sparge
    • omnidreams-cuda-sage3fp8
  • Skip final KV-cache advancement in steady-state performance presets.
  • Fix native CUDA extension builds on GB300:
    • stop forcing all devices to compile for 12.0a
    • enable SageAttention 3 sources only for validated SM120a devices
    • defer other architecture selection to PyTorch or explicit environment overrides
    • isolate extension names and caches by CUDA architecture
  • Add tests for backend selection, optimized-policy propagation, cache lifecycles, checkpoint compatibility, runner registration, and native architecture detection.

3. Benchmark and plot scripts

  • Add pytest-benchmark suites for:
    • quantization and dequantization
    • quantized linear layers and GEMMs
    • self- and cross-attention
    • Omnidreams attention modules and DiT blocks
    • complete Omnidreams network and pipeline execution
  • Compare Torch, optimized cuDNN, optimized FA2, and native CUDA configurations using matched production-shaped workloads.
  • Include warmup rounds to exclude compilation and autotuning overhead.
  • Record implementation, device, tensor geometry, precision, and backend metadata in benchmark results.
  • Add scripts that run benchmark suites, export JSON results, and generate comparison plots.
  • Add aggregate scripts for running or plotting all benchmark groups.
  • Document the Omnidreams test and benchmark workflows.
  • Add pytest-benchmark configuration and dependencies.

Testing

The change includes CPU, GPU, and manual benchmark coverage. GPU validation requires a supported NVIDIA GPU; the optimized MHA path requires compute capability 9.0 or newer.

Generated benchmark JSON and figures are intentionally not committed.

@copy-pr-bot

copy-pr-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@greptile-apps

greptile-apps Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR introduces reusable accelerated quantization and multi-head-attention primitives, integrates selectable optimized attention backends into OmniDreams, and adds benchmark tooling.

  • Adds Torch, Triton, cuDNN, FP8, and quantized projection paths under flashdreams.accelerated.
  • Adds configurable optimized self- and cross-attention implementations while preserving checkpoint-native projection parameters.
  • Refreshes non-persistent fused and quantized projection weights after checkpoint loading and device or dtype transformations.
  • Adds runner presets, architecture-aware native extension configuration, tests, benchmarks, and plotting scripts.

Confidence Score: 5/5

The PR appears safe to merge.

The previously reported stale derived-weight issue is resolved because optimized attention now registers a post-load hook that rebuilds fused and quantized projections from the loaded canonical parameters, and no blocking failure remains.

Important Files Changed

Filename Overview
flashdreams/flashdreams/accelerated/multi_head_attention/optimized.py Adds optimized attention policy, projection fusion, quantization, cache validation, and post-load/device-transform refresh of derived execution weights.
integrations/omnidreams/omnidreams/transformer/impl/modules.py Adds selectable optimized OmniDreams attention adapters while retaining canonical checkpoint projection names and cache contracts.
flashdreams/flashdreams/accelerated/quantization/quantizer.py Implements reference and accelerated tensor- and slice-granularity quantization interfaces.
flashdreams/flashdreams/accelerated/quantization/linear.py Adds non-persistent quantized linear execution for dynamic and prequantized activations.
integrations/omnidreams/omnidreams/transformer/impl/network.py Propagates independently configurable self- and cross-attention policies through the OmniDreams network.
integrations/omnidreams/omnidreams/config.py Registers optimized attention policies in new OmniDreams runner presets.
flashdreams/flashdreams/accelerated/multi_head_attention/cudnn/native_fp8.py Adds cached cuDNN Frontend graphs for FP8 scaled-dot-product attention.
uv.lock Records the new benchmark, development, and lint dependencies without changing the pre-existing flagged Torch or Pillow resolutions.

Sequence Diagram

sequenceDiagram
  participant Network as OmniDreams Network
  participant Attention as Optimized Attention
  participant Loader as load_state_dict
  participant Derived as Derived Projections
  participant Forward as Inference
  Network->>Attention: Construct canonical Q/K/V projections
  Attention->>Derived: Build initial non-persistent projections
  Loader->>Attention: Load checkpoint parameters
  Attention->>Derived: Post-load hook refreshes fused/quantized weights
  Network->>Attention: Move or cast module
  Attention->>Derived: _apply refreshes weights on final device/dtype
  Forward->>Derived: Execute accelerated projections
Loading

Reviews (7): Last reviewed commit: "Add benchmark and plot scripts for flash..." | Re-trigger Greptile

Comment thread integrations/omnidreams/omnidreams/transformer/impl/modules.py

@ArielG-NV ArielG-NV left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question on inclusion of file

Comment thread .nvim.lua Outdated
@fangjunzhou-nv
fangjunzhou-nv force-pushed the dev/fangjun/flashdreams-accelerated branch from fc4b03f to f63a088 Compare August 20, 2026 20:05
@fangjunzhou-nv
fangjunzhou-nv marked this pull request as draft August 20, 2026 21:33
@fangjunzhou-nv
fangjunzhou-nv force-pushed the dev/fangjun/flashdreams-accelerated branch from 4ad51b5 to 97dfa44 Compare August 22, 2026 00:13
@fangjunzhou-nv
fangjunzhou-nv marked this pull request as ready for review August 22, 2026 00:31
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we should restrict the license header stuff to just the first 2 lines, no need for the rest.



class _TorchMultiHeadAttention(TorchMultiHeadAttention):
"""Canonical Torch attention implementation used by benchmarks."""

@jarcherNV jarcherNV Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does canonical mean in this context? Is there a better way to describe this?
Same for all the other usages.

)


class _OptimizedHultiHeadAttention(OptimizedHultiHeadAttention):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be OptimizedMultiHeadAttention?

# Static K/V projection is part of this end-to-end cross-attention
# measurement because fusion changes that stage, not query-only forward.
def synchronized_cross_forward() -> Tensor:
cache = attention.compute_kv(context, context_rope)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this calls compute_kv() on every measured round, so the result represents cache setup plus one query rather than steady-state cross-attention. Both numbers seem useful, but combining them may obscure which optimization is helping. Do they need to be reported separately?

use_tma=use_tma,
quantization=QuantizationOption(
projection=torch.float8_e4m3fn,
quantized_sdpa=True,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like every quantized-SDPA configuration also enables FP8 projections, so the results cannot distinguish projection-quantization performance from FP8 SDPA/KV-cache performance. Is that what is happening here?

Does there need to also be a quantized_sdpa=True, projection=None case? Or a projection-only case for FA2?

OptimizedImplConfig(
sdpa_backend=sdpa_backend,
qkv_fusion_option=qkv_fusion_option,
use_tma=use_tma,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use_tma be varied only when sdpa_backend is FA2 or does that not matter?

This cross-product also creates cuDNN tma and no-tma rows even though TMA does not apply to that backend, is this a problem? Does it lead to duplicated benchmark work? The quantized generator at 91 has the same pattern.

cache.before_update(chunk_idx)
attention(inputs[chunk_idx], cache, rope_freqs[chunk_idx])
cache.after_update(chunk_idx)
cache.before_update(_WINDOW_CHUNKS)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to add a separate lifecycle benchmark that includes before_update() and after_update()? The current result intentionally measures forward after the full-cache roll, but it excludes cache movement and I am wondering if it may miss part of the benefit of lower-precision KV-cache storage.

bias_label,
)
)
benchmark.extra_info.update(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we include query_dim, n_heads, head_dim, device capability, driver version, Triton version, and effective TMA selection in extra_info? The JSON records the requested policy and chunk/window sizes, but does it contain enough information to reconstruct the full geometry or confirm the path that actually ran?

return f"optimized-{backend}-{fusion}-{tma}{projection}{quantized_sdpa}"


_SHARED_CONFIGS = tuple(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to run a smaller representative policy matrix by default and make the exhaustive sweep opt-in? The current combinations expand to 1,008 cases, each with five warmups and 50 measured rounds, and some implementation combinations are redundant. A smaller default might be easier to run and interpret.

torch.float8_e4m3fn if quantized_dtype is torch.float8_e5m2 else quantized_dtype
)
scaled_output_dtype = (
torch.bfloat16

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should label this case with its effective output precision rather than grouping it as FP32.

With slice scaling and an FP32 requested output, _scaled_mm produces BF16 and the end-to-end path only casts that result to FP32 afterward. It's not really precision-equivalent to the FP32 baseline and could make the comparison misleading.


assert dequantized.shape == _SHAPE
assert dequantized.dtype is torch.float16
assert torch.isfinite(dequantized).all()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure that shape, dtype, and finiteness would catch an incorrect but fast kernel.

Do we need to compare the dequantized result against original, and similarly compare the quantization outputs against the Torch reference outside the timed region? It may also be useful to save the numerical error in benchmark.extra_info but I'll leave that up to you.

)
amax_o_desc.set_output(False).set_dim(list(amax_o.shape)).set_stride(
list(amax_o.stride())
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should amax_s_desc and amax_o_desc be marked with set_output(True) here? I think cuDNN documents these as required FP8 SDPA outputs. With False, we still allocate and bind buffers for them but discard the calibration information needed to select meaningful scales.

list(amax_o.stride())
)
graph.build([cudnn.heur_mode.A, cudnn.heur_mode.FALLBACK])
scale = torch.ones_like(amax_s)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct to set these to 1? I am wondering if this leaves Q, K, V, the softmax probabilities, and the output uncalibrated in E4M3. I think cuDNN recommends deriving FP8 scaling from observed AMax values. Do we need to provide calibrated scales or long-context quality evidence before using this as an optimization?

},
workspace,
)
return output

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could each call return independent output storage, or could this aliasing contract be made explicit and enforced? output is allocated once inside the cached closure, so repeated calls with the same cache key return the same tensor and overwrite all previously returned results. The current MHA caller immediately converts it to BF16/FP16, but native_cudnn_fp8_sdpa() is exported and direct callers could retain a result that is silently mutated by the next invocation. I am wondering if this will cause issues.

bhld_strides = (x.stride(0), x.stride(2), x.stride(1), x.stride(3))
return bhld_strides[-1] == 1 and all(
stride > 0 and stride * element_size % 16 == 0 for stride in bhld_strides[:-1]
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think TMA tensor descriptors require the base pointer itself to be 16-byte aligned, but this predicate only checks the strides. Do we also need to require x.data_ptr() % 16 == 0, and add a misaligned-view test?

raise TypeError(f"use_tma must be a bool; got {self.use_tma!r}")


class OptimizedHultiHeadAttention(MultiHeadAttention[BlockKVCache]):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this should be OptimizedMultiHeadAttention.

.contiguous()
)
fused_bias = None
if self.query_projection.bias is not None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think whether the fused projection has a bias is determined solely from the query bias, is that right? If Q has no bias but K or V does, those biases would be silently dropped. If Q has a bias but K/V does not, this hits an assertion.

I think the fused-KV path has the same issue at line 382. Do we need to preserve mixed biases by filling absent components with zeros or maybe explicitly validate that the relevant bias configurations match?


write_end = kv_cache.write_end
write_start = write_end - kv_cache.chunk_size
return rope_freqs[write_start:write_end], rope_freqs[: kv_cache.size]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a static cross-attention cache, chunk_size and write_end both correspond to the context length S, so this returns S query frequencies. Would _apply_rope() then reject a query whose length L differs from S, even though the public cross-attention contract allows different query and context lengths?

The test avoids this by setting L == S. Should we support independent query/key frequency slices, or explicitly reject and document after-cache RoPE for static cross-attention?


write_end = kv_cache.write_end
write_start = write_end - kv_cache.chunk_size
return rope_freqs[write_start:write_end], rope_freqs[: kv_cache.size]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a static cache created by compute_kv(), chunk_size equals the context length S, so this produces S query frequencies. _apply_rope() then rejects a query with length L != S, but I think the public cross-attention contract permits different query and context lengths. Should we support separate query/key frequency slices or explicitly reject and document after-cache RoPE for static cross-attention?

self.dtype = dtype
self.register_buffer(
"weight_scale", weight_scale.contiguous(), persistent=False
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For FP8, both self.weight and weight_scale are floating-point buffers, so something like layer.to(dtype=torch.bfloat16) converts the FP8 weight and FP32 scale to BF16 while self.dtype still advertises FP8.

The optimized MHA rebuilds these derived buffers in its own _apply(), does this class need equivalent protection? Do we need to preserve or rebuild these buffers, or explicitly reject dtype casts, and add a direct regression test?

return output

current_dtype = scales[0].dtype
for index, scale in enumerate(scales):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could multiple scales be applied in one kernel, or could we benchmark whether that is worthwhile?

This loop allocates an intermediate tensor and launches a full-tensor multiply kernel for every scale. The INT8 linear path always supplies activation and weight scales, so it currently performs two dequantization passes after the GEMM. It would be useful to know whether fusing those scales materially improves the end-to-end result.

use_tma=True,
quantization=QuantizationOption(projection=torch.float8_e4m3fn),
),
),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this case also set minimum_compute_capability=(9, 0)? OptimizedMultiHeadAttention explicitly rejects CUDA devices below compute capability 9.0, but this case currently passes skip_unsupported_device() on SM8.x. The benchmark would then fail during execution instead of skipping cleanly, contrary to the README’s stated behavior. The FA2 optimized case below already declares this requirement.

@jarcherNV

Copy link
Copy Markdown
Collaborator

/ok to test 97dfa44

or config.quantization.projection is not None
)
for config in (self_config, cross_config)
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should every non-None optimized configuration require compute capability 9.0 here? OptimizedMultiHeadAttention._validate_device() rejects devices below 9.0 regardless of the selected SDPA backend or projection dtype. As written, a plain optimized cuDNN configuration without FP8 projection gets no minimum capability, passes skip_unsupported_device() on SM8.x, and then fails during execution instead of skipping cleanly.

_block_case(self_config, cross_config)
for self_config in _MODULE_SELF_ATTENTION_CONFIGS
for cross_config in _MODULE_CROSS_ATTENTION_CONFIGS
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need all of this for the full-block benchmark? There are 37 self-attention and 25 cross-attention choices, so this creates 925 block benchmark cases, each with five warmups and 50 measured rounds, before the 62 isolated attention cases run. Some configurations are also effectively redundant, such as varying TMA under cuDNN. Could the full-block benchmark use representative self/cross pairs while keeping the exhaustive policy sweep isolated or opt-in?

patch_volume = network_config.patch_temporal * network_config.patch_spatial**2
tokens_per_frame = patch_h * patch_w
chunk_tokens = patch_t * tokens_per_frame
_WINDOW_SIZE_T * tokens_per_frame

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how this line got in there.


def teardown_generate() -> None:
nonlocal next_chunk_index
pipeline.finalize(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is finalize() intended to be included in the reported full-pipeline latency? Because it is invoked through the benchmark teardown, its execution is excluded from the measured target. Finalization performs additional transformer work, so this may understate recurring per-chunk latency. Could we include it in the timed operation, or report generation and finalization separately? It will affect the total fps after all.

self_attn_optimized_impl_config=OptimizedImplConfig(
qkv_fusion_option=QKVFusionOption.FULL,
sdpa_backend=SDPABackend.FA2,
),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the quantization policy missing from this preset? The description says it uses FP8 projections, but both OptimizedImplConfig instances retain the default quantization, where projection=None and quantized_sdpa=False. As written, the projections remain at the model’s native precision rather than FP8. Should we configure the intended quantization explicitly for both attention branches, or update the description if native precision is intentional?

pipeline=derive_config(
SV_2STEPS_CHUNK2_LOC6_LIGHTVAE_LIGHTTAE_PERF,
diffusion_model=dict(transformer=dict(skip_finalize_kv_cache=True)),
),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is skipping the final KV-cache refresh intentional for all public accelerated runners? Finalization is not merely cleanup: it re-noises the generated latent and runs finalize_kv_cache() to prepare the context for the next autoregressive chunk. This file otherwise treats skipping that operation as an experimental ablation, so enabling it here, and repeatedly in the runners below, changes model behavior and may affect rollout quality. Should we retain normal finalization, or document and justify this tradeoff with quality results?

assert network_config.cross_attention_backend is AttentionBackend.OPTIMIZED
assert (
network_config.self_attn_optimized_impl_config.sdpa_backend is SDPABackend.FA2
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this test also assert the preset’s advertised quantization policy? It currently verifies only the FA2 backend and fusion options, so it passes while both OptimizedImplConfig instances default to projection=None and quantized_sdpa=False, despite the preset being documented as using FP8 projections. Should we also assert the intended QuantizationOption for both branches, or compare these policies with the selected RTX PRO 6000 benchmark case?

matrix,
aspect="auto",
cmap="RdYlGn_r",
norm=CenteredNorm(vcenter=1.0),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the runtime ratios be transformed to log space before applying the diverging color scale?

CenteredNorm(vcenter=1.0) treats ratios additively, so multiplicatively equivalent changes such as 0.5x and 2x receive different color intensities. A very slow outlier can also determine the range and wash out all useful speedups. Using log2(runtime / reference) centered at zero would represent equivalent speedups and slowdowns symmetrically.

if reference is None:
return f"{value:.2f} ms\nreference unavailable"
if value < reference:
return f"{value:.2f} ms\n{(1 - value / reference) * 100:.0f}% faster"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This formula calculates the percentage reduction in latency, rather than percentage "faster." For example, half the runtime is displayed as 50% faster, although it represents a 2X speedup. Could these labels say % lower/higher latency, or report multiplicative speedup instead, so the benchmark result is unambiguous?

)
axes.set_xlabel("GEMM configuration")
axes.set_ylabel("Source dtype")
axes.set_title(f"Quantized GEMM {_SCOPE_LABELS[timing_scope]}\n{subtitle}")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gemm-only plot ranks these configurations as though they produce equivalent results, but INT8 measures raw torch._int_mm output as INT32 without dequantization, while FP8 still applies its scales and full precision returns the source dtype. Should we make these different output semantics explicit, or avoid presenting a single “fastest quantized config” for this scope? Otherwise the comparison could be interpreted as an apples-to-apples speedup.

matrix,
aspect="auto",
cmap="RdYlGn_r",
norm=CenteredNorm(vcenter=1.0),

@jarcherNV jarcherNV Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the comment I left in the other file.

if reference is None:
return f"{value:.2f} ms\nreference unavailable"
if value < reference:
return f"{value:.2f} ms\n{(1 - value / reference) * 100:.0f}% faster"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same faster comment as the one I left for the other file.

if reference is None:
return f"{value:.2f} ms\nreference unavailable"
if value < reference:
return f"{value:.2f} ms\n{(1 - value / reference) * 100:.0f}% faster"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as the faster one from the other files.

matrix,
aspect="auto",
cmap="RdYlGn_r",
norm=CenteredNorm(vcenter=1.0),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the comment from the other files.

np.asarray(matrix),
aspect="auto",
cmap="RdYlGn_r",
norm=CenteredNorm(vcenter=1.0),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above files.

r"(?:optimized|triton)-(cudnn|fa2)-(bf16)-self-(none|full|fuse-kv)-"
r"(no-tma|tma)-cross-(none|fuse-kv)-(no-tma|tma)",
implementation,
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these label parsers accept the identifiers produced by the current benchmarks?

Module configurations ending in -quantized-sdpa miss this pattern, while the selected end-to-end cases use optimized-cudnn-fp8-... and optimized-fa2-quantized-sdpa-..., neither of which matches the patterns below.

Do these configurations silently fall back to raw parameter text? Should the parsing be updated so all current benchmark cases receive accurate structured labels?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this also applies to line 196.

@jarcherNV

Copy link
Copy Markdown
Collaborator

Could we add a document summarizing the performance findings from this work so far? The benchmark scripts explain how to generate measurements, but it would be useful to have a durable summary of what was learned: the hardware and workloads tested, baseline and optimized results, which optimizations were beneficial at each layer, and any important tradeoffs or limitations. It does not need to be exhaustive, but it would provide context for the configurations selected in the OmniDreams integration and give future changes a reference point.

Also, the PR will need to be rebased, and the CI failures will need to be fixed. Other than that, this is looking pretty good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants