Integrate guarded FC2 cuBLASLt schedule
This commit is contained in:
parent
0298ccd1cb
commit
a29b8960b0
21 changed files with 8879 additions and 60 deletions
|
|
@ -160,7 +160,8 @@ video and audio tensors. Spark enables the path with
|
||||||
`SAGE2_BLACKWELL_DESIGN.md`, and
|
`SAGE2_BLACKWELL_DESIGN.md`, and
|
||||||
`benchmarks/gb10-post-optimization-profile-summary.json`.
|
`benchmarks/gb10-post-optimization-profile-summary.json`.
|
||||||
|
|
||||||
The isolated FC2 cuBLASLt scheduling study is complete. The production
|
The FC2 cuBLASLt scheduling study and guarded canonical-shape integration are
|
||||||
|
complete. The production
|
||||||
heuristic's `_stream_k` kernel requests the same `25.664 GB` of operands as the
|
heuristic's `_stream_k` kernel requests the same `25.664 GB` of operands as the
|
||||||
retained public split-K-1 schedule, but its L2 hit rate is only `53.32%` versus
|
retained public split-K-1 schedule, but its L2 hit rate is only `53.32%` versus
|
||||||
`91.10%`; it incurs `9.853 GB` more L2 read misses and spends heavily in
|
`91.10%`; it incurs `9.853 GB` more L2 read misses and spends heavily in
|
||||||
|
|
@ -168,8 +169,15 @@ synchronization polling. Algorithm 70, tile 20, stages 37, split-K 1 is
|
||||||
byte-exact with zero workspace. It improves complete blocks 0, 24, and 49 by
|
byte-exact with zero workspace. It improves complete blocks 0, 24, and 49 by
|
||||||
`8.16-8.88%`, the two-step trajectory by `7.50%`, and the canonical 12-step
|
`8.16-8.88%`, the two-step trajectory by `7.50%`, and the canonical 12-step
|
||||||
trajectory from `278.201 s` to `255.371 s` (`8.21%`) with exact video and audio
|
trajectory from `278.201 s` to `255.371 s` (`8.21%`) with exact video and audio
|
||||||
latents. This remains a research-retained integration candidate: production
|
latents. The final hardened production method improves 20-round blocks by
|
||||||
dispatch and configuration are unchanged. See
|
`7.86-9.36%` and the canonical 12-step trajectory from `286.431 s` to
|
||||||
|
`262.979 s` (`8.19%`), with 600 successful dispatches, zero fallback, and exact
|
||||||
|
latents. `H3_NVFP4_FC2_LT_SPLITK1=1` enables only the validated `M=37,810`
|
||||||
|
descriptor; nearby row counts can differ by two BF16 elements and therefore
|
||||||
|
retain the existing Comfy fallback. The extension and measured runtime ABI are
|
||||||
|
prepared during H3 model loading rather than on the first canonical request.
|
||||||
|
The Spark hot service is intentionally
|
||||||
|
stopped until performance work is complete. See
|
||||||
`research/fc2_nvfp4_scheduling/RESULTS.md`.
|
`research/fc2_nvfp4_scheduling/RESULTS.md`.
|
||||||
|
|
||||||
The Spark hot runtime was rebuilt and recreated with image
|
The Spark hot runtime was rebuilt and recreated with image
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ RUN python -m pip install --no-cache-dir --no-deps -e . \
|
||||||
|
|
||||||
ENV TORCH_EXTENSIONS_DIR=/opt/h3-blackwell-runtime/.torch_extensions
|
ENV TORCH_EXTENSIONS_DIR=/opt/h3-blackwell-runtime/.torch_extensions
|
||||||
RUN TORCH_CUDA_ARCH_LIST=12.1 python -c \
|
RUN TORCH_CUDA_ARCH_LIST=12.1 python -c \
|
||||||
"from h3_blackwell_runtime.nvfp4_quant import _vortex_scale_extension; print(_vortex_scale_extension().__file__)"
|
"from h3_blackwell_runtime.nvfp4_quant import _vortex_scale_extension; from h3_blackwell_runtime.fc2_lt import _fc2_lt_extension; print(_vortex_scale_extension().__file__, _fc2_lt_extension().__file__)"
|
||||||
|
|
||||||
ENV H3_MODEL_PATH=/models/minimax_h3_ref2va_pruned_nvfp4.safetensors
|
ENV H3_MODEL_PATH=/models/minimax_h3_ref2va_pruned_nvfp4.safetensors
|
||||||
ENV PYTHONPATH=/opt/ComfyUI-sol-attn
|
ENV PYTHONPATH=/opt/ComfyUI-sol-attn
|
||||||
|
|
|
||||||
|
|
@ -394,12 +394,18 @@ tensor-pipe activity from `25.01%` to `82.42%`. There is no material global
|
||||||
partial-accumulator or output-reduction traffic; the baseline instead spends
|
partial-accumulator or output-reduction traffic; the baseline instead spends
|
||||||
heavily in Stream-K synchronization polling and loses traversal locality.
|
heavily in Stream-K synchronization polling and loses traversal locality.
|
||||||
|
|
||||||
The candidate is byte-exact and improves FC2 p50 from `52.521 ms` to
|
The candidate is byte-exact and improves FC2 p50 from `53.618 ms` to
|
||||||
`15.636 ms`. Complete blocks 0, 24, and 49 improve by `8.16-8.88%`; two-step
|
`15.636 ms`. Complete blocks 0, 24, and 49 improve by `8.16-8.88%`; two-step
|
||||||
and canonical 12-step trajectories improve by `7.50%` and `8.21%`, with exact
|
and canonical 12-step trajectories improve by `7.50%` and `8.21%`, with exact
|
||||||
video and audio latents. The custom persistent-kernel branch is therefore
|
video and audio latents. The custom persistent-kernel branch is therefore
|
||||||
closed. Production integration remains separate work, so current dispatch is
|
closed. Guarded production integration is complete behind
|
||||||
unchanged. See `research/fc2_nvfp4_scheduling/RESULTS.md` and
|
`H3_NVFP4_FC2_LT_SPLITK1=1`. Its direct 20-round block gate improves blocks
|
||||||
|
0/24/49 by `9.36%`, `8.16%`, and `7.86%`; the exact canonical 12-step gate
|
||||||
|
improves by `8.19%`
|
||||||
|
with 600 successful dispatches and zero fallback. Shape testing found two-BF16
|
||||||
|
differences at several nearby row counts, so only `M=37,810` is eligible and
|
||||||
|
all variable shapes retain Comfy. The hot service remains stopped until the
|
||||||
|
performance program is complete. See `research/fc2_nvfp4_scheduling/RESULTS.md` and
|
||||||
`benchmarks/gb10-fc2-nvfp4-library-sweep-20260825.json`.
|
`benchmarks/gb10-fc2-nvfp4-library-sweep-20260825.json`.
|
||||||
|
|
||||||
The follow-up real block-24 Sage2 decomposition now selects the next exact
|
The follow-up real block-24 Sage2 decomposition now selects the next exact
|
||||||
|
|
|
||||||
2025
benchmarks/gb10-fc2-nvfp4-production-block-gate-20260825.json
Normal file
2025
benchmarks/gb10-fc2-nvfp4-production-block-gate-20260825.json
Normal file
File diff suppressed because it is too large
Load diff
1776
benchmarks/gb10-fc2-nvfp4-production-trajectory-12step-20260825.json
Normal file
1776
benchmarks/gb10-fc2-nvfp4-production-trajectory-12step-20260825.json
Normal file
File diff suppressed because it is too large
Load diff
1776
benchmarks/gb10-fc2-nvfp4-production-trajectory-2step-20260825.json
Normal file
1776
benchmarks/gb10-fc2-nvfp4-production-trajectory-2step-20260825.json
Normal file
File diff suppressed because it is too large
Load diff
2404
benchmarks/gb10-fc2-nvfp4-shape-gate-20260825.json
Normal file
2404
benchmarks/gb10-fc2-nvfp4-shape-gate-20260825.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -20,6 +20,7 @@ services:
|
||||||
H3_FUSED_ELEMENTWISE: "1"
|
H3_FUSED_ELEMENTWISE: "1"
|
||||||
H3_NVFP4_MODULATE_FUSION: "1"
|
H3_NVFP4_MODULATE_FUSION: "1"
|
||||||
H3_NVFP4_SWIGLU_FUSION: "1"
|
H3_NVFP4_SWIGLU_FUSION: "1"
|
||||||
|
H3_NVFP4_FC2_LT_SPLITK1: "1"
|
||||||
H3_SAGE_QKV_LAYOUT: "strided_nhd"
|
H3_SAGE_QKV_LAYOUT: "strided_nhd"
|
||||||
H3_SOL_QKV_LAYOUT: "native"
|
H3_SOL_QKV_LAYOUT: "native"
|
||||||
command: ["python", "/opt/h3-blackwell-runtime/tools/serve_hot_runtime.py", "--host", "0.0.0.0", "--port", "8000", "--attention", "sage2", "--warmup"]
|
command: ["python", "/opt/h3-blackwell-runtime/tools/serve_hot_runtime.py", "--host", "0.0.0.0", "--port", "8000", "--attention", "sage2", "--warmup"]
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ services:
|
||||||
H3_FUSED_ELEMENTWISE: "1"
|
H3_FUSED_ELEMENTWISE: "1"
|
||||||
H3_NVFP4_MODULATE_FUSION: "1"
|
H3_NVFP4_MODULATE_FUSION: "1"
|
||||||
H3_NVFP4_SWIGLU_FUSION: "1"
|
H3_NVFP4_SWIGLU_FUSION: "1"
|
||||||
|
H3_NVFP4_FC2_LT_SPLITK1: "1"
|
||||||
H3_SAGE_QKV_LAYOUT: "strided_nhd"
|
H3_SAGE_QKV_LAYOUT: "strided_nhd"
|
||||||
H3_SOL_QKV_LAYOUT: "native"
|
H3_SOL_QKV_LAYOUT: "native"
|
||||||
command: ["python", "/opt/h3-blackwell-runtime/tools/serve_hot_runtime.py", "--host", "0.0.0.0", "--port", "8000", "--attention", "sage2", "--turbo-lora", "4step=/turbo/minimax_h3_fl2v_turbo_4step_v1.1_768p_comfyui_bf16.safetensors", "--turbo-lora", "8step=/turbo/minimax_h3_fl2v_turbo_8step_v1.0_comfyui_bf16.safetensors", "--latent-upscaler", "/latent-upscaler/minimax_h3_latent_upscaler_3d_fp16.safetensors", "--warmup"]
|
command: ["python", "/opt/h3-blackwell-runtime/tools/serve_hot_runtime.py", "--host", "0.0.0.0", "--port", "8000", "--attention", "sage2", "--turbo-lora", "4step=/turbo/minimax_h3_fl2v_turbo_4step_v1.1_768p_comfyui_bf16.safetensors", "--turbo-lora", "8step=/turbo/minimax_h3_fl2v_turbo_8step_v1.0_comfyui_bf16.safetensors", "--latent-upscaler", "/latent-upscaler/minimax_h3_latent_upscaler_3d_fp16.safetensors", "--warmup"]
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,6 @@ flash4 = ["flash-attn-4[cu13]==4.0.0b27"]
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["src"]
|
where = ["src"]
|
||||||
|
|
||||||
|
[tool.setuptools.package-data]
|
||||||
|
h3_blackwell_runtime = ["csrc/*.cpp", "csrc/*.cu"]
|
||||||
|
|
|
||||||
|
|
@ -2,36 +2,36 @@
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"algorithm": "SHA-256",
|
"algorithm": "SHA-256",
|
||||||
"generated_date": "2026-08-25",
|
"generated_date": "2026-08-25",
|
||||||
"scope_note": "Local selected scope is the established 167-file set plus 10 retained FC2 scheduling artifacts. Spark records are the complete reproducible current top-level benchmark output set; the audit-retained 279-file/665950155-byte aggregate cannot be reconstructed because its path list was not retained.",
|
"scope_note": "Local selected scope is the established 167-file set plus 14 retained FC2 scheduling and integration artifacts. Spark records are the complete reproducible current top-level benchmark output set; the audit-retained 279-file/665950155-byte aggregate cannot be reconstructed because its path list was not retained.",
|
||||||
"summary": {
|
"summary": {
|
||||||
"local": {
|
"local": {
|
||||||
"record_count": 177,
|
"record_count": 181,
|
||||||
"size_bytes": 230157548,
|
"size_bytes": 230398680,
|
||||||
"expected_record_count": 177,
|
"expected_record_count": 181,
|
||||||
"expected_size_bytes": 230157548,
|
"expected_size_bytes": 230398680,
|
||||||
"reconciled": true
|
"reconciled": true
|
||||||
},
|
},
|
||||||
"spark": {
|
"spark": {
|
||||||
"record_count": 290,
|
"record_count": 294,
|
||||||
"size_bytes": 567477443,
|
"size_bytes": 567718575,
|
||||||
"expected_record_count": 279,
|
"expected_record_count": 279,
|
||||||
"expected_size_bytes": 665950155,
|
"expected_size_bytes": 665950155,
|
||||||
"reconciled": false,
|
"reconciled": false,
|
||||||
"record_count_delta": 11,
|
"record_count_delta": 15,
|
||||||
"size_bytes_delta": -98472712
|
"size_bytes_delta": -98231580
|
||||||
},
|
},
|
||||||
"total": {
|
"total": {
|
||||||
"record_count": 467,
|
"record_count": 475,
|
||||||
"size_bytes": 797634991
|
"size_bytes": 798117255
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"json_reconciliation": {
|
"json_reconciliation": {
|
||||||
"identical": 112,
|
"identical": 118,
|
||||||
"mismatches": 2,
|
"mismatches": 0,
|
||||||
"local_only": 40,
|
"local_only": 40,
|
||||||
"spark_only": 107,
|
"spark_only": 107,
|
||||||
"local_total": 154,
|
"local_total": 158,
|
||||||
"spark_total": 221
|
"spark_total": 225
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
|
|
@ -195,6 +195,70 @@
|
||||||
"artifact_class": "benchmark_json",
|
"artifact_class": "benchmark_json",
|
||||||
"location": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-trajectory-2step-20260825.json"
|
"location": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-trajectory-2step-20260825.json"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"scope": "local",
|
||||||
|
"path": "benchmarks/gb10-fc2-nvfp4-production-block-gate-20260825.json",
|
||||||
|
"size_bytes": 61575,
|
||||||
|
"sha256": "a49664031af6c21a341cfb0787000e54801c4067cf8ad231cc614c11de251067",
|
||||||
|
"artifact_class": "benchmark_json",
|
||||||
|
"location": "benchmarks/gb10-fc2-nvfp4-production-block-gate-20260825.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "local",
|
||||||
|
"path": "benchmarks/gb10-fc2-nvfp4-production-trajectory-2step-20260825.json",
|
||||||
|
"size_bytes": 53189,
|
||||||
|
"sha256": "3e0e42addf5a748462f1bc600d5bb08875b31da5177d2af2b2abddfb1078ebd3",
|
||||||
|
"artifact_class": "benchmark_json",
|
||||||
|
"location": "benchmarks/gb10-fc2-nvfp4-production-trajectory-2step-20260825.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "local",
|
||||||
|
"path": "benchmarks/gb10-fc2-nvfp4-production-trajectory-12step-20260825.json",
|
||||||
|
"size_bytes": 53192,
|
||||||
|
"sha256": "94a915acee1bf3d3c23a934f93bafc583ac2ae0f3e91ff1277ee57f628e2d254",
|
||||||
|
"artifact_class": "benchmark_json",
|
||||||
|
"location": "benchmarks/gb10-fc2-nvfp4-production-trajectory-12step-20260825.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "local",
|
||||||
|
"path": "benchmarks/gb10-fc2-nvfp4-shape-gate-20260825.json",
|
||||||
|
"size_bytes": 73176,
|
||||||
|
"sha256": "cbde2aab33ceb9df32e65c8649838dcdf47e0e135aaaae4165b2f9b6e8d2734b",
|
||||||
|
"artifact_class": "benchmark_json",
|
||||||
|
"location": "benchmarks/gb10-fc2-nvfp4-shape-gate-20260825.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "spark",
|
||||||
|
"path": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-production-block-gate-20260825.json",
|
||||||
|
"size_bytes": 61575,
|
||||||
|
"sha256": "a49664031af6c21a341cfb0787000e54801c4067cf8ad231cc614c11de251067",
|
||||||
|
"artifact_class": "benchmark_json",
|
||||||
|
"location": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-production-block-gate-20260825.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "spark",
|
||||||
|
"path": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-production-trajectory-2step-20260825.json",
|
||||||
|
"size_bytes": 53189,
|
||||||
|
"sha256": "3e0e42addf5a748462f1bc600d5bb08875b31da5177d2af2b2abddfb1078ebd3",
|
||||||
|
"artifact_class": "benchmark_json",
|
||||||
|
"location": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-production-trajectory-2step-20260825.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "spark",
|
||||||
|
"path": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-production-trajectory-12step-20260825.json",
|
||||||
|
"size_bytes": 53192,
|
||||||
|
"sha256": "94a915acee1bf3d3c23a934f93bafc583ac2ae0f3e91ff1277ee57f628e2d254",
|
||||||
|
"artifact_class": "benchmark_json",
|
||||||
|
"location": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-production-trajectory-12step-20260825.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scope": "spark",
|
||||||
|
"path": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-shape-gate-20260825.json",
|
||||||
|
"size_bytes": 73176,
|
||||||
|
"sha256": "cbde2aab33ceb9df32e65c8649838dcdf47e0e135aaaae4165b2f9b6e8d2734b",
|
||||||
|
"artifact_class": "benchmark_json",
|
||||||
|
"location": "/home/daniel/StoryStudioAssets/H3-output/h3-blackwell-runtime/benchmarks/gb10-fc2-nvfp4-shape-gate-20260825.json"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"scope": "local",
|
"scope": "local",
|
||||||
"path": "benchmarks/audio-dialogue-format-sweep-sage2-seeds440420-440429.json",
|
"path": "benchmarks/audio-dialogue-format-sweep-sage2-seeds440420-440429.json",
|
||||||
|
|
|
||||||
|
|
@ -504,22 +504,22 @@
|
||||||
"id": "fc2-cublaslt-splitk1-schedule",
|
"id": "fc2-cublaslt-splitk1-schedule",
|
||||||
"name": "FC2 cuBLASLt public split-K-1 schedule",
|
"name": "FC2 cuBLASLt public split-K-1 schedule",
|
||||||
"family": "nvfp4-library-scheduling",
|
"family": "nvfp4-library-scheduling",
|
||||||
"status": "research_retained",
|
"status": "production_accepted",
|
||||||
"hypothesis": "A documented cuBLASLt schedule can preserve the exact FC2 reduction result while avoiding the production heuristic's traffic and synchronization regression.",
|
"hypothesis": "A documented cuBLASLt schedule can preserve the exact FC2 reduction result while avoiding the production heuristic's traffic and synchronization regression.",
|
||||||
"implementation_strategy": "Reproduce the exact Comfy Kitchen descriptors in an isolated extension, enumerate checked cuBLASLt algorithms, and compare one selected public split-K-1 schedule against the accepted FC2 path.",
|
"implementation_strategy": "Reproduce the exact Comfy Kitchen descriptors in an isolated extension, enumerate checked cuBLASLt algorithms, and compare one selected public split-K-1 schedule against the accepted FC2 path.",
|
||||||
"source_locations": ["research/fc2_nvfp4_scheduling/README.md", "research/fc2_nvfp4_scheduling/RESULTS.md", "research/fc2_nvfp4_scheduling/fc2_nvfp4_lt.cpp", "tools/benchmark_fc2_nvfp4_algorithms.py"],
|
"source_locations": ["research/fc2_nvfp4_scheduling/README.md", "research/fc2_nvfp4_scheduling/RESULTS.md", "src/h3_blackwell_runtime/csrc/fc2_nvfp4_lt.cpp", "tools/benchmark_fc2_nvfp4_algorithms.py"],
|
||||||
"active_source_location": "research/fc2_nvfp4_scheduling/fc2_nvfp4_lt.cpp",
|
"active_source_location": "src/h3_blackwell_runtime/csrc/fc2_nvfp4_lt.cpp",
|
||||||
"commit_hash": null,
|
"commit_hash": null,
|
||||||
"benchmark_artifacts": [{"path": "benchmarks/gb10-fc2-nvfp4-library-sweep-20260825.json", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-block-gate-20260825.json", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-trajectory-2step-20260825.json", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-trajectory-12step-20260825.json", "exists": true}],
|
"benchmark_artifacts": [{"path": "benchmarks/gb10-fc2-nvfp4-library-sweep-20260825.json", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-production-block-gate-20260825.json", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-production-trajectory-2step-20260825.json", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-production-trajectory-12step-20260825.json", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-shape-gate-20260825.json", "exists": true}],
|
||||||
"profiler_artifacts": [{"path": "benchmarks/gb10-fc2-nvfp4-baseline-20260825.ncu-rep", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-splitk1-20260825.ncu-rep", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-baseline-20260825.csv", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-splitk1-20260825.csv", "exists": true}],
|
"profiler_artifacts": [{"path": "benchmarks/gb10-fc2-nvfp4-baseline-20260825.ncu-rep", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-splitk1-20260825.ncu-rep", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-baseline-20260825.csv", "exists": true}, {"path": "benchmarks/gb10-fc2-nvfp4-splitk1-20260825.csv", "exists": true}],
|
||||||
"environment": {"gpu": "NVIDIA GB10", "cuda": "13", "driver": null, "pytorch": null, "triton": null, "container_image": "sha256:1d340e14cb6fc45ccfdbe63dde8db2a2b3aea94b493702c8a08e1f8d5b4f7b83", "commit_context": "isolated extension; production source and dispatch unchanged"},
|
"environment": {"gpu": "NVIDIA GB10", "cuda": "13", "driver": null, "pytorch": "2.9.1+cu130", "triton": null, "container_image": "sha256:d61c8d29e8b59306d140bfd279b0de31d827ad13bd92447ea72ea0a04182494d", "commit_context": "guarded canonical production integration"},
|
||||||
"metrics": {"fc2_p50_ms_baseline": 53.618, "fc2_p50_ms_candidate": 15.636, "block_improvement_pct": [8.16, 8.63, 8.88], "two_step_improvement_pct": 7.50, "canonical_12_step_improvement_pct": 8.21, "trajectory_measurement_note": "single baseline-then-candidate paired runs", "l2_hit_rate_pct_baseline": 53.32, "l2_hit_rate_pct_candidate": 91.10, "l2_read_miss_reduction_bytes": 9853094784},
|
"metrics": {"fc2_p50_ms_baseline": 53.618, "fc2_p50_ms_candidate": 15.636, "production_block_improvement_pct": [9.36, 8.16, 7.86], "production_two_step_seconds": {"baseline": 47.30056222799976, "candidate": 43.727866895999796, "improvement_percent": 7.553177306389591}, "production_canonical_12_step_seconds": {"baseline": 286.43065266899976, "candidate": 262.97871506399997, "improvement_percent": 8.187649396624085}, "production_12_step_dispatches": 600, "production_12_step_fallbacks": 0, "l2_hit_rate_pct_baseline": 53.32, "l2_hit_rate_pct_candidate": 91.10, "l2_read_miss_reduction_bytes": 9853094784},
|
||||||
"correctness_evidence": ["Selected FC2 output is byte-exact.", "Blocks 0, 24, and 49 are byte-exact against paired baseline and retained traversal.", "Two-step and canonical 12-step video and audio latents are byte-exact."],
|
"correctness_evidence": ["Production blocks 0, 24, and 49 are byte-exact against paired baseline and retained traversal.", "Production two-step and canonical 12-step video and audio latents are byte-exact.", "Nearby M=37711/37725/37742 shape probes differ in two BF16 elements and are excluded by the canonical guard."],
|
||||||
"decision_rationale": "The library schedule passed all exactness and performance gates, explaining the prior traffic amplification without requiring a custom kernel. It is retained pending explicit production integration and deployment validation.",
|
"decision_rationale": "The shape-guarded library schedule passed production-method block and trajectory gates without requiring a custom kernel. Noncanonical shapes retain the accepted fallback because AlgoCheck alone does not guarantee byte exactness.",
|
||||||
"reproducer_commands": ["python tools/benchmark_fc2_nvfp4_algorithms.py --mode sweep --rounds 20 --workspace-bytes 67108864", "python tools/benchmark_fc2_nvfp4_algorithms.py --mode block-gate --candidate research/fc2_nvfp4_scheduling/candidate_splitk1.json --rounds 20 --workspace-bytes 0"],
|
"reproducer_commands": ["python tools/benchmark_fc2_nvfp4_algorithms.py --mode production-block-gate --candidate research/fc2_nvfp4_scheduling/candidate_splitk1.json --rounds 20 --workspace-bytes 0", "python tools/benchmark_fc2_nvfp4_algorithms.py --mode production-trajectory --candidate research/fc2_nvfp4_scheduling/candidate_splitk1.json --steps 12 --workspace-bytes 0", "python tools/benchmark_fc2_nvfp4_algorithms.py --mode shape-gate --candidate research/fc2_nvfp4_scheduling/candidate_splitk1.json --workspace-bytes 0"],
|
||||||
"timestamp": "2026-08-25",
|
"timestamp": "2026-08-25",
|
||||||
"evidence_missing": ["Production Nvfp4Linear integration", "Production deployment smoke", "Portable validation outside GB10/SM121"],
|
"evidence_missing": ["Resident-service deployment validation deferred until performance work completes", "Trajectory timings are single baseline-then-candidate pairs rather than repeated AB/BA trials", "Shape-gate artifact predates runtime-version fields in build_info", "Portable validation outside GB10/SM121"],
|
||||||
"production_behavior": "Not dispatched. Production still uses Comfy Kitchen's heuristic-selected FC2 path.",
|
"production_behavior": "H3_NVFP4_FC2_LT_SPLITK1 selects algorithm 70 only for the validated canonical FC2 descriptor; every other shape, device, library, LoRA, and failure case falls back to Comfy Kitchen.",
|
||||||
"source_recovery": "The direct cuBLASLt extension, benchmark harness, selected candidate, raw NCU reports, and full gate artifacts are retained in this checkout."
|
"source_recovery": "The direct cuBLASLt extension, benchmark harness, selected candidate, raw NCU reports, and full gate artifacts are retained in this checkout."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
# FC2 NVFP4 Library Scheduling
|
# FC2 NVFP4 Library Scheduling
|
||||||
|
|
||||||
This directory contains the first isolated library-scheduling study for H3 FC2.
|
This directory contains the first isolated library-scheduling study for H3 FC2.
|
||||||
It does not register a PyTorch operator, alter `Nvfp4Linear`, or participate in
|
The validated extension source moved to
|
||||||
production dispatch. The extension is loaded only by
|
`src/h3_blackwell_runtime/csrc/fc2_nvfp4_lt.cpp` when the guarded production
|
||||||
`tools/benchmark_fc2_nvfp4_algorithms.py`.
|
integration was added. The research harness and production loader share that
|
||||||
|
source.
|
||||||
|
|
||||||
The completed measurements and decision are in `RESULTS.md`. Algorithm 70 with
|
The completed measurements and decision are in `RESULTS.md`. Algorithm 70 with
|
||||||
public split-K 1 is byte-exact and passes the block, two-step, and canonical
|
public split-K 1 is byte-exact and passes the block, two-step, and canonical
|
||||||
12-step gates, but remains research-only until production integration and
|
12-step gates. Canonical-only production integration is complete behind an
|
||||||
deployment validation are performed.
|
opt-in flag; resident-service deployment validation remains deferred.
|
||||||
|
|
||||||
## Exact Operation
|
## Exact Operation
|
||||||
|
|
||||||
|
|
@ -175,6 +176,10 @@ latents; see `RESULTS.md` and the linked benchmark artifacts.
|
||||||
|
|
||||||
## Prohibited Experiments
|
## Prohibited Experiments
|
||||||
|
|
||||||
|
These restrictions governed the isolated study. Production integration was
|
||||||
|
authorized only after the library candidate passed the complete validation
|
||||||
|
ladder; the canonical-only guard and automatic fallback preserve that boundary.
|
||||||
|
|
||||||
- Do not alter production source, configuration, dispatch, or existing
|
- Do not alter production source, configuration, dispatch, or existing
|
||||||
research files.
|
research files.
|
||||||
- Do not expose this extension through `Nvfp4Linear` or any production operator.
|
- Do not expose this extension through `Nvfp4Linear` or any production operator.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# FC2 NVFP4 Scheduling Results
|
# FC2 NVFP4 Scheduling Results
|
||||||
|
|
||||||
Status: research retained; validated as a canonical-shape production-integration
|
Status: production accepted for the canonical descriptor shape behind
|
||||||
candidate, but not wired into production dispatch.
|
`H3_NVFP4_FC2_LT_SPLITK1`; all other shapes retain the existing fallback.
|
||||||
|
|
||||||
## Decision
|
## Decision
|
||||||
|
|
||||||
|
|
@ -70,13 +70,52 @@ traversal.
|
||||||
| Canonical 12-step | `278.201 s` | `255.371 s` | `8.21%` | video/audio BF16 exact |
|
| Canonical 12-step | `278.201 s` | `255.371 s` | `8.21%` | video/audio BF16 exact |
|
||||||
|
|
||||||
Trajectory values are single paired runs in baseline-then-candidate
|
Trajectory values are single paired runs in baseline-then-candidate
|
||||||
order, not repeated medians. Their approximately `38 ms` per-FC2 savings agree
|
order, not repeated medians or AB/BA trials. Their approximately `38 ms` per-FC2 savings agree
|
||||||
with the alternating isolated and complete-block gates, but the precise
|
with the alternating isolated and complete-block gates, but the precise
|
||||||
end-to-end percentages retain run-order uncertainty.
|
end-to-end percentages retain run-order uncertainty.
|
||||||
|
|
||||||
The 12-step saving of `22.829 s` matches approximately 600 FC2 invocations
|
The 12-step saving of `22.829 s` matches approximately 600 FC2 invocations
|
||||||
multiplied by the isolated roughly `38 ms` saving.
|
multiplied by the isolated roughly `38 ms` saving.
|
||||||
|
|
||||||
|
## Production Integration
|
||||||
|
|
||||||
|
The packaged path lives in `src/h3_blackwell_runtime/fc2_lt.py` and is called
|
||||||
|
from `Nvfp4Linear.forward_swiglu` only after the accepted native producer. It
|
||||||
|
requires the FC2 role, unsharded `K=14,336/N=5,376`, BF16 output, no bias or
|
||||||
|
active LoRA, GB10/SM121, CUDA 13.0, cuBLAS 13.1 headers, cuBLASLt 13.0 runtime,
|
||||||
|
logical `M=37,810`, and packed
|
||||||
|
`M=37,824`. A shape-specific `cublasLtMatmulAlgoCheck` must pass with zero
|
||||||
|
workspace. Load, check, or execution failure falls back through the already
|
||||||
|
produced NVFP4 bytes without quantizing twice; strict mode is available through
|
||||||
|
`H3_NVFP4_FC2_LT_STRICT=1`. Production startup prepares the extension and
|
||||||
|
caches its measured runtime compatibility result before inference.
|
||||||
|
|
||||||
|
The 20-round production-method gate exercised `Nvfp4Linear.forward_swiglu`
|
||||||
|
directly and recorded 23 successful dispatches with zero fallbacks per block:
|
||||||
|
|
||||||
|
| Block | Baseline p50 | Production p50 | Improvement |
|
||||||
|
| ---: | ---: | ---: | ---: |
|
||||||
|
| 0 | `464.153 ms` | `420.714 ms` | `9.36%` |
|
||||||
|
| 24 | `460.184 ms` | `422.656 ms` | `8.16%` |
|
||||||
|
| 49 | `456.183 ms` | `420.335 ms` | `7.86%` |
|
||||||
|
|
||||||
|
Production-method trajectories remained exact:
|
||||||
|
|
||||||
|
| Trajectory | Baseline | Production | Improvement | Dispatch |
|
||||||
|
| --- | ---: | ---: | ---: | --- |
|
||||||
|
| Two-step | `47.301 s` | `43.728 s` | `7.55%` | `100/100`, zero fallback |
|
||||||
|
| Canonical 12-step | `286.431 s` | `262.979 s` | `8.19%` | `600/600`, zero fallback |
|
||||||
|
|
||||||
|
The row-shape gate prevents unsafe generalization. Algorithm 70 was exact for
|
||||||
|
the tested `M=37,774`, `37,809`, `37,810`, `37,838`, `37,966`, and `38,222`
|
||||||
|
inputs, but differed in two BF16 elements at each of `M=37,711`, `37,725`, and
|
||||||
|
`37,742` despite passing `AlgoCheck`. Production therefore intentionally keeps
|
||||||
|
the canonical-only guard rather than inferring exactness from nearby shapes.
|
||||||
|
The retained shape artifact records compile-time CUDA/cuBLAS versions but
|
||||||
|
predates runtime-version fields in `build_info()`. The final extension separately
|
||||||
|
measured CUDA runtime `13000` and cuBLASLt runtime `130000`; the shape artifact
|
||||||
|
alone is not runtime-ABI evidence.
|
||||||
|
|
||||||
## Traffic Attribution
|
## Traffic Attribution
|
||||||
|
|
||||||
The one-pass distinct-data footprint is:
|
The one-pass distinct-data footprint is:
|
||||||
|
|
@ -150,9 +189,12 @@ synchronization waiting, not occupancy or a reduction workspace.
|
||||||
- `benchmarks/gb10-fc2-nvfp4-splitk1-20260825.csv`
|
- `benchmarks/gb10-fc2-nvfp4-splitk1-20260825.csv`
|
||||||
- `benchmarks/gb10-fc2-nvfp4-baseline-20260825.ncu-rep`
|
- `benchmarks/gb10-fc2-nvfp4-baseline-20260825.ncu-rep`
|
||||||
- `benchmarks/gb10-fc2-nvfp4-splitk1-20260825.ncu-rep`
|
- `benchmarks/gb10-fc2-nvfp4-splitk1-20260825.ncu-rep`
|
||||||
|
- `benchmarks/gb10-fc2-nvfp4-production-block-gate-20260825.json`
|
||||||
|
- `benchmarks/gb10-fc2-nvfp4-production-trajectory-2step-20260825.json`
|
||||||
|
- `benchmarks/gb10-fc2-nvfp4-production-trajectory-12step-20260825.json`
|
||||||
|
- `benchmarks/gb10-fc2-nvfp4-shape-gate-20260825.json`
|
||||||
|
- `src/h3_blackwell_runtime/csrc/fc2_nvfp4_lt.cpp`
|
||||||
|
|
||||||
Production source and configuration were not changed by this study. The
|
The Spark hot runtime remains stopped while performance work continues.
|
||||||
candidate is validated only for the canonical descriptor shape and current
|
Resident-service validation is intentionally deferred until the performance
|
||||||
GB10/CUDA-library combination. Promotion still requires shape-specific
|
program is finished.
|
||||||
`AlgoCheck` with a safe fallback, integration behind the existing
|
|
||||||
`Nvfp4Linear` boundary, broader shape tests, and deployment validation.
|
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,22 @@ namespace py = pybind11;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
thread_local cublasLtHandle_t handle = nullptr;
|
struct LtHandle {
|
||||||
|
cublasLtHandle_t value = nullptr;
|
||||||
|
|
||||||
|
LtHandle() { LT_CHECK(cublasLtCreate(&value)); }
|
||||||
|
~LtHandle() {
|
||||||
|
if (value) cublasLtDestroy(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
cublasLtHandle_t get_handle() {
|
cublasLtHandle_t get_handle() {
|
||||||
if (!handle) LT_CHECK(cublasLtCreate(&handle));
|
static LtHandle handle;
|
||||||
return handle;
|
return handle.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void prepare() { get_handle(); }
|
||||||
|
|
||||||
void require_cuda_contiguous(const torch::Tensor& tensor, const char* name) {
|
void require_cuda_contiguous(const torch::Tensor& tensor, const char* name) {
|
||||||
TORCH_CHECK(tensor.is_cuda(), name, " must be CUDA");
|
TORCH_CHECK(tensor.is_cuda(), name, " must be CUDA");
|
||||||
TORCH_CHECK(tensor.is_contiguous(), name, " must be contiguous");
|
TORCH_CHECK(tensor.is_contiguous(), name, " must be contiguous");
|
||||||
|
|
@ -50,6 +59,19 @@ struct Problem {
|
||||||
const torch::Tensor& activation_block_scale,
|
const torch::Tensor& activation_block_scale,
|
||||||
const torch::Tensor& weight_qdata,
|
const torch::Tensor& weight_qdata,
|
||||||
const torch::Tensor& weight_block_scale) {
|
const torch::Tensor& weight_block_scale) {
|
||||||
|
try {
|
||||||
|
initialize(activation_qdata, activation_block_scale, weight_qdata,
|
||||||
|
weight_block_scale);
|
||||||
|
} catch (...) {
|
||||||
|
reset();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize(const torch::Tensor& activation_qdata,
|
||||||
|
const torch::Tensor& activation_block_scale,
|
||||||
|
const torch::Tensor& weight_qdata,
|
||||||
|
const torch::Tensor& weight_block_scale) {
|
||||||
require_cuda_contiguous(activation_qdata, "activation_qdata");
|
require_cuda_contiguous(activation_qdata, "activation_qdata");
|
||||||
require_cuda_contiguous(activation_block_scale, "activation_block_scale");
|
require_cuda_contiguous(activation_block_scale, "activation_block_scale");
|
||||||
require_cuda_contiguous(weight_qdata, "weight_qdata");
|
require_cuda_contiguous(weight_qdata, "weight_qdata");
|
||||||
|
|
@ -122,13 +144,17 @@ struct Problem {
|
||||||
LT_CHECK(cublasLtMatrixLayoutCreate(&d, CUDA_R_16BF, m, n, m));
|
LT_CHECK(cublasLtMatrixLayoutCreate(&d, CUDA_R_16BF, m, n, m));
|
||||||
}
|
}
|
||||||
|
|
||||||
~Problem() {
|
void reset() noexcept {
|
||||||
if (d) cublasLtMatrixLayoutDestroy(d);
|
if (d) cublasLtMatrixLayoutDestroy(d);
|
||||||
if (c) cublasLtMatrixLayoutDestroy(c);
|
if (c) cublasLtMatrixLayoutDestroy(c);
|
||||||
if (b) cublasLtMatrixLayoutDestroy(b);
|
if (b) cublasLtMatrixLayoutDestroy(b);
|
||||||
if (a) cublasLtMatrixLayoutDestroy(a);
|
if (a) cublasLtMatrixLayoutDestroy(a);
|
||||||
if (operation) cublasLtMatmulDescDestroy(operation);
|
if (operation) cublasLtMatmulDescDestroy(operation);
|
||||||
|
d = c = b = a = nullptr;
|
||||||
|
operation = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Problem() { reset(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
@ -367,8 +393,15 @@ void run(torch::Tensor activation_qdata,
|
||||||
|
|
||||||
py::dict build_info() {
|
py::dict build_info() {
|
||||||
py::dict result;
|
py::dict result;
|
||||||
|
int cuda_runtime_version = 0;
|
||||||
|
const cudaError_t cuda_status = cudaRuntimeGetVersion(&cuda_runtime_version);
|
||||||
|
TORCH_CHECK(cuda_status == cudaSuccess, "cudaRuntimeGetVersion failed: ",
|
||||||
|
cudaGetErrorString(cuda_status));
|
||||||
result["cuda_version"] = CUDA_VERSION;
|
result["cuda_version"] = CUDA_VERSION;
|
||||||
result["cublas_version"] = CUBLAS_VERSION;
|
result["cublas_version"] = CUBLAS_VERSION;
|
||||||
|
result["cuda_runtime_version"] = cuda_runtime_version;
|
||||||
|
result["cublaslt_runtime_version"] =
|
||||||
|
static_cast<int64_t>(cublasLtGetVersion());
|
||||||
result["stream_k_public_control"] = false;
|
result["stream_k_public_control"] = false;
|
||||||
result["stream_k_note"] =
|
result["stream_k_note"] =
|
||||||
"CUDA 13 cuBLASLt exposes no documented MatmulAlgoConfig attribute "
|
"CUDA 13 cuBLASLt exposes no documented MatmulAlgoConfig attribute "
|
||||||
|
|
@ -381,6 +414,7 @@ py::dict build_info() {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
PYBIND11_MODULE(TORCH_EXTENSION_NAME, module) {
|
PYBIND11_MODULE(TORCH_EXTENSION_NAME, module) {
|
||||||
|
module.def("prepare", &prepare);
|
||||||
module.def("enumerate", &enumerate);
|
module.def("enumerate", &enumerate);
|
||||||
module.def("check", &check);
|
module.def("check", &check);
|
||||||
module.def("run", &run);
|
module.def("run", &run);
|
||||||
197
src/h3_blackwell_runtime/fc2_lt.py
Normal file
197
src/h3_blackwell_runtime/fc2_lt.py
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
"""Guarded production dispatch for the canonical GB10 FC2 cuBLASLt schedule."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from functools import lru_cache
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
|
||||||
|
_TRUTHY = {"1", "true", "yes", "on"}
|
||||||
|
_CANONICAL_LOGICAL_SHAPE = (37_810, 14_336, 5_376)
|
||||||
|
_CANONICAL_PACKED_ROWS = 37_824
|
||||||
|
_CONFIG = {
|
||||||
|
"algorithm_id": 70,
|
||||||
|
"tile_id": 20,
|
||||||
|
"stages_id": 37,
|
||||||
|
"split_k": 1,
|
||||||
|
"reduction_scheme": 0,
|
||||||
|
"custom_option": 0,
|
||||||
|
"cta_swizzle": 0,
|
||||||
|
}
|
||||||
|
_VALIDATED: set[tuple[int, tuple[int, ...], tuple[int, ...]]] = set()
|
||||||
|
_STATS = {"attempts": 0, "successes": 0, "fallbacks": 0}
|
||||||
|
|
||||||
|
|
||||||
|
class _Fc2LtStrictError(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def fc2_lt_enabled() -> bool:
|
||||||
|
return os.getenv("H3_NVFP4_FC2_LT_SPLITK1", "").lower() in _TRUTHY
|
||||||
|
|
||||||
|
|
||||||
|
def _fc2_lt_strict() -> bool:
|
||||||
|
return os.getenv("H3_NVFP4_FC2_LT_STRICT", "").lower() in _TRUTHY
|
||||||
|
|
||||||
|
|
||||||
|
def fc2_lt_status() -> dict:
|
||||||
|
return {"enabled": fc2_lt_enabled(), **_STATS}
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def _fc2_lt_extension():
|
||||||
|
from torch.utils.cpp_extension import CUDA_HOME, load
|
||||||
|
|
||||||
|
if CUDA_HOME is None:
|
||||||
|
raise RuntimeError("CUDA_HOME is unavailable")
|
||||||
|
source = Path(__file__).resolve().parent / "csrc" / "fc2_nvfp4_lt.cpp"
|
||||||
|
return load(
|
||||||
|
name="h3_fc2_nvfp4_lt",
|
||||||
|
sources=[str(source)],
|
||||||
|
extra_include_paths=[str(Path(CUDA_HOME) / "include")],
|
||||||
|
extra_cflags=["-O2", "-std=c++17"],
|
||||||
|
extra_ldflags=[
|
||||||
|
"-L" + str(Path(CUDA_HOME) / "lib64"),
|
||||||
|
"-lcublasLt",
|
||||||
|
"-lcublas",
|
||||||
|
"-lcudart",
|
||||||
|
],
|
||||||
|
verbose=os.getenv("H3_NVFP4_FC2_LT_VERBOSE", "").lower() in _TRUTHY,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def _fc2_lt_extension_result():
|
||||||
|
try:
|
||||||
|
return _fc2_lt_extension(), None
|
||||||
|
except Exception as error:
|
||||||
|
return None, error
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def _fc2_lt_compatible_result():
|
||||||
|
extension, load_error = _fc2_lt_extension_result()
|
||||||
|
if extension is None:
|
||||||
|
return None, load_error
|
||||||
|
try:
|
||||||
|
info = dict(extension.build_info())
|
||||||
|
compatible = (
|
||||||
|
info.get("cuda_version") == 13_000
|
||||||
|
and info.get("cublas_version") == 130_100
|
||||||
|
and info.get("cuda_runtime_version") == 13_000
|
||||||
|
and info.get("cublaslt_runtime_version") == 130_000
|
||||||
|
)
|
||||||
|
if not compatible:
|
||||||
|
return None, RuntimeError(f"unsupported FC2 cuBLASLt environment: {info}")
|
||||||
|
extension.prepare()
|
||||||
|
return extension, None
|
||||||
|
except Exception as error:
|
||||||
|
return None, error
|
||||||
|
|
||||||
|
|
||||||
|
def _canonical_fc2_supported(linear, gate_up: torch.Tensor, qdata: torch.Tensor) -> bool:
|
||||||
|
logical_rows, in_features, out_features = _CANONICAL_LOGICAL_SHAPE
|
||||||
|
return (
|
||||||
|
linear.role == "h3_mlp_fc2"
|
||||||
|
and linear.in_features == in_features
|
||||||
|
and linear.out_features == out_features
|
||||||
|
and linear.output_dtype == torch.bfloat16
|
||||||
|
and linear.bias is None
|
||||||
|
and tuple(gate_up.shape) == (logical_rows, in_features * 2)
|
||||||
|
and tuple(qdata.shape) == (_CANONICAL_PACKED_ROWS, in_features // 2)
|
||||||
|
and torch.cuda.get_device_capability(gate_up.device) == (12, 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _fallback_or_raise(message: str, error: Exception | None = None):
|
||||||
|
_STATS["fallbacks"] += 1
|
||||||
|
if _fc2_lt_strict():
|
||||||
|
if error is None:
|
||||||
|
raise _Fc2LtStrictError(message)
|
||||||
|
raise _Fc2LtStrictError(message) from error
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_fc2_lt() -> bool:
|
||||||
|
"""Load and validate the extension before latency-sensitive inference."""
|
||||||
|
if not fc2_lt_enabled():
|
||||||
|
return False
|
||||||
|
extension, error = _fc2_lt_compatible_result()
|
||||||
|
if extension is None:
|
||||||
|
_fallback_or_raise("FC2 cuBLASLt extension is incompatible", error)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def fc2_lt_linear(
|
||||||
|
linear,
|
||||||
|
gate_up: torch.Tensor,
|
||||||
|
tensor_scale: torch.Tensor,
|
||||||
|
qdata: torch.Tensor,
|
||||||
|
block_scale: torch.Tensor,
|
||||||
|
) -> torch.Tensor | None:
|
||||||
|
"""Run the validated canonical schedule, or return ``None`` for fallback."""
|
||||||
|
if not fc2_lt_enabled():
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
supported = _canonical_fc2_supported(linear, gate_up, qdata)
|
||||||
|
except Exception as error:
|
||||||
|
return _fallback_or_raise("FC2 cuBLASLt eligibility check failed", error)
|
||||||
|
if not supported:
|
||||||
|
_STATS["fallbacks"] += 1
|
||||||
|
return None
|
||||||
|
_STATS["attempts"] += 1
|
||||||
|
|
||||||
|
extension, load_error = _fc2_lt_compatible_result()
|
||||||
|
if extension is None:
|
||||||
|
return _fallback_or_raise(
|
||||||
|
"FC2 cuBLASLt extension failed to load or is incompatible", load_error
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
device = gate_up.device.index if gate_up.device.index is not None else torch.cuda.current_device()
|
||||||
|
validation_key = (device, tuple(qdata.shape), tuple(linear.weight.shape))
|
||||||
|
if validation_key not in _VALIDATED:
|
||||||
|
checked = dict(
|
||||||
|
extension.check(
|
||||||
|
qdata,
|
||||||
|
block_scale,
|
||||||
|
linear.weight,
|
||||||
|
linear.weight_scale,
|
||||||
|
_CONFIG,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not checked.get("valid") or checked.get("required_workspace_bytes") != 0:
|
||||||
|
return _fallback_or_raise(
|
||||||
|
f"FC2 cuBLASLt algorithm check rejected the candidate: {checked}"
|
||||||
|
)
|
||||||
|
_VALIDATED.add(validation_key)
|
||||||
|
|
||||||
|
alpha = (tensor_scale.float() * linear.weight_scale_2.float()).reshape(1).contiguous()
|
||||||
|
beta = torch.zeros_like(alpha)
|
||||||
|
output = torch.empty(
|
||||||
|
(qdata.shape[0], linear.out_features),
|
||||||
|
device=qdata.device,
|
||||||
|
dtype=torch.bfloat16,
|
||||||
|
)
|
||||||
|
workspace = torch.empty(0, device=qdata.device, dtype=torch.uint8)
|
||||||
|
extension.run(
|
||||||
|
qdata,
|
||||||
|
block_scale,
|
||||||
|
linear.weight,
|
||||||
|
linear.weight_scale,
|
||||||
|
alpha,
|
||||||
|
beta,
|
||||||
|
output,
|
||||||
|
workspace,
|
||||||
|
_CONFIG,
|
||||||
|
)
|
||||||
|
_STATS["successes"] += 1
|
||||||
|
return output[: gate_up.shape[0], : linear.out_features]
|
||||||
|
except _Fc2LtStrictError:
|
||||||
|
raise
|
||||||
|
except Exception as error:
|
||||||
|
return _fallback_or_raise("FC2 cuBLASLt candidate execution failed", error)
|
||||||
|
|
@ -164,9 +164,16 @@ class Nvfp4Linear(DynamicLoraMixin, nn.Module):
|
||||||
if gate_up.dtype != torch.bfloat16 or not gate_up.is_cuda or not gate_up.is_contiguous():
|
if gate_up.dtype != torch.bfloat16 or not gate_up.is_cuda or not gate_up.is_contiguous():
|
||||||
raise ValueError("SwiGLU NVFP4 dispatch requires contiguous CUDA BF16 input")
|
raise ValueError("SwiGLU NVFP4 dispatch requires contiguous CUDA BF16 input")
|
||||||
|
|
||||||
from .nvfp4_quant import vortex_quantize_swiglu_nvfp4
|
from .nvfp4_quant import vortex_native_quantize_swiglu_nvfp4, wrap_native_swiglu_nvfp4
|
||||||
|
|
||||||
packed_x = vortex_quantize_swiglu_nvfp4(gate_up)
|
packed = vortex_native_quantize_swiglu_nvfp4(gate_up)
|
||||||
|
if os.getenv("H3_NVFP4_FC2_LT_SPLITK1", "").lower() in {"1", "true", "yes", "on"}:
|
||||||
|
from .fc2_lt import fc2_lt_linear
|
||||||
|
|
||||||
|
scheduled = fc2_lt_linear(self, gate_up, *packed)
|
||||||
|
if scheduled is not None:
|
||||||
|
return scheduled
|
||||||
|
packed_x = wrap_native_swiglu_nvfp4(gate_up, packed)
|
||||||
bias = self.bias.to(gate_up) if self.bias is not None else None
|
bias = self.bias.to(gate_up) if self.bias is not None else None
|
||||||
output = functional.linear(packed_x, self._packed_weight(), bias)[
|
output = functional.linear(packed_x, self._packed_weight(), bias)[
|
||||||
: gate_up.shape[0], : self.out_features
|
: gate_up.shape[0], : self.out_features
|
||||||
|
|
|
||||||
|
|
@ -328,9 +328,16 @@ def vortex_native_quantize_swiglu_nvfp4(tensor: torch.Tensor):
|
||||||
|
|
||||||
def vortex_quantize_swiglu_nvfp4(tensor: torch.Tensor):
|
def vortex_quantize_swiglu_nvfp4(tensor: torch.Tensor):
|
||||||
"""Wrap fused SwiGLU output as a Comfy TensorCoreNVFP4Layout tensor."""
|
"""Wrap fused SwiGLU output as a Comfy TensorCoreNVFP4Layout tensor."""
|
||||||
|
return wrap_native_swiglu_nvfp4(
|
||||||
|
tensor, vortex_native_quantize_swiglu_nvfp4(tensor),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_native_swiglu_nvfp4(tensor: torch.Tensor, packed):
|
||||||
|
"""Wrap already-produced fused SwiGLU bytes for the Comfy fallback."""
|
||||||
from comfy_kitchen.tensor import QuantizedTensor, TensorCoreNVFP4Layout
|
from comfy_kitchen.tensor import QuantizedTensor, TensorCoreNVFP4Layout
|
||||||
|
|
||||||
tensor_scale, qdata, block_scale = vortex_native_quantize_swiglu_nvfp4(tensor)
|
tensor_scale, qdata, block_scale = packed
|
||||||
output_shape = (tensor.shape[0], tensor.shape[1] // 2)
|
output_shape = (tensor.shape[0], tensor.shape[1] // 2)
|
||||||
return QuantizedTensor(
|
return QuantizedTensor(
|
||||||
qdata,
|
qdata,
|
||||||
|
|
|
||||||
|
|
@ -154,9 +154,14 @@ class H3HotRuntime:
|
||||||
def _load_h3(self) -> H3PackedDenoiser:
|
def _load_h3(self) -> H3PackedDenoiser:
|
||||||
model = H3PackedDenoiser.from_checkpoint(self.checkpoint, attention_backend=self.config.attention).eval()
|
model = H3PackedDenoiser.from_checkpoint(self.checkpoint, attention_backend=self.config.attention).eval()
|
||||||
configure_mlp_chunking(model, self.config.mlp_chunks, self.config.mlp_chunk_threshold)
|
configure_mlp_chunking(model, self.config.mlp_chunks, self.config.mlp_chunk_threshold)
|
||||||
|
from .fc2_lt import prepare_fc2_lt
|
||||||
|
|
||||||
|
prepare_fc2_lt()
|
||||||
return model
|
return model
|
||||||
|
|
||||||
def status(self) -> dict:
|
def status(self) -> dict:
|
||||||
|
from .fc2_lt import fc2_lt_status
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"ready": True,
|
"ready": True,
|
||||||
"initial_attention": self.config.attention,
|
"initial_attention": self.config.attention,
|
||||||
|
|
@ -168,6 +173,7 @@ class H3HotRuntime:
|
||||||
"vae_tile_size": self.config.vae_tile_size,
|
"vae_tile_size": self.config.vae_tile_size,
|
||||||
"mlp_chunks": self.config.mlp_chunks,
|
"mlp_chunks": self.config.mlp_chunks,
|
||||||
"mlp_chunk_threshold": self.config.mlp_chunk_threshold,
|
"mlp_chunk_threshold": self.config.mlp_chunk_threshold,
|
||||||
|
"fc2_lt": fc2_lt_status(),
|
||||||
"loaded_at": self.loaded_at,
|
"loaded_at": self.loaded_at,
|
||||||
"load_stages": self.load_stages,
|
"load_stages": self.load_stages,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
287
tests/test_fc2_lt.py
Normal file
287
tests/test_fc2_lt.py
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from h3_blackwell_runtime.fc2_lt import (
|
||||||
|
_VALIDATED,
|
||||||
|
_canonical_fc2_supported,
|
||||||
|
_fc2_lt_compatible_result,
|
||||||
|
_STATS,
|
||||||
|
fc2_lt_enabled,
|
||||||
|
fc2_lt_linear,
|
||||||
|
prepare_fc2_lt,
|
||||||
|
)
|
||||||
|
from h3_blackwell_runtime.nvfp4 import Nvfp4Linear
|
||||||
|
from h3_blackwell_runtime.runtime import H3HotRuntime
|
||||||
|
|
||||||
|
|
||||||
|
class Fc2LtContracts(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
_VALIDATED.clear()
|
||||||
|
_fc2_lt_compatible_result.cache_clear()
|
||||||
|
_STATS.update(attempts=0, successes=0, fallbacks=0)
|
||||||
|
|
||||||
|
def test_flag_is_opt_in(self):
|
||||||
|
with patch.dict(os.environ, {}, clear=True):
|
||||||
|
self.assertFalse(fc2_lt_enabled())
|
||||||
|
with patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "yes"}, clear=True):
|
||||||
|
self.assertTrue(fc2_lt_enabled())
|
||||||
|
|
||||||
|
def test_only_canonical_unsharded_fc2_is_eligible(self):
|
||||||
|
linear = SimpleNamespace(
|
||||||
|
role="h3_mlp_fc2",
|
||||||
|
in_features=14_336,
|
||||||
|
out_features=5_376,
|
||||||
|
output_dtype=torch.bfloat16,
|
||||||
|
bias=None,
|
||||||
|
)
|
||||||
|
gate_up = SimpleNamespace(
|
||||||
|
shape=(37_810, 28_672),
|
||||||
|
device=torch.device("cuda", 0),
|
||||||
|
)
|
||||||
|
qdata = SimpleNamespace(shape=(37_824, 7_168))
|
||||||
|
with patch("torch.cuda.get_device_capability", return_value=(12, 1)):
|
||||||
|
self.assertTrue(_canonical_fc2_supported(linear, gate_up, qdata))
|
||||||
|
linear.in_features //= 2
|
||||||
|
self.assertFalse(_canonical_fc2_supported(linear, gate_up, qdata))
|
||||||
|
|
||||||
|
def test_prepare_loads_and_validates_extension(self):
|
||||||
|
extension = MagicMock()
|
||||||
|
extension.build_info.return_value = {
|
||||||
|
"cuda_version": 13_000,
|
||||||
|
"cublas_version": 130_100,
|
||||||
|
"cuda_runtime_version": 13_000,
|
||||||
|
"cublaslt_runtime_version": 130_000,
|
||||||
|
}
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1"}, clear=True),
|
||||||
|
patch(
|
||||||
|
"h3_blackwell_runtime.fc2_lt._fc2_lt_extension_result",
|
||||||
|
return_value=(extension, None),
|
||||||
|
),
|
||||||
|
):
|
||||||
|
self.assertTrue(prepare_fc2_lt())
|
||||||
|
extension.prepare.assert_called_once_with()
|
||||||
|
|
||||||
|
def test_ineligible_shape_is_counted_as_fallback(self):
|
||||||
|
args = (MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock())
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1"}, clear=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._canonical_fc2_supported", return_value=False),
|
||||||
|
):
|
||||||
|
self.assertIsNone(fc2_lt_linear(*args))
|
||||||
|
self.assertEqual(_STATS["fallbacks"], 1)
|
||||||
|
|
||||||
|
def test_h3_model_load_prepares_extension(self):
|
||||||
|
model = MagicMock()
|
||||||
|
model.eval.return_value = model
|
||||||
|
runtime = SimpleNamespace(
|
||||||
|
checkpoint=MagicMock(),
|
||||||
|
config=SimpleNamespace(attention="sage2", mlp_chunks=1, mlp_chunk_threshold=4096),
|
||||||
|
)
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"h3_blackwell_runtime.runtime.H3PackedDenoiser.from_checkpoint",
|
||||||
|
return_value=model,
|
||||||
|
),
|
||||||
|
patch("h3_blackwell_runtime.runtime.configure_mlp_chunking") as configure,
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt.prepare_fc2_lt") as prepare,
|
||||||
|
):
|
||||||
|
self.assertIs(H3HotRuntime._load_h3(runtime), model)
|
||||||
|
configure.assert_called_once_with(model, 1, 4096)
|
||||||
|
prepare.assert_called_once_with()
|
||||||
|
|
||||||
|
def test_extension_load_failure_falls_back_unless_strict(self):
|
||||||
|
args = (MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock())
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1"}, clear=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._canonical_fc2_supported", return_value=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._fc2_lt_extension_result", return_value=(None, RuntimeError("load"))),
|
||||||
|
):
|
||||||
|
self.assertIsNone(fc2_lt_linear(*args))
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1", "H3_NVFP4_FC2_LT_STRICT": "1"}, clear=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._canonical_fc2_supported", return_value=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._fc2_lt_extension_result", return_value=(None, RuntimeError("load"))),
|
||||||
|
):
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "failed to load"):
|
||||||
|
fc2_lt_linear(*args)
|
||||||
|
|
||||||
|
def test_build_info_and_runtime_version_failures_fall_back(self):
|
||||||
|
extension = MagicMock()
|
||||||
|
extension.build_info.side_effect = RuntimeError("metadata")
|
||||||
|
args = (MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock())
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1"}, clear=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._canonical_fc2_supported", return_value=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._fc2_lt_extension_result", return_value=(extension, None)),
|
||||||
|
):
|
||||||
|
self.assertIsNone(fc2_lt_linear(*args))
|
||||||
|
|
||||||
|
_fc2_lt_compatible_result.cache_clear()
|
||||||
|
extension.build_info.side_effect = None
|
||||||
|
extension.build_info.return_value = {
|
||||||
|
"cuda_version": 13_000,
|
||||||
|
"cublas_version": 130_100,
|
||||||
|
"cuda_runtime_version": 12_900,
|
||||||
|
"cublaslt_runtime_version": 130_000,
|
||||||
|
}
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1"}, clear=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._canonical_fc2_supported", return_value=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._fc2_lt_extension_result", return_value=(extension, None)),
|
||||||
|
):
|
||||||
|
self.assertIsNone(fc2_lt_linear(*args))
|
||||||
|
|
||||||
|
def test_algo_check_and_execution_failures_fall_back(self):
|
||||||
|
extension = MagicMock()
|
||||||
|
extension.build_info.return_value = {
|
||||||
|
"cuda_version": 13_000,
|
||||||
|
"cublas_version": 130_100,
|
||||||
|
"cuda_runtime_version": 13_000,
|
||||||
|
"cublaslt_runtime_version": 130_000,
|
||||||
|
}
|
||||||
|
linear = MagicMock()
|
||||||
|
linear.weight.shape = (5_376, 7_168)
|
||||||
|
gate_up = MagicMock()
|
||||||
|
gate_up.device.index = 0
|
||||||
|
qdata = MagicMock()
|
||||||
|
qdata.shape = (37_824, 7_168)
|
||||||
|
args = (linear, gate_up, MagicMock(), qdata, MagicMock())
|
||||||
|
common = (
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1"}, clear=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._canonical_fc2_supported", return_value=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._fc2_lt_extension_result", return_value=(extension, None)),
|
||||||
|
)
|
||||||
|
extension.check.return_value = {"valid": False, "required_workspace_bytes": 0}
|
||||||
|
with common[0], common[1], common[2]:
|
||||||
|
self.assertIsNone(fc2_lt_linear(*args))
|
||||||
|
|
||||||
|
_VALIDATED.clear()
|
||||||
|
extension.check.return_value = {"valid": True, "required_workspace_bytes": 0}
|
||||||
|
extension.run.side_effect = RuntimeError("run")
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1"}, clear=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._canonical_fc2_supported", return_value=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._fc2_lt_extension_result", return_value=(extension, None)),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt.torch.zeros_like", return_value=MagicMock()),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt.torch.empty", return_value=MagicMock()),
|
||||||
|
):
|
||||||
|
self.assertIsNone(fc2_lt_linear(*args))
|
||||||
|
|
||||||
|
def test_strict_mode_raises_on_algo_check_and_execution_failures(self):
|
||||||
|
extension = MagicMock()
|
||||||
|
extension.build_info.return_value = {
|
||||||
|
"cuda_version": 13_000,
|
||||||
|
"cublas_version": 130_100,
|
||||||
|
"cuda_runtime_version": 13_000,
|
||||||
|
"cublaslt_runtime_version": 130_000,
|
||||||
|
}
|
||||||
|
linear = MagicMock()
|
||||||
|
linear.weight.shape = (5_376, 7_168)
|
||||||
|
gate_up = MagicMock()
|
||||||
|
gate_up.device.index = 0
|
||||||
|
qdata = MagicMock()
|
||||||
|
qdata.shape = (37_824, 7_168)
|
||||||
|
args = (linear, gate_up, MagicMock(), qdata, MagicMock())
|
||||||
|
environment = {
|
||||||
|
"H3_NVFP4_FC2_LT_SPLITK1": "1",
|
||||||
|
"H3_NVFP4_FC2_LT_STRICT": "1",
|
||||||
|
}
|
||||||
|
extension.check.return_value = {"valid": False, "required_workspace_bytes": 0}
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, environment, clear=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._canonical_fc2_supported", return_value=True),
|
||||||
|
patch(
|
||||||
|
"h3_blackwell_runtime.fc2_lt._fc2_lt_extension_result",
|
||||||
|
return_value=(extension, None),
|
||||||
|
),
|
||||||
|
):
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "algorithm check rejected"):
|
||||||
|
fc2_lt_linear(*args)
|
||||||
|
|
||||||
|
_VALIDATED.clear()
|
||||||
|
_fc2_lt_compatible_result.cache_clear()
|
||||||
|
extension.check.return_value = {"valid": True, "required_workspace_bytes": 0}
|
||||||
|
extension.run.side_effect = RuntimeError("run")
|
||||||
|
with (
|
||||||
|
patch.dict(os.environ, environment, clear=True),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt._canonical_fc2_supported", return_value=True),
|
||||||
|
patch(
|
||||||
|
"h3_blackwell_runtime.fc2_lt._fc2_lt_extension_result",
|
||||||
|
return_value=(extension, None),
|
||||||
|
),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt.torch.zeros_like", return_value=MagicMock()),
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt.torch.empty", return_value=MagicMock()),
|
||||||
|
):
|
||||||
|
with self.assertRaisesRegex(RuntimeError, "execution failed"):
|
||||||
|
fc2_lt_linear(*args)
|
||||||
|
|
||||||
|
def test_forward_swiglu_uses_schedule_without_repacking(self):
|
||||||
|
linear = SimpleNamespace(
|
||||||
|
role="h3_mlp_fc2",
|
||||||
|
in_features=4,
|
||||||
|
out_features=3,
|
||||||
|
output_dtype=torch.bfloat16,
|
||||||
|
bias=None,
|
||||||
|
pre_quant_scale=None,
|
||||||
|
active_lora=None,
|
||||||
|
lora_strength=0.0,
|
||||||
|
full_precision_matrix_mult=False,
|
||||||
|
)
|
||||||
|
gate_up = torch.randn(1, 8, dtype=torch.bfloat16)
|
||||||
|
packed = (MagicMock(), MagicMock(), MagicMock())
|
||||||
|
expected = torch.randn(1, 3, dtype=torch.bfloat16)
|
||||||
|
with (
|
||||||
|
torch.inference_mode(),
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1"}, clear=True),
|
||||||
|
patch.object(torch.Tensor, "is_cuda", new_callable=unittest.mock.PropertyMock, return_value=True),
|
||||||
|
patch("h3_blackwell_runtime.nvfp4_quant.vortex_native_quantize_swiglu_nvfp4", return_value=packed) as quantize,
|
||||||
|
patch("h3_blackwell_runtime.nvfp4_quant.wrap_native_swiglu_nvfp4") as wrap,
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt.fc2_lt_linear", return_value=expected) as scheduled,
|
||||||
|
):
|
||||||
|
actual = Nvfp4Linear.forward_swiglu(linear, gate_up)
|
||||||
|
self.assertTrue(torch.equal(actual, expected))
|
||||||
|
quantize.assert_called_once_with(gate_up)
|
||||||
|
scheduled.assert_called_once_with(linear, gate_up, *packed)
|
||||||
|
wrap.assert_not_called()
|
||||||
|
|
||||||
|
def test_forward_swiglu_reuses_native_bytes_on_fallback(self):
|
||||||
|
packed_weight = MagicMock()
|
||||||
|
linear = SimpleNamespace(
|
||||||
|
role="h3_mlp_fc2",
|
||||||
|
in_features=4,
|
||||||
|
out_features=3,
|
||||||
|
output_dtype=torch.bfloat16,
|
||||||
|
bias=None,
|
||||||
|
pre_quant_scale=None,
|
||||||
|
active_lora=None,
|
||||||
|
lora_strength=0.0,
|
||||||
|
full_precision_matrix_mult=False,
|
||||||
|
_packed_weight=lambda: packed_weight,
|
||||||
|
)
|
||||||
|
gate_up = torch.randn(1, 8, dtype=torch.bfloat16)
|
||||||
|
packed = (MagicMock(), MagicMock(), MagicMock())
|
||||||
|
wrapped = MagicMock()
|
||||||
|
expected = torch.randn(1, 3, dtype=torch.bfloat16)
|
||||||
|
with (
|
||||||
|
torch.inference_mode(),
|
||||||
|
patch.dict(os.environ, {"H3_NVFP4_FC2_LT_SPLITK1": "1"}, clear=True),
|
||||||
|
patch.object(torch.Tensor, "is_cuda", new_callable=unittest.mock.PropertyMock, return_value=True),
|
||||||
|
patch("h3_blackwell_runtime.nvfp4_quant.vortex_native_quantize_swiglu_nvfp4", return_value=packed) as quantize,
|
||||||
|
patch("h3_blackwell_runtime.nvfp4_quant.wrap_native_swiglu_nvfp4", return_value=wrapped) as wrap,
|
||||||
|
patch("h3_blackwell_runtime.fc2_lt.fc2_lt_linear", return_value=None),
|
||||||
|
patch("h3_blackwell_runtime.nvfp4.functional.linear", return_value=expected) as gemm,
|
||||||
|
):
|
||||||
|
actual = Nvfp4Linear.forward_swiglu(linear, gate_up)
|
||||||
|
self.assertTrue(torch.equal(actual, expected))
|
||||||
|
quantize.assert_called_once_with(gate_up)
|
||||||
|
wrap.assert_called_once_with(gate_up, packed)
|
||||||
|
gemm.assert_called_once_with(wrapped, packed_weight, None)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
|
|
@ -27,7 +27,7 @@ from h3_blackwell_runtime.t2v import random_av_latents
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
SOURCE = ROOT / "research" / "fc2_nvfp4_scheduling" / "fc2_nvfp4_lt.cpp"
|
SOURCE = ROOT / "src" / "h3_blackwell_runtime" / "csrc" / "fc2_nvfp4_lt.cpp"
|
||||||
DEFAULT_BUDGETS = (0, 4 << 20, 8 << 20, 16 << 20, 32 << 20, 64 << 20)
|
DEFAULT_BUDGETS = (0, 4 << 20, 8 << 20, 16 << 20, 32 << 20, 64 << 20)
|
||||||
DEFAULT_BLOCKS = (0, 24, 49)
|
DEFAULT_BLOCKS = (0, 24, 49)
|
||||||
ENV_PREFIXES = ("H3_", "COMFY_KITCHEN_", "CUDA_", "TORCH_")
|
ENV_PREFIXES = ("H3_", "COMFY_KITCHEN_", "CUDA_", "TORCH_")
|
||||||
|
|
@ -35,7 +35,15 @@ ENV_PREFIXES = ("H3_", "COMFY_KITCHEN_", "CUDA_", "TORCH_")
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument("--mode", choices=("compile", "characterize", "sweep", "selected", "profile", "block-gate", "trajectory"), default="characterize")
|
parser.add_argument(
|
||||||
|
"--mode",
|
||||||
|
choices=(
|
||||||
|
"compile", "characterize", "sweep", "selected", "profile",
|
||||||
|
"block-gate", "trajectory", "production-block-gate",
|
||||||
|
"production-trajectory", "shape-gate",
|
||||||
|
),
|
||||||
|
default="characterize",
|
||||||
|
)
|
||||||
parser.add_argument("--model-path", default="/models/minimax_h3_fl2va_pruned_nvfp4.safetensors")
|
parser.add_argument("--model-path", default="/models/minimax_h3_fl2va_pruned_nvfp4.safetensors")
|
||||||
parser.add_argument("--capture", type=Path, help="Directory containing input.pt, or an input .pt file")
|
parser.add_argument("--capture", type=Path, help="Directory containing input.pt, or an input .pt file")
|
||||||
parser.add_argument("--output", type=Path, default=Path("/output/h3-blackwell-runtime/benchmarks/fc2-nvfp4-scheduling.json"))
|
parser.add_argument("--output", type=Path, default=Path("/output/h3-blackwell-runtime/benchmarks/fc2-nvfp4-scheduling.json"))
|
||||||
|
|
@ -48,6 +56,10 @@ def parse_args() -> argparse.Namespace:
|
||||||
parser.add_argument("--sampler-step", type=int, default=1)
|
parser.add_argument("--sampler-step", type=int, default=1)
|
||||||
parser.add_argument("--seed", type=int, default=440420)
|
parser.add_argument("--seed", type=int, default=440420)
|
||||||
parser.add_argument("--text-tokens", type=int, default=100)
|
parser.add_argument("--text-tokens", type=int, default=100)
|
||||||
|
parser.add_argument(
|
||||||
|
"--shape-text-tokens", type=int, nargs="+",
|
||||||
|
default=[1, 15, 32, 64, 99, 100, 128, 256, 512],
|
||||||
|
)
|
||||||
parser.add_argument("--blocks", type=int, nargs="+", default=list(DEFAULT_BLOCKS))
|
parser.add_argument("--blocks", type=int, nargs="+", default=list(DEFAULT_BLOCKS))
|
||||||
parser.add_argument("--probe-block", type=int, default=24)
|
parser.add_argument("--probe-block", type=int, default=24)
|
||||||
parser.add_argument("--workspace-budgets", type=int, nargs="+", default=list(DEFAULT_BUDGETS))
|
parser.add_argument("--workspace-budgets", type=int, nargs="+", default=list(DEFAULT_BUDGETS))
|
||||||
|
|
@ -553,7 +565,109 @@ def block_gate(extension, model, block_inputs, block_outputs, gate_up, timesteps
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
|
|
||||||
def trajectory_gate(extension, checkpoint, model, config, args):
|
def production_block_gate(model, block_inputs, block_outputs, timesteps, rotation, segments, args):
|
||||||
|
from h3_blackwell_runtime.fc2_lt import fc2_lt_status
|
||||||
|
|
||||||
|
flag = "H3_NVFP4_FC2_LT_SPLITK1"
|
||||||
|
previous = os.environ.get(flag)
|
||||||
|
rows = []
|
||||||
|
|
||||||
|
def select(enabled: bool) -> None:
|
||||||
|
if enabled:
|
||||||
|
os.environ[flag] = "1"
|
||||||
|
else:
|
||||||
|
os.environ.pop(flag, None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
for index in DEFAULT_BLOCKS:
|
||||||
|
block = model.backbone.blocks[index]
|
||||||
|
adaln_values = tuple(value.detach().clone() for value in model.backbone.adaln[index](timesteps))
|
||||||
|
|
||||||
|
def baseline():
|
||||||
|
select(False)
|
||||||
|
return block(block_inputs[index].detach().clone(), rotation, *adaln_values, segments)
|
||||||
|
|
||||||
|
def candidate():
|
||||||
|
select(True)
|
||||||
|
return block(block_inputs[index].detach().clone(), rotation, *adaln_values, segments)
|
||||||
|
|
||||||
|
before = fc2_lt_status()
|
||||||
|
timing = benchmark_pair(
|
||||||
|
baseline, candidate, args.rounds, args.warmup,
|
||||||
|
block_outputs[index].shape[0], block_outputs[index].shape[1], 1,
|
||||||
|
report_dense=False,
|
||||||
|
)
|
||||||
|
with torch.inference_mode():
|
||||||
|
baseline_value = baseline().detach().clone()
|
||||||
|
candidate_value = candidate().detach().clone()
|
||||||
|
after = fc2_lt_status()
|
||||||
|
dispatch_delta = {
|
||||||
|
name: after[name] - before[name]
|
||||||
|
for name in ("attempts", "successes", "fallbacks")
|
||||||
|
}
|
||||||
|
row = {
|
||||||
|
"block": index,
|
||||||
|
"production_method": "block.mlp.fc2.forward_swiglu",
|
||||||
|
"candidate_vs_baseline": compare(candidate_value, baseline_value),
|
||||||
|
"baseline_vs_traversal": compare(baseline_value, block_outputs[index]),
|
||||||
|
"candidate_vs_traversal": compare(candidate_value, block_outputs[index]),
|
||||||
|
"timing": timing,
|
||||||
|
"dispatch_delta": dispatch_delta,
|
||||||
|
}
|
||||||
|
if not all(
|
||||||
|
row[name]["bf16_exact"]
|
||||||
|
for name in ("candidate_vs_baseline", "baseline_vs_traversal", "candidate_vs_traversal")
|
||||||
|
):
|
||||||
|
raise RuntimeError(f"production FC2 schedule is not byte-exact in block {index}")
|
||||||
|
if dispatch_delta["successes"] == 0 or dispatch_delta["fallbacks"] != 0:
|
||||||
|
raise RuntimeError(f"production FC2 schedule did not dispatch cleanly in block {index}")
|
||||||
|
rows.append(row)
|
||||||
|
finally:
|
||||||
|
if previous is None:
|
||||||
|
os.environ.pop(flag, None)
|
||||||
|
else:
|
||||||
|
os.environ[flag] = previous
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def shape_gate(extension, gate_up, fc2, config, args):
|
||||||
|
base_rows = gate_up.shape[0] - args.text_tokens
|
||||||
|
rows = []
|
||||||
|
for text_tokens in args.shape_text_tokens:
|
||||||
|
logical_rows = base_rows + text_tokens
|
||||||
|
if logical_rows <= gate_up.shape[0]:
|
||||||
|
actual_gate_up = gate_up[:logical_rows].contiguous()
|
||||||
|
else:
|
||||||
|
extra = logical_rows - gate_up.shape[0]
|
||||||
|
actual_gate_up = torch.cat((gate_up, gate_up[:extra]), dim=0).contiguous()
|
||||||
|
packed = packed_boundary(actual_gate_up, fc2)
|
||||||
|
run_candidate, _output, workspace = candidate_runner(
|
||||||
|
extension, packed, fc2, config, args.workspace_bytes,
|
||||||
|
)
|
||||||
|
timing = benchmark_pair(
|
||||||
|
comfy_packed_runner(packed, fc2, logical_rows), run_candidate,
|
||||||
|
args.rounds, args.warmup, logical_rows, fc2.out_features, fc2.in_features,
|
||||||
|
)
|
||||||
|
checked = dict(
|
||||||
|
extension.check(
|
||||||
|
packed[1], packed[2], fc2.weight, fc2.weight_scale, config,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
row = {
|
||||||
|
"text_tokens": text_tokens,
|
||||||
|
"logical_rows": logical_rows,
|
||||||
|
"packed_rows": packed[1].shape[0],
|
||||||
|
"parity": timing["parity"],
|
||||||
|
"timing": timing,
|
||||||
|
"checked": checked,
|
||||||
|
"supplied_workspace_bytes": workspace.numel(),
|
||||||
|
}
|
||||||
|
row["passed"] = row["parity"]["bf16_exact"] and bool(checked.get("valid"))
|
||||||
|
rows.append(row)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def trajectory_gate(extension, checkpoint, model, config, args, *, production_dispatch=False):
|
||||||
packer = H3PromptPacker(checkpoint)
|
packer = H3PromptPacker(checkpoint)
|
||||||
torch.manual_seed(args.seed)
|
torch.manual_seed(args.seed)
|
||||||
video, audio, aligned_frames = random_av_latents(
|
video, audio, aligned_frames = random_av_latents(
|
||||||
|
|
@ -583,9 +697,29 @@ def trajectory_gate(extension, checkpoint, model, config, args):
|
||||||
return output[:actual_gate_up.shape[0], :fc2.out_features]
|
return output[:actual_gate_up.shape[0], :fc2.out_features]
|
||||||
return types.MethodType(replacement, fc2)
|
return types.MethodType(replacement, fc2)
|
||||||
|
|
||||||
candidates = [candidate_method(block.mlp.fc2) for block in model.backbone.blocks]
|
candidates = None if production_dispatch else [
|
||||||
|
candidate_method(block.mlp.fc2) for block in model.backbone.blocks
|
||||||
|
]
|
||||||
|
flag = "H3_NVFP4_FC2_LT_SPLITK1"
|
||||||
|
previous_flag = os.environ.get(flag)
|
||||||
|
if production_dispatch:
|
||||||
|
os.environ[flag] = "1"
|
||||||
|
from h3_blackwell_runtime.fc2_lt import prepare_fc2_lt
|
||||||
|
|
||||||
|
if not prepare_fc2_lt():
|
||||||
|
raise RuntimeError("production FC2 extension preparation failed")
|
||||||
|
if previous_flag is None:
|
||||||
|
os.environ.pop(flag, None)
|
||||||
|
else:
|
||||||
|
os.environ[flag] = previous_flag
|
||||||
|
|
||||||
def run(candidate: bool):
|
def run(candidate: bool):
|
||||||
|
if production_dispatch:
|
||||||
|
if candidate:
|
||||||
|
os.environ[flag] = "1"
|
||||||
|
else:
|
||||||
|
os.environ.pop(flag, None)
|
||||||
|
else:
|
||||||
for index, block in enumerate(model.backbone.blocks):
|
for index, block in enumerate(model.backbone.blocks):
|
||||||
block.mlp.fc2.forward_swiglu = candidates[index] if candidate else originals[index]
|
block.mlp.fc2.forward_swiglu = candidates[index] if candidate else originals[index]
|
||||||
torch.cuda.synchronize()
|
torch.cuda.synchronize()
|
||||||
|
|
@ -607,10 +741,19 @@ def trajectory_gate(extension, checkpoint, model, config, args):
|
||||||
try:
|
try:
|
||||||
with torch.inference_mode():
|
with torch.inference_mode():
|
||||||
(reference_video, reference_audio), baseline_seconds = run(False)
|
(reference_video, reference_audio), baseline_seconds = run(False)
|
||||||
|
if production_dispatch:
|
||||||
|
from h3_blackwell_runtime.fc2_lt import fc2_lt_status
|
||||||
|
dispatch_before = fc2_lt_status()
|
||||||
(candidate_video, candidate_audio), candidate_seconds = run(True)
|
(candidate_video, candidate_audio), candidate_seconds = run(True)
|
||||||
|
if production_dispatch:
|
||||||
|
dispatch_after = fc2_lt_status()
|
||||||
finally:
|
finally:
|
||||||
for block, original in zip(model.backbone.blocks, originals, strict=True):
|
for block, original in zip(model.backbone.blocks, originals, strict=True):
|
||||||
block.mlp.fc2.forward_swiglu = original
|
block.mlp.fc2.forward_swiglu = original
|
||||||
|
if previous_flag is None:
|
||||||
|
os.environ.pop(flag, None)
|
||||||
|
else:
|
||||||
|
os.environ[flag] = previous_flag
|
||||||
|
|
||||||
video_parity = compare(candidate_video, reference_video)
|
video_parity = compare(candidate_video, reference_video)
|
||||||
audio_parity = compare(candidate_audio, reference_audio)
|
audio_parity = compare(candidate_audio, reference_audio)
|
||||||
|
|
@ -631,6 +774,17 @@ def trajectory_gate(extension, checkpoint, model, config, args):
|
||||||
"accepted_swiglu_producer_preserved": True,
|
"accepted_swiglu_producer_preserved": True,
|
||||||
"accepted_gate_and_residual_path_preserved": True,
|
"accepted_gate_and_residual_path_preserved": True,
|
||||||
}
|
}
|
||||||
|
if production_dispatch:
|
||||||
|
result["production_dispatch"] = True
|
||||||
|
result["dispatch_delta"] = {
|
||||||
|
name: dispatch_after[name] - dispatch_before[name]
|
||||||
|
for name in ("attempts", "successes", "fallbacks")
|
||||||
|
}
|
||||||
|
if result["dispatch_delta"]["successes"] == 0 or result["dispatch_delta"]["fallbacks"] != 0:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"production FC2 schedule did not dispatch cleanly in trajectory: "
|
||||||
|
f"{result['dispatch_delta']}"
|
||||||
|
)
|
||||||
if not result["bf16_exact"]:
|
if not result["bf16_exact"]:
|
||||||
raise RuntimeError("FC2 library candidate trajectory is not byte-exact")
|
raise RuntimeError("FC2 library candidate trajectory is not byte-exact")
|
||||||
return result
|
return result
|
||||||
|
|
@ -684,7 +838,11 @@ def main() -> None:
|
||||||
"explicit_split_k_checks": explicit,
|
"explicit_split_k_checks": explicit,
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.mode in {"characterize", "sweep", "selected", "profile", "block-gate", "trajectory"}:
|
if args.mode in {
|
||||||
|
"characterize", "sweep", "selected", "profile", "block-gate",
|
||||||
|
"trajectory", "production-block-gate", "production-trajectory",
|
||||||
|
"shape-gate",
|
||||||
|
}:
|
||||||
config = resolve_candidate(args, enumerated, explicit)
|
config = resolve_candidate(args, enumerated, explicit)
|
||||||
result["selected"] = config
|
result["selected"] = config
|
||||||
if args.mode == "characterize":
|
if args.mode == "characterize":
|
||||||
|
|
@ -712,8 +870,20 @@ def main() -> None:
|
||||||
result["profile"] = profile_one(extension, packed, fc2, config, gate_ups[probe].shape[0], args)
|
result["profile"] = profile_one(extension, packed, fc2, config, gate_ups[probe].shape[0], args)
|
||||||
elif args.mode == "block-gate":
|
elif args.mode == "block-gate":
|
||||||
result["block_gate"] = block_gate(extension, model, block_inputs, block_outputs, gate_ups, timesteps, rotation, segments, config, args)
|
result["block_gate"] = block_gate(extension, model, block_inputs, block_outputs, gate_ups, timesteps, rotation, segments, config, args)
|
||||||
|
elif args.mode == "production-block-gate":
|
||||||
|
result["production_block_gate"] = production_block_gate(
|
||||||
|
model, block_inputs, block_outputs, timesteps, rotation, segments, args,
|
||||||
|
)
|
||||||
|
elif args.mode == "shape-gate":
|
||||||
|
result["shape_gate"] = shape_gate(
|
||||||
|
extension, gate_ups[probe], fc2, config, args,
|
||||||
|
)
|
||||||
elif args.mode == "trajectory":
|
elif args.mode == "trajectory":
|
||||||
result["trajectory"] = trajectory_gate(extension, checkpoint, model, config, args)
|
result["trajectory"] = trajectory_gate(extension, checkpoint, model, config, args)
|
||||||
|
elif args.mode == "production-trajectory":
|
||||||
|
result["trajectory"] = trajectory_gate(
|
||||||
|
extension, checkpoint, model, config, args, production_dispatch=True,
|
||||||
|
)
|
||||||
|
|
||||||
result["errors_and_unsupported"] = errors + [{
|
result["errors_and_unsupported"] = errors + [{
|
||||||
"feature": "Stream-K",
|
"feature": "Stream-K",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue