Skip to content

Conversation

@Ubospica
Copy link
Collaborator

@Ubospica Ubospica commented Nov 30, 2025

Signed-off-by: Ubospica ubospica@gmail.com

Summary by CodeRabbit

  • New Features

    • Introduced TorchBuilder for unified CUDA compilation support
    • Added centralized builder registry system for streamlined compilation pipeline
    • Support for C++ language added to compilation targets
    • Implemented solution hashing for improved caching efficiency
  • Refactoring

    • Simplified builder architecture with cache-aware build system
    • Restructured metadata handling for compiled runnables
    • Consolidated builder initialization and prioritization logic
  • Removals

    • CUDA builder removed in favor of TorchBuilder

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: Ubospica <ubospica@gmail.com>
Signed-off-by: Ubospica <ubospica@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Nov 30, 2025

Walkthrough

A comprehensive refactoring of the build system replaces cache and builder patterns with a centralized BuilderRegistry, introduces a new RunnableMetadata model for build artifacts, removes the CUDA builder, adds a TorchBuilder replacement, updates all builders to use cache-path semantics, and adds utility functions for package naming and file I/O.

Changes

Cohort / File(s) Summary
Builder Architecture Refactor
flashinfer_bench/compile/builder.py, flashinfer_bench/compile/registry.py
Replaced per-solution cache and abstract _build/_make_key pattern with explicit constructor taking key_prefix and build_dir_name; added get_package_name_and_build_path utility. Introduced new BuilderRegistry singleton for managing builder priority queue and caching built Runnables by solution hash.
Builder Implementations
flashinfer_bench/compile/builders/python_builder.py, flashinfer_bench/compile/builders/triton_builder.py, flashinfer_bench/compile/builders/tvm_ffi_builder.py, flashinfer_bench/compile/builders/torch_builder.py
Adapted all builders to new architecture: replaced _make_key/_build with get_key/build, switched from temp directories to cache-path semantics using write_sources_to_path, introduced _get_cleaner factory for resource cleanup, and updated Runnable construction to use new metadata model. TorchBuilder introduced as CUDA replacement; TritonBuilder now extends PythonBuilder.
Builder Removal & Export Updates
flashinfer_bench/compile/builders/cuda_builder.py, flashinfer_bench/compile/builders/__init__.py
Removed entire CUDABuilder implementation file; updated package exports to expose TorchBuilder instead of CUDABuilder.
Runnable & Metadata
flashinfer_bench/compile/runnable.py, flashinfer_bench/compile/__init__.py
Introduced BuildType literal and RunnableMetadata model encapsulating build_type, definition, solution, and misc fields. Updated Runnable constructor from (fn, closer, meta) to (callable, metadata, cleaner=None); replaced close() with cleanup(); added call_dps() for destination-passing style support. Exported RunnableMetadata in package init.
Utilities & Data Extensions
flashinfer_bench/compile/utils.py, flashinfer_bench/data/solution.py
Added write_sources_to_path and create_package_name utilities for deterministic package generation and secure file writing. Extended Solution with get_entry_path(), get_entry_symbol(), and hash() methods for cache keying; added CPP language support to SupportedLanguages enum.
Minor Updates
examples/win_at_p.py, flashinfer_bench/data/trace_set.py, flashinfer_bench/apply/key.py
Formatting cleanup (removed blank lines), removed TODO comment, and flattened nested Union type annotation.
Test Updates
tests/apply/test_runtime.py, tests/compile/test_builder.py, tests/compile/test_python_builder.py, tests/compile/test_runnable.py, tests/compile/test_triton_builder.py, tests/compile/test_tvm_ffi_builder.py, tests/data/test_solution.py
Updated test mocks and assertions to use new builder/Runnable APIs: replaced _make_key→get_key, _build→build, close()→cleanup(), patching public build methods instead of private ones, and removed requires_build() assertions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • flashinfer_bench/compile/builder.py: New constructor signature and removed internal cache mechanism require careful validation that all subclasses properly initialize via super().init()
  • flashinfer_bench/compile/builders/torch_builder.py: New builder replacing CUDA functionality; verify CUDA detection, dependency resolution (cuBLAS, cuDNN, CUTLASS), and torch.utils.cpp_extension.load integration
  • flashinfer_bench/compile/registry.py: New singleton BuilderRegistry pattern; verify builder priority ordering, hash-based caching logic, and cache invalidation/cleanup
  • flashinfer_bench/compile/runnable.py: Major API overhaul with new RunnableMetadata model and call_dps() implementation; verify metadata field mappings and DPS output tensor allocation
  • Builder implementations (python, triton, tvm_ffi): Each adapted to new base class pattern; verify correct initialization, cleaner lifecycle, and cache path semantics across all variants

Possibly related PRs

  • [tvm-ffi] TVMFFIBuilder #111: Introduces TVMFFIBuilder export and registry integration; directly overlaps with this PR's builder/registry refactoring at the implementation level
  • chore: rename modules #68: Rename/reorganization of modules and import paths; affects the same import surface and re-export patterns modified here
  • refactor: Enhance TvmFfiBuilder #117: Further TVM FFI builder and registry modifications; shares overlapping changes to build/cache/metadata behavior

Poem

🐰 Hop, hop! The builders march in order,
Registry keeps peace on the border,
CUDA departs, TorchBuilder stands tall,
Cache paths and metadata guide them all,
Clean runnable roads, no temp trash in sight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.68% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor: Builder System' clearly summarizes the main change of the pull request, which is a comprehensive refactoring of the builder system across multiple files including builder base class, implementations, and registry.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Ubospica, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a comprehensive refactoring of the builder system, which is central to how solution implementations are compiled and executed. The changes aim to enhance modularity, improve extensibility, and optimize performance through intelligent caching. Key updates include the introduction of a dedicated TorchBuilder for PyTorch-based CUDA extensions, a redesigned BuilderRegistry for automatic builder discovery and caching, and a more robust Runnable interface. These modifications streamline the integration and execution of diverse solution types, making the system more maintainable and efficient.

Highlights

  • Builder System Refactoring: The core builder system has undergone a significant refactoring, introducing a more modular and extensible architecture for compiling and loading solution implementations.
  • New TorchBuilder: A new TorchBuilder has been added to specifically handle CUDA solutions that leverage PyTorch's C++/CUDA extension system, replacing the previous CUDABuilder.
  • Builder Class Enhancements: The abstract Builder class has been updated with a new constructor, a public build method, and a get_package_name_and_build_path helper, streamlining how builders are defined and interact with the system.
  • Singleton BuilderRegistry with Caching: The BuilderRegistry is now a singleton, automatically discovering available builders based on priority and implementing a robust caching mechanism to prevent redundant builds of identical solutions.
  • Runnable Interface Redesign: The Runnable class has been refactored to use a more consistent API with callable, cleaner, and a new RunnableMetadata model for structured information. It also introduces a call_dps method for destination-passing style functions.
  • Solution Metadata and Hashing: The Solution class now includes methods to extract entry file paths and symbols, supports a new CPP language type, and provides a deterministic hash method crucial for the new caching strategy.
  • Centralized Utility Functions: A new flashinfer_bench/compile/utils.py file has been introduced to house common utility functions like write_sources_to_path and create_package_name, promoting code reuse and consistency across builders.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a major and well-executed refactoring of the builder system. The new design centralizes caching in a BuilderRegistry, simplifies the Builder interface, and improves the structure of concrete builder implementations. The introduction of RunnableMetadata and a unified Runnable class with call_dps support is a great improvement.

I've found a few critical issues that will cause runtime errors: a typo in the Runnable.cleanup method and a missing value in the BuildType literal that will break TorchBuilder. I also noticed that several tests seem to be broken due to the refactoring, as they call a non-existent build_with_cache method. Additionally, I have a couple of suggestions to improve code style and maintainability in TritonBuilder and TVMFFIBuilder.

Overall, this is a high-quality refactoring. Once the identified issues are addressed, the builder system will be much more robust and maintainable.

from flashinfer_bench.data import Definition
from flashinfer_bench.utils import dtype_str_to_torch_dtype

BuildType = Literal["cuda", "tvm_ffi", "python", "triton"]
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The BuildType literal is defined as Literal["cuda", "tvm_ffi", "python", "triton"]. However, the new TorchBuilder sets the build_type to "torch". This will cause a pydantic validation error at runtime. The BuildType should be updated to include "torch". Since CUDABuilder has been removed and replaced by TorchBuilder, "cuda" should probably be replaced with "torch".

Suggested change
BuildType = Literal["cuda", "tvm_ffi", "python", "triton"]
BuildType = Literal["torch", "tvm_ffi", "python", "triton"]

Comment on lines +168 to +172
if self._closer:
try:
self._closer()
finally:
self._closer = None
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

There's a typo in the cleanup method. The __init__ method sets the cleaner function to self._cleaner, but this method attempts to access self._closer. This will result in an AttributeError when cleanup is called.

Suggested change
if self._closer:
try:
self._closer()
finally:
self._closer = None
if self._cleaner:
try:
self._cleaner()
finally:
self._cleaner = None

Comment on lines +48 to +49
r1 = b.build_with_cache(d, s)
r2 = b.build_with_cache(d, s)
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

This test, along with others in test_python_builder.py, test_triton_builder.py, and test_tvm_ffi_builder.py, has been updated to call build_with_cache. However, this method does not exist on the Builder class after the refactoring. The caching logic has been moved to BuilderRegistry, and the build method on Builder no longer performs caching.

These tests appear to be broken and will fail. They need to be updated to reflect the new design, likely by testing the BuilderRegistry for caching behavior or by adapting the builder tests to call the build method directly without expecting caching at the builder level.

Additionally, DummyBuilder in this file implements _build but the Builder abstract base class now requires build to be implemented, so DummyBuilder is no longer a valid subclass of Builder.

Comment on lines +54 to 77
def build(self, definition: Definition, solution: Solution) -> Runnable:
"""Build a solution into a runnable implementation.
This method compiles/loads the solution's source code and returns a Runnable
object that can be executed with the interface specified by the definition.
Parameters
----------
definition : Definition
The problem definition that specifies the expected interface.
solution : Solution
The solution implementation to build.
Returns
-------
Runnable
An executable wrapper around the built implementation.
Raises
------
BuildError
If the build fails for any reason (compilation errors, missing dependencies, etc.).
"""
...
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The build method is now abstract, but some test implementations like DummyBuilder in tests/compile/test_builder.py still implement _build, which will cause an error. Please ensure all concrete builder implementations, including those in tests, are updated to implement the public build method.

super().__init__()
self._py_builder = py_builder
def __init__(self) -> None:
Builder.__init__(self, self._KEY_PREFIX, self._BUILD_DIR_NAME)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The __init__ method directly calls Builder.__init__, bypassing the __init__ of its direct parent, PythonBuilder. While this works currently because PythonBuilder.__init__ only calls its superclass's __init__, it's a fragile design. If PythonBuilder.__init__ is modified in the future to include additional setup logic, TritonBuilder would not inherit it. It's more robust to use super().__init__() to respect the method resolution order and make the inheritance chain clearer.

Suggested change
Builder.__init__(self, self._KEY_PREFIX, self._BUILD_DIR_NAME)
super().__init__()

Comment on lines +235 to +236
import tvm_ffi
from tvm_ffi.utils import FileLock
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The imports for tvm_ffi and tvm_ffi.utils.FileLock are inside the build method. It's generally better practice to place imports at the top of the file for clarity and to avoid repeated import overhead. Since the availability of tvm_ffi is already checked by is_available() before this builder is used, these imports can be safely moved to the top level of the file.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (12)
flashinfer_bench/compile/__init__.py (1)

16-27: Exports look correct; consider sorting __all__ alphabetically.

The new RunnableMetadata export is appropriate. Static analysis flags that __all__ is not sorted (RUF022). If the project enforces isort-style sorting, apply this diff:

 __all__ = [
     "Builder",
     "BuildError",
     "BuilderRegistry",
+    "get_builder_registry",
     "Runnable",
     "RunnableMetadata",
-    "get_builder_registry",
 ]
flashinfer_bench/compile/builders/__init__.py (1)

1-8: LGTM! TorchBuilder correctly replaces CUDABuilder.

The module docstring is helpful and the export list is updated correctly. If the project enforces sorted __all__ (RUF022), consider alphabetical ordering:

-__all__ = ["TorchBuilder", "PythonBuilder", "TritonBuilder", "TVMFFIBuilder"]
+__all__ = ["PythonBuilder", "TorchBuilder", "TritonBuilder", "TVMFFIBuilder"]
flashinfer_bench/compile/utils.py (2)

39-54: Security checks use assertions - consider using explicit exceptions.

Assertions can be disabled with python -O, which would bypass these security checks. The SourceFile model already validates paths at creation time (per the relevant snippet from flashinfer_bench/data/solution.py), so these are truly defensive. However, for defense-in-depth, explicit exceptions are safer:

-        assert not src_path_obj.is_absolute(), f"Absolute path detected: {src.path}"
-        assert ".." not in src_path_obj.parts, f"Path traversal detected: {src.path}"
+        if src_path_obj.is_absolute():
+            raise ValueError(f"Absolute path detected: {src.path}")
+        if ".." in src_path_obj.parts:
+            raise ValueError(f"Path traversal detected: {src.path}")

28-31: Minor: Docstring says "absolute paths" but returned paths may not be resolved.

The docstring states the function returns "List of absolute paths" but the code appends path / src.path without calling .resolve(). If path is relative, the returned paths will also be relative.

-        paths.append(src_path)
+        paths.append(src_path.resolve())

Alternatively, update the docstring to say "paths relative to the provided root" if that's the intended behavior.

Also applies to: 53-53

flashinfer_bench/compile/builders/torch_builder.py (1)

96-131: Robust error handling; consider using spread operator for list construction.

The broad Exception catch (line 125) is appropriate here since dependency discovery is best-effort. For the Windows linker flags (line 123), using the spread operator is cleaner per RUF005:

-                        ldflags = [f"/LIBPATH:{lib_path}"] + lib_names
+                        ldflags = [f"/LIBPATH:{lib_path}", *lib_names]
flashinfer_bench/compile/builder.py (1)

7-7: Unused import Dict.

Dict is imported but not used in this file.

-from typing import Dict, Tuple
+from typing import Tuple
flashinfer_bench/compile/builders/python_builder.py (2)

6-6: Unused import os.

os is imported but not used in this file.

 import importlib
-import os
 import shutil

41-58: get_key and _get_build_path duplicate base class logic.

These methods replicate functionality already provided by get_package_name_and_build_path in the base class. Consider removing them to reduce code duplication, unless there's a specific reason to keep them separate.

flashinfer_bench/compile/registry.py (1)

122-124: Variable hash shadows builtin.

Using hash as a variable name shadows the Python builtin. Consider renaming to sol_hash or solution_hash.

-        hash = sol.hash()
-        if hash in self._cache:
-            return self._cache[hash]
+        sol_hash = sol.hash()
+        if sol_hash in self._cache:
+            return self._cache[sol_hash]

And update the cache assignment on line 130:

-                self._cache[hash] = runnable
+                self._cache[sol_hash] = runnable
flashinfer_bench/compile/builders/triton_builder.py (2)

28-29: Direct call to Builder.__init__ bypasses PythonBuilder.__init__.

Calling Builder.__init__(self, ...) directly instead of super().__init__() is fragile. If PythonBuilder.__init__ ever adds initialization logic beyond calling its parent, TritonBuilder will miss it.

Since PythonBuilder.__init__ currently just calls super().__init__(), this works, but it's a maintenance hazard.

Consider overriding the class variables and letting the parent chain handle initialization:

 class TritonBuilder(PythonBuilder):
     ...
     _KEY_PREFIX: ClassVar[str] = "fib_triton_"
     _BUILD_DIR_NAME: ClassVar[str] = "triton"

     def __init__(self) -> None:
-        Builder.__init__(self, self._KEY_PREFIX, self._BUILD_DIR_NAME)
+        super().__init__()

But this requires PythonBuilder.__init__ to use self._KEY_PREFIX and self._BUILD_DIR_NAME instead of hardcoded class variables:

# In PythonBuilder.__init__:
def __init__(self) -> None:
    super().__init__(self._KEY_PREFIX, self._BUILD_DIR_NAME)

68-69: Mutating metadata.build_type after construction.

While this works because RunnableMetadata isn't frozen, mutating the returned Runnable's metadata is somewhat surprising. Consider passing the correct build_type during construction instead.

An alternative approach would be to override the metadata construction in the build method rather than mutating it post-hoc. However, since this would require more significant refactoring of PythonBuilder.build(), the current approach is acceptable.

flashinfer_bench/compile/builders/tvm_ffi_builder.py (1)

119-122: Consider using explicit checks instead of assertions for path validation.

Assertions can be disabled with python -O. While the comment notes this is defensive (relying on upstream validation), if this code could run in optimized mode, these security checks would be bypassed.

             # Defensive assertion: the path in the solution should be validated by the Solution
             # model validator, but we add this defensive assertion to be safe.
             src_path_obj = Path(src.path)
-            assert not src_path_obj.is_absolute(), f"Absolute path detected: {src.path}"
-            assert ".." not in src_path_obj.parts, f"Path traversal detected: {src.path}"
+            if src_path_obj.is_absolute():
+                raise BuildError(f"Absolute path detected: {src.path}")
+            if ".." in src_path_obj.parts:
+                raise BuildError(f"Path traversal detected: {src.path}")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2a7154c and d9c9328.

📒 Files selected for processing (22)
  • examples/win_at_p.py (0 hunks)
  • flashinfer_bench/apply/key.py (1 hunks)
  • flashinfer_bench/compile/__init__.py (1 hunks)
  • flashinfer_bench/compile/builder.py (1 hunks)
  • flashinfer_bench/compile/builders/__init__.py (1 hunks)
  • flashinfer_bench/compile/builders/cuda_builder.py (0 hunks)
  • flashinfer_bench/compile/builders/python_builder.py (1 hunks)
  • flashinfer_bench/compile/builders/torch_builder.py (1 hunks)
  • flashinfer_bench/compile/builders/triton_builder.py (1 hunks)
  • flashinfer_bench/compile/builders/tvm_ffi_builder.py (8 hunks)
  • flashinfer_bench/compile/registry.py (2 hunks)
  • flashinfer_bench/compile/runnable.py (2 hunks)
  • flashinfer_bench/compile/utils.py (1 hunks)
  • flashinfer_bench/data/solution.py (5 hunks)
  • flashinfer_bench/data/trace_set.py (0 hunks)
  • tests/apply/test_runtime.py (2 hunks)
  • tests/compile/test_builder.py (2 hunks)
  • tests/compile/test_python_builder.py (2 hunks)
  • tests/compile/test_runnable.py (2 hunks)
  • tests/compile/test_triton_builder.py (3 hunks)
  • tests/compile/test_tvm_ffi_builder.py (8 hunks)
  • tests/data/test_solution.py (0 hunks)
💤 Files with no reviewable changes (4)
  • flashinfer_bench/data/trace_set.py
  • examples/win_at_p.py
  • flashinfer_bench/compile/builders/cuda_builder.py
  • tests/data/test_solution.py
🧰 Additional context used
🧬 Code graph analysis (8)
flashinfer_bench/compile/__init__.py (3)
flashinfer_bench/compile/builder.py (2)
  • Builder (20-93)
  • BuildError (16-17)
flashinfer_bench/compile/registry.py (2)
  • BuilderRegistry (25-167)
  • get_builder_registry (170-180)
flashinfer_bench/compile/runnable.py (2)
  • Runnable (33-172)
  • RunnableMetadata (16-30)
flashinfer_bench/compile/utils.py (1)
flashinfer_bench/data/solution.py (2)
  • SourceFile (30-61)
  • hash (186-213)
flashinfer_bench/compile/builders/python_builder.py (6)
flashinfer_bench/compile/builder.py (2)
  • build (54-77)
  • get_package_name_and_build_path (79-93)
flashinfer_bench/compile/runnable.py (2)
  • Runnable (33-172)
  • RunnableMetadata (16-30)
flashinfer_bench/compile/utils.py (2)
  • create_package_name (58-91)
  • write_sources_to_path (12-55)
flashinfer_bench/data/definition.py (1)
  • Definition (95-363)
flashinfer_bench/data/solution.py (4)
  • Solution (100-213)
  • SupportedLanguages (13-27)
  • get_entry_path (145-156)
  • get_entry_symbol (158-170)
flashinfer_bench/env.py (1)
  • get_fib_cache_path (46-57)
flashinfer_bench/compile/builders/triton_builder.py (5)
flashinfer_bench/compile/builder.py (3)
  • Builder (20-93)
  • can_build (38-51)
  • build (54-77)
flashinfer_bench/data/solution.py (2)
  • Solution (100-213)
  • SupportedLanguages (13-27)
flashinfer_bench/compile/builders/python_builder.py (2)
  • can_build (37-39)
  • build (97-162)
flashinfer_bench/compile/builders/torch_builder.py (3)
  • is_available (135-147)
  • can_build (149-151)
  • build (241-311)
flashinfer_bench/compile/registry.py (1)
  • build (97-132)
flashinfer_bench/compile/runnable.py (5)
flashinfer_bench/data/definition.py (2)
  • get_var_values (239-279)
  • get_output_shapes (343-363)
flashinfer_bench/utils.py (1)
  • dtype_str_to_torch_dtype (39-45)
flashinfer_bench/compile/builders/python_builder.py (1)
  • cleaner (79-93)
flashinfer_bench/compile/builders/torch_builder.py (1)
  • cleaner (236-237)
flashinfer_bench/compile/builders/tvm_ffi_builder.py (1)
  • cleaner (201-202)
flashinfer_bench/compile/builders/__init__.py (4)
flashinfer_bench/compile/builders/python_builder.py (1)
  • PythonBuilder (19-162)
flashinfer_bench/compile/builders/torch_builder.py (1)
  • TorchBuilder (31-311)
flashinfer_bench/compile/builders/triton_builder.py (1)
  • TritonBuilder (14-70)
flashinfer_bench/compile/builders/tvm_ffi_builder.py (1)
  • TVMFFIBuilder (23-296)
tests/compile/test_builder.py (2)
flashinfer_bench/compile/builders/python_builder.py (2)
  • get_key (41-43)
  • cleaner (79-93)
flashinfer_bench/compile/runnable.py (1)
  • Runnable (33-172)
tests/apply/test_runtime.py (5)
flashinfer_bench/compile/builders/python_builder.py (2)
  • PythonBuilder (19-162)
  • build (97-162)
flashinfer_bench/compile/builders/torch_builder.py (1)
  • build (241-311)
flashinfer_bench/compile/registry.py (1)
  • build (97-132)
flashinfer_bench/data/solution.py (1)
  • Solution (100-213)
flashinfer_bench/compile/runnable.py (1)
  • Runnable (33-172)
🪛 GitHub Actions: .github/workflows/linting.yaml
flashinfer_bench/compile/builders/tvm_ffi_builder.py

[error] 59-59: ruff: F401 'tvm_ffi' imported but unused. Remove unused import.

🪛 Ruff (0.14.6)
flashinfer_bench/compile/__init__.py

20-27: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

flashinfer_bench/data/solution.py

202-202: Probable use of insecure hash functions in hashlib: sha1

(S324)

flashinfer_bench/compile/builders/python_builder.py

85-86: try-except-pass detected, consider logging the exception

(S110)


85-85: Do not catch blind exception: Exception

(BLE001)


125-125: Avoid specifying long messages outside the exception class

(TRY003)


141-141: Avoid specifying long messages outside the exception class

(TRY003)


147-149: Avoid specifying long messages outside the exception class

(TRY003)


153-153: Avoid specifying long messages outside the exception class

(TRY003)

flashinfer_bench/compile/registry.py

58-58: Avoid specifying long messages outside the exception class

(TRY003)


72-73: try-except-pass detected, consider logging the exception

(S110)


72-72: Do not catch blind exception: Exception

(BLE001)


132-132: Avoid specifying long messages outside the exception class

(TRY003)

flashinfer_bench/compile/builders/torch_builder.py

123-123: Consider [f"/LIBPATH:{lib_path}", *lib_names] instead of concatenation

Replace with [f"/LIBPATH:{lib_path}", *lib_names]

(RUF005)


125-125: Do not catch blind exception: Exception

(BLE001)


211-214: Avoid specifying long messages outside the exception class

(TRY003)


269-272: Avoid specifying long messages outside the exception class

(TRY003)


295-295: Avoid specifying long messages outside the exception class

(TRY003)


300-300: Avoid specifying long messages outside the exception class

(TRY003)

flashinfer_bench/compile/runnable.py

128-131: Avoid specifying long messages outside the exception class

(TRY003)

flashinfer_bench/compile/builders/__init__.py

8-8: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

tests/compile/test_builder.py

27-27: Unused method argument: definition

(ARG002)


27-27: Unused method argument: solution

(ARG002)

flashinfer_bench/compile/builders/tvm_ffi_builder.py

267-269: Avoid specifying long messages outside the exception class

(TRY003)


293-293: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run unit tests on ubuntu-latest and Python 3.10
  • GitHub Check: Run unit tests on ubuntu-latest and Python 3.12
  • GitHub Check: Run unit tests on ubuntu-latest and Python 3.11
  • GitHub Check: Run unit tests on ubuntu-latest and Python 3.13
🔇 Additional comments (36)
flashinfer_bench/apply/key.py (1)

15-15: Clean type annotation refactoring.

Flattening the nested Union to Union[int, float, bool] is more idiomatic and improves readability without affecting runtime behavior.

flashinfer_bench/data/solution.py (2)

24-27: LGTM! New CPP language variant properly added.

The new CPP enum value follows the existing pattern and documentation style.


145-170: LGTM! Clean helper methods for entry point parsing.

Both get_entry_path() and get_entry_symbol() properly extract components from the validated entry point format. Using split("::")[-1] for the symbol is a good defensive choice.

tests/compile/test_python_builder.py (2)

41-41: LGTM! Correctly updated to use cache-aware build API.

The test properly sets up an isolated cache directory and uses the new build_with_cache method.


80-80: LGTM! Consistent API migration.

Same pattern applied correctly with proper cache cleanup via b.clear_cache().

tests/compile/test_builder.py (3)

21-22: LGTM! Method renamed to match updated builder interface.

The get_key naming is consistent with the production PythonBuilder.get_key method shown in the relevant code snippets.


48-50: LGTM! Cache behavior correctly tested.

The test verifies that build_with_cache returns the same instance on repeated calls (r1 is r2), confirming cache hit behavior.


27-30: Verify metadata parameter type compatibility.

The Runnable constructor expects metadata: RunnableMetadata, but the test passes a plain dict {"dummy": True}. Confirm whether RunnableMetadata accepts dict input (e.g., via TypedDict or dataclass supporting dict initialization). If RunnableMetadata is a TypedDict, this usage is valid; if it's a stricter type, adjust the test accordingly.

tests/compile/test_triton_builder.py (3)

57-57: LGTM! Import guard test correctly uses cache-aware API.

The test verifies that BuildError is raised when Triton is unavailable.


77-77: LGTM! Minimum test updated consistently.

Proper cache directory setup and availability state reset via monkeypatch.setattr.


141-141: LGTM! Vector add test with correct API usage.

The Triton kernel test properly exercises the cached build path and verifies execution correctness.

tests/compile/test_tvm_ffi_builder.py (8)

123-123: LGTM! CPU build test migrated to cache-aware API.

Test correctly uses the isolated_cache fixture and verifies kernel execution.


170-170: LGTM! CUDA GPU test updated consistently.


254-262: LGTM! Builder-level caching test properly validates cache behavior.

The test measures build time vs cache load time, demonstrating the caching benefit.


303-311: LGTM! Cross-builder caching test correctly validates shared cache.

Tests that different TVMFFIBuilder instances can share cached artifacts.


353-353: LGTM! call_dest test updated.


404-404: LGTM! Error handling test for invalid entry point.


436-436: LGTM! Error handling test for invalid sources.


469-469: LGTM! Subdirectory source handling test updated.

flashinfer_bench/compile/__init__.py (1)

1-14: LGTM! Clear and helpful docstring.

The module docstring provides a good overview of the package components and a concise workflow example.

tests/apply/test_runtime.py (1)

246-272: LGTM! Correctly updated to patch the public build method.

The change from _build to build aligns with the refactored builder API. The patch/restore logic is correct.

Note: This test is currently skipped (line 163). Ensure the underlying issue is tracked for resolution.

tests/compile/test_runnable.py (2)

5-5: LGTM! Import updated correctly.


17-27: Test correctly uses the new Runnable API, but there's a potential bug in the Runnable.cleanup() method.

The test construction and cleanup calls are correct. However, the Runnable class may have an inconsistency where the constructor assigns to self._cleaner but the cleanup() method references self._closer. This mismatch would cause the test to fail at runtime with an AttributeError since _closer would be undefined.

flashinfer_bench/compile/utils.py (1)

58-91: LGTM! Package name generation is sound.

The normalization handles edge cases well (digit prefix, special characters). Using the first 6 characters of the SHA1 hash provides reasonable uniqueness for cache keys.

flashinfer_bench/compile/builders/torch_builder.py (2)

50-68: LGTM! Clean dependency discovery pattern.

The lazy discovery of CUDA dependencies at init time with graceful fallback is appropriate.


284-311: Build logic is correct and well-structured.

The compilation flow using torch.utils.cpp_extension.load() is appropriate, with proper error handling and metadata construction. The cleaner pattern ensures resources can be released.

flashinfer_bench/compile/builder.py (1)

20-93: Well-structured abstract base class.

The Builder abstraction is clean and provides a good foundation for concrete builders. The get_package_name_and_build_path helper centralizes path computation, reducing duplication across subclasses.

flashinfer_bench/compile/builders/python_builder.py (2)

79-95: Cleaner implementation is sound.

The cleaner correctly handles module unloading, sys.path cleanup, and directory removal. The broad except Exception: pass is acceptable here since cleanup failures shouldn't propagate and prevent further cleanup steps.


97-162: Build method is well-structured with proper error handling.

The build flow correctly validates the entry file, writes sources, imports the module, and constructs the Runnable with appropriate metadata. Error handling properly invokes the cleaner before raising BuildError.

flashinfer_bench/compile/registry.py (3)

62-74: Clear method correctly handles cleanup failures.

The broad exception catch is appropriate here to ensure all runnables are processed even if some cleanup operations fail.


134-167: build_reference creates a well-formed pseudo-solution.

The method correctly constructs a Solution object with appropriate defaults for building reference implementations.


89-94: Missing is_available() in Builder interface.

builder_type.is_available() is called on line 92, but is_available() is not defined as an abstract method in the Builder base class. If any builder subclass doesn't implement this method, this will raise an AttributeError at runtime.

Consider adding is_available() as an abstract static method in Builder, or add a default implementation that returns True:

# In builder.py
+    @staticmethod
+    def is_available() -> bool:
+        """Check if this builder is available in the current environment.
+        
+        Returns True by default. Subclasses should override if they have
+        optional dependencies.
+        """
+        return True
flashinfer_bench/compile/builders/triton_builder.py (1)

31-48: is_available() and can_build() are correctly implemented.

The availability check properly handles import failures, and can_build correctly filters by language type.

flashinfer_bench/compile/runnable.py (2)

16-31: RunnableMetadata model is well-defined.

The Pydantic model provides good structure for metadata with appropriate fields and documentation.


93-159: call_dps implementation is solid.

The DPS wrapper correctly:

  • Validates the presence of the definition in metadata
  • Derives output shapes from input shapes
  • Handles device placement
  • Normalizes return values (None/single/tuple)
flashinfer_bench/compile/builders/tvm_ffi_builder.py (1)

206-296: LGTM!

The build method is well-implemented with proper multi-process safety through the FileLock and double-check pattern after acquiring the lock. The caching logic correctly handles concurrent builds, and the Runnable construction with metadata and cleaner follows the new builder pattern.

Comment on lines +127 to +130
package_name, build_path = self.get_package_name_and_build_path(solution)
module_name = package_name + "." + ".".join(Path(entry_file).with_suffix("").parts)

build_path = self._get_build_path(package_name)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Redundant build_path computation.

get_package_name_and_build_path already returns build_path, but it's immediately overwritten by _get_build_path. This is wasteful and confusing. Either use the base class method entirely, or remove the call to get_package_name_and_build_path and just use get_key + _get_build_path.

-        package_name, build_path = self.get_package_name_and_build_path(solution)
+        package_name, build_path = self.get_package_name_and_build_path(solution)
         module_name = package_name + "." + ".".join(Path(entry_file).with_suffix("").parts)

-        build_path = self._get_build_path(package_name)

Alternatively, if _get_build_path logic differs intentionally, use:

-        package_name, build_path = self.get_package_name_and_build_path(solution)
+        package_name = self.get_key(solution)
         module_name = package_name + "." + ".".join(Path(entry_file).with_suffix("").parts)

         build_path = self._get_build_path(package_name)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In flashinfer_bench/compile/builders/python_builder.py around lines 127 to 130,
there's a redundant call to get_package_name_and_build_path whose returned
build_path is immediately overwritten by _get_build_path; remove the unnecessary
value and either (A) call get_package_name_and_build_path to obtain both
package_name and build_path and stop calling _get_build_path, or (B) if you only
need package_name here, replace get_package_name_and_build_path with get_key (or
another method that returns only the package_name) and then call
_get_build_path(package_name); update the surrounding code to use the single,
consistent source of truth for build_path.

Comment on lines +280 to +282
extra_include_paths, extra_ldflags = self._get_dependency_flags(solution)
# Add build directory to include paths
extra_include_paths.append(build_dir)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Type mismatch: build_dir is Path but extra_include_paths is List[str].

extra_include_paths collects strings, but build_dir is a Path object. This may cause issues with torch.utils.cpp_extension.load() depending on how it handles the list. Convert to string for consistency:

         # Add build directory to include paths
-        extra_include_paths.append(build_dir)
+        extra_include_paths.append(str(build_dir))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
extra_include_paths, extra_ldflags = self._get_dependency_flags(solution)
# Add build directory to include paths
extra_include_paths.append(build_dir)
extra_include_paths, extra_ldflags = self._get_dependency_flags(solution)
# Add build directory to include paths
extra_include_paths.append(str(build_dir))
🤖 Prompt for AI Agents
In flashinfer_bench/compile/builders/torch_builder.py around lines 280 to 282,
extra_include_paths is a list of strings but build_dir is a Path object; convert
build_dir to a string before appending so the list is homogeneous (e.g., append
str(build_dir)) to avoid type errors when passing extra_include_paths to
torch.utils.cpp_extension.load().

Comment on lines +56 to +63
@staticmethod
def is_available() -> bool:
"""Check if TVM-FFI is available in the current environment."""
try:
import tvm_ffi
except ImportError:
return False
return True
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix unused import linter error (F401) breaking CI.

The pipeline is failing because ruff sees the import tvm_ffi as unused. Add a noqa comment to suppress this false positive since the import is intentionally used only for availability checking.

     @staticmethod
     def is_available() -> bool:
         """Check if TVM-FFI is available in the current environment."""
         try:
-            import tvm_ffi
+            import tvm_ffi  # noqa: F401
         except ImportError:
             return False
         return True
🧰 Tools
🪛 GitHub Actions: .github/workflows/linting.yaml

[error] 59-59: ruff: F401 'tvm_ffi' imported but unused. Remove unused import.

🤖 Prompt for AI Agents
In flashinfer_bench/compile/builders/tvm_ffi_builder.py around lines 56 to 63,
the try/except import of tvm_ffi is flagged by the linter as an unused import
(F401); modify the import line to suppress this false positive by adding a noqa
comment (e.g., "import tvm_ffi  # noqa: F401") so the availability check remains
intact and the linter error is silenced.

Comment on lines +12 to +13
BuildType = Literal["cuda", "tvm_ffi", "python", "triton"]
"""The type of build that produced this runnable. Each builder has a unique build type."""
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

BuildType is missing "torch".

TorchBuilder sets build_type="torch" (per torch_builder.py line 299), but BuildType only includes "cuda", "tvm_ffi", "python", "triton". This will cause Pydantic validation errors when building CUDA solutions with TorchBuilder.

-BuildType = Literal["cuda", "tvm_ffi", "python", "triton"]
+BuildType = Literal["python", "triton", "torch", "tvm_ffi"]

Also update the docstring on line 24 which mentions "torch" but it's not currently in the type.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
BuildType = Literal["cuda", "tvm_ffi", "python", "triton"]
"""The type of build that produced this runnable. Each builder has a unique build type."""
BuildType = Literal["python", "triton", "torch", "tvm_ffi"]
"""The type of build that produced this runnable. Each builder has a unique build type."""
🤖 Prompt for AI Agents
In flashinfer_bench/compile/runnable.py around lines 12-13 (and update the
docstring at line 24), the BuildType Literal is missing the "torch" variant
which causes Pydantic validation failures because TorchBuilder uses
build_type="torch"; add "torch" to the Literal union for BuildType and update
the docstring on line 24 to include "torch" in the listed build types so the
type and documentation match.

Comment on lines +168 to +172
if self._closer:
try:
self._closer()
finally:
self._closer = None
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical bug: _closer should be _cleaner.

The cleanup() method references self._closer, but the field is defined as self._cleaner (lines 46, 68). This will raise an AttributeError when cleanup() is called.

     def cleanup(self) -> None:
         """Clean up build artifacts and release resources.

         This method calls the cleaner function if one was provided during construction.
         It is idempotent: calling it multiple times is safe and has no additional effect
         after the first call.
         """
-        if self._closer:
+        if self._cleaner:
             try:
-                self._closer()
+                self._cleaner()
             finally:
-                self._closer = None
+                self._cleaner = None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if self._closer:
try:
self._closer()
finally:
self._closer = None
if self._cleaner:
try:
self._cleaner()
finally:
self._cleaner = None
🤖 Prompt for AI Agents
In flashinfer_bench/compile/runnable.py around lines 168 to 172, the cleanup()
method mistakenly references self._closer (which does not exist) instead of the
defined self._cleaner; update the block to call self._cleaner() inside the try
and then set self._cleaner = None in the finally so the cleanup uses the correct
attribute and clears it afterward.

Comment on lines +186 to +213
def hash(self) -> str:
"""Compute a deterministic hash of the solution content.
The hash is computed from all fields that affect the solution's behavior:
name, definition, language, entry point, dependencies, and all source file
paths and contents. This ensures that any meaningful change to the solution
results in a different hash.
The hash is used for caching build artifacts - solutions with the same hash
can reuse the same cached build result.
Returns
-------
bool
True if the solution requires building (has build commands or uses CUDA),
False otherwise.
str
A SHA1 hash (40 hex characters) uniquely identifying this solution's content.
"""
return self.spec.language == SupportedLanguages.CUDA
h = hashlib.sha1()
for s in (
self.name,
self.definition,
self.spec.language,
self.spec.entry_point,
*self.spec.dependencies,
*(part for src in self.sources for part in (src.path, src.content)),
):
h.update(s.encode())

return h.hexdigest()
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential TypeError if dependencies is None.

The dependencies field is typed as Optional[List[NonEmptyString]], meaning it can be explicitly set to None. If that happens, line 208 will raise TypeError: 'NoneType' object is not iterable when unpacking with *self.spec.dependencies.

         h = hashlib.sha1()
         for s in (
             self.name,
             self.definition,
             self.spec.language,
             self.spec.entry_point,
-            *self.spec.dependencies,
+            *(self.spec.dependencies or []),
             *(part for src in self.sources for part in (src.path, src.content)),
         ):
             h.update(s.encode())

Regarding the static analysis hint about SHA1 (S324): SHA1 is appropriate here since it's used for content-based cache keying, not cryptographic security. The concern is a false positive in this context.

🧰 Tools
🪛 Ruff (0.14.6)

202-202: Probable use of insecure hash functions in hashlib: sha1

(S324)

🤖 Prompt for AI Agents
In flashinfer_bench/data/solution.py around lines 186 to 213, the hash() method
unpacks self.spec.dependencies with *self.spec.dependencies which will raise
TypeError if dependencies is None; change to iterate over self.spec.dependencies
or an empty iterable (e.g. self.spec.dependencies or []) when building the tuple
of fields so None is treated as no dependencies, and ensure each item appended
is a str before calling .encode() (cast or skip None) so h.update(...) always
receives bytes.

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.

1 participant