-
Notifications
You must be signed in to change notification settings - Fork 637
[Tool] Provide layout visualization tool #1353
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
b59e6e8
3f8ab91
70519cd
67f27dd
695009b
79b0914
00feb64
3495e81
18c93c4
360854b
bb9d49f
eac27c9
fd25d67
86eb08a
2da20b0
03dc6b8
5d075f2
7b1532d
581beab
376b63c
cf9b3c7
c1230a4
9282abd
d897a95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import tilelang | ||
| import tilelang.language as T | ||
|
|
||
|
|
||
| # use pass_configs to enable layout visualization | ||
| @tilelang.jit(out_idx=[-1], pass_configs={tilelang.PassConfigKey.TL_ENABLE_LAYOUT_VISUAL: True}) | ||
| def matmul(M, N, K, block_M, block_N, block_K, dtype="float16", accum_dtype="float"): | ||
|
|
||
| @T.prim_func | ||
| def gemm( | ||
| A: T.Tensor((M, K), dtype), | ||
| B: T.Tensor((K, N), dtype), | ||
| C: T.Tensor((M, N), dtype), | ||
| ): | ||
| with T.Kernel(T.ceildiv(N, block_N), T.ceildiv(M, block_M), threads=128) as (bx, by): | ||
| A_shared = T.alloc_shared((block_M, block_K), dtype) | ||
| B_shared = T.alloc_shared((block_K, block_N), dtype) | ||
| C_local = T.alloc_fragment((block_M, block_N), accum_dtype) | ||
|
|
||
| T.clear(C_local) | ||
| for k in T.Pipelined(T.ceildiv(K, block_K), num_stages=3): | ||
| T.copy(A[by * block_M, k * block_K], A_shared) | ||
| T.copy(B[k * block_K, bx * block_N], B_shared) | ||
| T.gemm(A_shared, B_shared, C_local) | ||
|
|
||
| T.copy(C_local, C[by * block_M, bx * block_N]) | ||
|
|
||
| return gemm | ||
|
|
||
|
|
||
| def main(): | ||
| kernel = matmul(128, 128, 128, 32, 32, 32) | ||
|
|
||
| import torch | ||
|
|
||
| a = torch.randn(128, 128).cuda().half() | ||
| b = torch.randn(128, 128).cuda().half() | ||
|
|
||
| c = kernel(a, b) | ||
|
|
||
| ref_c = a @ b | ||
|
|
||
| torch.testing.assert_close(c, ref_c, rtol=1e-2, atol=1e-2) | ||
| print("All check passed.") | ||
|
|
||
| # print the layout visualization result and save figures to ./tmp. | ||
| ''' | ||
| C_local layout inference: | ||
| Shape: [32, 32] -> [8] | ||
| Thread: _j // 16 * 64 + _i // 16 * 32 + _i % 8 * 4 + _j % 8 // 2 | ||
| Index: [_j % 16 // 8 * 4 + _i % 16 // 8 * 2 + _j % 2] | ||
| ''' | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| from .plot_layout import plot_layout # noqa: F401 | ||
| from .Analyzer import * | ||
| from .layout_visual import LayoutVisual # noqa: F401 |
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,50 @@ | ||||||||||||||||
| import tilelang.language as T | ||||||||||||||||
| from tvm import tir | ||||||||||||||||
| from tvm.tir import PyStmtExprVisitor | ||||||||||||||||
|
|
||||||||||||||||
| from tvm.tir.transform import prim_func_pass | ||||||||||||||||
| from tilelang.tools.plot_layout import plot_layout | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| def print_layout_format(layout: T.Fragment) -> str: | ||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This only works for fragment layout, I think we should rename it into |
||||||||||||||||
| input_shape = layout.get_input_shape() | ||||||||||||||||
| output_shape = layout.get_output_shape() | ||||||||||||||||
| lines = [ | ||||||||||||||||
| f" Shape: {input_shape} -> {output_shape}", f" Thread: {layout.forward_thread}", | ||||||||||||||||
| f" Index: {layout.forward_index}" | ||||||||||||||||
| ] | ||||||||||||||||
|
|
||||||||||||||||
| return "\n".join(lines) | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| @tir.functor.visitor | ||||||||||||||||
| class _LayoutVisualVisitor(PyStmtExprVisitor): | ||||||||||||||||
|
|
||||||||||||||||
| def __init__(self): | ||||||||||||||||
| super().__init__() | ||||||||||||||||
| self.layout_found = [] | ||||||||||||||||
| self.processed_layouts = set() | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Remove unused The def __init__(self):
super().__init__()
- self.layout_found = []
self.processed_layouts = set()📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| def visit_block_(self, op: tir.Block) -> None: | ||||||||||||||||
| if "layout_map" in op.annotations: | ||||||||||||||||
| layout_map = op.annotations["layout_map"] | ||||||||||||||||
|
|
||||||||||||||||
| for key, layout in layout_map.items(): | ||||||||||||||||
| if isinstance(layout, T.Fragment): | ||||||||||||||||
| layout_id = str(layout) | ||||||||||||||||
| if layout_id not in self.processed_layouts: | ||||||||||||||||
| print(f"{key} layout inference:") | ||||||||||||||||
| print(print_layout_format(layout)) | ||||||||||||||||
| plot_layout(layout, name=f"{key}_layout") | ||||||||||||||||
| self.processed_layouts.add(layout_id) | ||||||||||||||||
|
|
||||||||||||||||
| self.visit_stmt(op.body) | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| def LayoutVisual(): | ||||||||||||||||
|
|
||||||||||||||||
| def pass_fn(func: tir.PrimFunc, mod, ctx): | ||||||||||||||||
| _LayoutVisualVisitor().visit_stmt(func.body) | ||||||||||||||||
| return func | ||||||||||||||||
|
|
||||||||||||||||
| return prim_func_pass(pass_fn, opt_level=0) | ||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import tilelang.language as T | ||
|
|
||
|
|
||
| def plot_layout(layout: T.Layout, | ||
| def plot_layout(layout: T.Fragment, | ||
| save_directory="./tmp", | ||
| name: str = "layout", | ||
| colormap: str = "RdPu", | ||
|
|
@@ -82,6 +82,12 @@ def plot_layout(layout: T.Layout, | |
| raw_colors = [cmap(i) for i in range(num_threads)] | ||
| colors = raw_colors.copy() | ||
|
|
||
| # Show the distribution of registers in each thread of a warp. | ||
| warp_size = 32 | ||
| spectral_camp = plt.get_cmap("hsv", warp_size * 6) | ||
| for i in range(warp_size): | ||
| colors[i] = spectral_camp(i * 6) | ||
|
Comment on lines
+88
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The new warp-aware coloring loop assumes at least 32 threads, but Useful? React with 👍 / 👎.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @Cunxiao2002 |
||
|
|
||
|
LeiWang1999 marked this conversation as resolved.
|
||
| # Determine the number of rows and columns in the input shape | ||
| nrows, ncols = input_shape | ||
| # Adjust figure size to maintain square cells | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be better to be C_local inferenced layout: