mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-08-18 01:58:26 +00:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbf8e641ac | ||
|
|
ac53c8866e | ||
|
|
c260482d84 | ||
|
|
26d54530d6 | ||
|
|
4300ebc455 | ||
|
|
7596c1b8f5 | ||
|
|
001b3710d3 | ||
|
|
a62dc9ceb5 | ||
|
|
464c56862f | ||
|
|
ac96d98745 | ||
|
|
89be3590aa | ||
|
|
95ad047445 | ||
|
|
e625c27598 | ||
|
|
6ec96f6088 | ||
|
|
9471157346 |
@@ -625,11 +625,11 @@ jobs:
|
||||
- name: benchmark openpilot 0.9.9 dmonitoring
|
||||
run: BENCHMARK_LOG=openpilot_0_9_9_dmonitoring PYTHONPATH=. NOLOCALS=1 FLOAT16=1 IMAGE=2 QCOM=1 taskset -c 4-7 python3 test/external/external_benchmark_openpilot.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/dmonitoring_model.onnx
|
||||
- name: openpilot compile3 0.9.9 driving_vision
|
||||
run: PYTHONPATH="." ASSERT_MIN_STEP_TIME=22 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/driving_vision.onnx
|
||||
run: PYTHONPATH="." ASSERT_MIN_STEP_TIME=18 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/driving_vision.onnx
|
||||
- name: openpilot compile3 0.9.9 driving_policy
|
||||
run: PYTHONPATH="." ASSERT_MIN_STEP_TIME=7 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/driving_policy.onnx
|
||||
- name: openpilot compile3 0.9.9 dmonitoring
|
||||
run: PYTHONPATH="." ASSERT_MIN_STEP_TIME=15 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/dmonitoring_model.onnx
|
||||
run: PYTHONPATH="." ASSERT_MIN_STEP_TIME=12 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://github.com/commaai/openpilot/raw/v0.9.9/selfdrive/modeld/models/dmonitoring_model.onnx
|
||||
- name: openpilot compile3 Space Lab policy + vision
|
||||
run: |
|
||||
PYTHONPATH="." ASSERT_MIN_STEP_TIME=4 QCOM=1 taskset -c 4-7 python3 examples/openpilot/compile3.py https://gitlab.com/commaai/openpilot-lfs.git/gitlab-lfs/objects/22aec22a10ce09384d4a4af2a0bbff08d54af7e0c888503508f356fae4ff0e29
|
||||
|
||||
+2
-2
@@ -435,8 +435,8 @@ generate_sqtt() {
|
||||
-o extra/sqtt/rocprof/rocprof.py
|
||||
fixup extra/sqtt/rocprof/rocprof.py
|
||||
sed -i '1s/^/# pylint: skip-file\n/' extra/sqtt/rocprof/rocprof.py
|
||||
sed -i "s/import ctypes/import ctypes, tinygrad.helpers.fetch as tgfetch/g" extra/sqtt/rocprof/rocprof.py
|
||||
sed -i "s|FunctionFactoryStub()|ctypes.CDLL(str(tgfetch('https://github.com/ROCm/rocprof-trace-decoder/raw/5420409ad0963b2d76450add067b9058493ccbd0/releases/linux_glibc_2_28_x86_64/librocprof-trace-decoder.so')))|g" extra/sqtt/rocprof/rocprof.py
|
||||
sed -i "s/import ctypes/import ctypes\nfrom tinygrad.helpers import fetch/g" extra/sqtt/rocprof/rocprof.py
|
||||
sed -i "s|FunctionFactoryStub()|ctypes.CDLL(str(fetch('https://github.com/ROCm/rocprof-trace-decoder/raw/5420409ad0963b2d76450add067b9058493ccbd0/releases/linux_glibc_2_28_x86_64/librocprof-trace-decoder.so')))|g" extra/sqtt/rocprof/rocprof.py
|
||||
}
|
||||
|
||||
generate_webgpu() {
|
||||
|
||||
@@ -156,6 +156,9 @@ class RGP:
|
||||
sqtt_events = [x for x in profile if isinstance(x, ProfileSQTTEvent) and x.device == device_event.device]
|
||||
if len(sqtt_events) == 0: raise RuntimeError(f"Device {device_event.device} doesn't contain SQTT data")
|
||||
device_props = sqtt_events[0].props
|
||||
gfx_ver = device_props['gfx_target_version'] // 10000
|
||||
gfx_iplvl = getattr(sqtt, f"SQTT_GFXIP_LEVEL_GFXIP_{device_props['gfx_target_version']//10000}_{(device_props['gfx_target_version']//100)%100}",
|
||||
getattr(sqtt, f"SQTT_GFXIP_LEVEL_GFXIP_{device_props['gfx_target_version']//10000}", None))
|
||||
sqtt_itrace_enabled = any([event.itrace for event in sqtt_events])
|
||||
sqtt_itrace_masked = not all_same([event.itrace for event in sqtt_events])
|
||||
sqtt_itrace_se_mask = functools.reduce(lambda a,b: a|b, [int(event.itrace) << event.se for event in sqtt_events], 0) if sqtt_itrace_masked else 0
|
||||
@@ -193,7 +196,7 @@ class RGP:
|
||||
flags=0,
|
||||
trace_shader_core_clock=0x93f05080,
|
||||
trace_memory_clock=0x4a723a40,
|
||||
device_id={110000: 0x744c, 110003: 0x7480}[device_props['gfx_target_version']],
|
||||
device_id={110000: 0x744c, 110003: 0x7480, 120001: 0x7550}[device_props['gfx_target_version']],
|
||||
device_revision_id=0xc8,
|
||||
vgprs_per_simd=1536,
|
||||
sgprs_per_simd=128*16,
|
||||
@@ -207,7 +210,7 @@ class RGP:
|
||||
sgpr_alloc_granularity=128,
|
||||
hardware_contexts=8,
|
||||
gpu_type=sqtt.SQTT_GPU_TYPE_DISCRETE,
|
||||
gfxip_level=sqtt.SQTT_GFXIP_LEVEL_GFXIP_11_0,
|
||||
gfxip_level=gfx_iplvl,
|
||||
gpu_index=0,
|
||||
gds_size=0,
|
||||
gds_per_shader_engine=0,
|
||||
@@ -258,7 +261,7 @@ class RGP:
|
||||
major_version=0, minor_version=2,
|
||||
),
|
||||
shader_engine_index=sqtt_event.se,
|
||||
sqtt_version=sqtt.SQTT_VERSION_3_2,
|
||||
sqtt_version={11: sqtt.SQTT_VERSION_3_2, 12: sqtt.SQTT_VERSION_3_3}.get(gfx_ver),
|
||||
_0=sqtt.union_sqtt_file_chunk_sqtt_desc_0(
|
||||
v1=sqtt.struct_sqtt_file_chunk_sqtt_desc_0_v1(
|
||||
instrumentation_spec_version=1,
|
||||
|
||||
+10
-9
@@ -20,14 +20,15 @@ class InstInfo:
|
||||
class _ROCParseCtx:
|
||||
def __init__(self, sqtt_evs:list[ProfileSQTTEvent], prog_evs:list[ProfileProgramEvent]):
|
||||
self.sqtt_evs, self.prog_evs = iter(sqtt_evs), prog_evs
|
||||
self.wave_events = {}
|
||||
self.wave_events, self.disasms, self.addr2prg = {}, {}, {}
|
||||
|
||||
for prog in prog_evs:
|
||||
for addr, info in comgr_get_address_table(prog.lib).items():
|
||||
self.disasms[prog.base + addr] = info
|
||||
self.addr2prg[prog.base + addr] = prog
|
||||
|
||||
def next_sqtt(self): return next(self.sqtt_evs, None)
|
||||
def find_program(self, idx): return self.prog_evs[idx]
|
||||
def get_instr_info(self, idx, exec_addr): return self.disasm_program(idx)[exec_addr - self.find_program(idx).base]
|
||||
|
||||
@functools.lru_cache(None)
|
||||
def disasm_program(self, idx): return comgr_get_address_table(self.find_program(idx).lib)
|
||||
def find_program(self, addr): return self.addr2prg[addr]
|
||||
|
||||
def on_occupancy_ev(self, ev):
|
||||
if DEBUG >= 4: print("OCC", ev.time, ev.cu, ev.simd, ev.wave_id, ev.start)
|
||||
@@ -39,10 +40,10 @@ class _ROCParseCtx:
|
||||
for j in range(ev.instructions_size):
|
||||
inst_ev = ev.instructions_array[j]
|
||||
inst_typ = rocprof.rocprofiler_thread_trace_decoder_inst_category_t__enumvalues[inst_ev.category]
|
||||
asm.setdefault(inst_ev.pc.address, InstInfo(typ=inst_typ, inst=self.get_instr_info(inst_ev.pc.code_object_id, inst_ev.pc.address)[0]))
|
||||
asm.setdefault(inst_ev.pc.address, InstInfo(typ=inst_typ, inst=self.disasms[inst_ev.pc.address][0]))
|
||||
asm[inst_ev.pc.address].on_ev(inst_ev)
|
||||
|
||||
self.wave_events[(self.find_program(ev.instructions_array[0].pc.code_object_id).name, ev.wave_id, ev.cu, ev.simd)] = asm
|
||||
self.wave_events[(self.find_program(ev.instructions_array[0].pc.address).name, ev.wave_id, ev.cu, ev.simd)] = asm
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
@@ -78,7 +79,7 @@ if __name__ == "__main__":
|
||||
|
||||
@rocprof.rocprof_trace_decoder_isa_callback_t
|
||||
def isa_cb(instr_ptr, mem_size_ptr, size_ptr, pc, data_ptr):
|
||||
instr, mem_size_ptr[0] = ROCParseCtx.get_instr_info(pc.code_object_id, pc.address)
|
||||
instr, mem_size_ptr[0] = ROCParseCtx.disasms[pc.address]
|
||||
|
||||
# this is the number of bytes to next instruction, set to 0 for end_pgm
|
||||
if instr == "s_endpgm": mem_size_ptr[0] = 0
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
# POINTER_SIZE is: 8
|
||||
# LONGDOUBLE_SIZE is: 16
|
||||
#
|
||||
import ctypes, tinygrad.helpers.fetch as tgfetch
|
||||
import ctypes
|
||||
from tinygrad.helpers import fetch
|
||||
|
||||
|
||||
class AsDictMixin:
|
||||
@@ -155,7 +156,7 @@ class FunctionFactoryStub:
|
||||
# You can either re-run clan2py with -l /path/to/library.so
|
||||
# Or manually fix this by comment the ctypes.CDLL loading
|
||||
_libraries = {}
|
||||
_libraries['FIXME_STUB'] = ctypes.CDLL(str(tgfetch('https://github.com/ROCm/rocprof-trace-decoder/raw/5420409ad0963b2d76450add067b9058493ccbd0/releases/linux_glibc_2_28_x86_64/librocprof-trace-decoder.so'))) # ctypes.CDLL('FIXME_STUB')
|
||||
_libraries['FIXME_STUB'] = ctypes.CDLL(str(fetch('https://github.com/ROCm/rocprof-trace-decoder/raw/5420409ad0963b2d76450add067b9058493ccbd0/releases/linux_glibc_2_28_x86_64/librocprof-trace-decoder.so'))) # ctypes.CDLL('FIXME_STUB')
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ enum sqtt_version
|
||||
SQTT_VERSION_2_3 = 0x6, /* GFX9 */
|
||||
SQTT_VERSION_2_4 = 0x7, /* GFX10+ */
|
||||
SQTT_VERSION_3_2 = 0xb, /* GFX11+ */
|
||||
SQTT_VERSION_3_3 = 0xc, /* GFX12+ */
|
||||
};
|
||||
|
||||
enum sqtt_file_chunk_type
|
||||
@@ -144,6 +145,8 @@ enum sqtt_gfxip_level
|
||||
SQTT_GFXIP_LEVEL_GFXIP_10_1 = 0x7,
|
||||
SQTT_GFXIP_LEVEL_GFXIP_10_3 = 0x9,
|
||||
SQTT_GFXIP_LEVEL_GFXIP_11_0 = 0xc,
|
||||
SQTT_GFXIP_LEVEL_GFXIP_11_5 = 0xd,
|
||||
SQTT_GFXIP_LEVEL_GFXIP_12 = 0x10,
|
||||
};
|
||||
|
||||
enum sqtt_memory_type
|
||||
@@ -427,6 +430,8 @@ enum elf_gfxip_level
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1010 = 0x033,
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1030 = 0x036,
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1100 = 0x041,
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1150 = 0x043,
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1200 = 0x04e,
|
||||
};
|
||||
|
||||
struct sqtt_file_chunk_spm_db {
|
||||
|
||||
@@ -50,4 +50,7 @@ exclude = [
|
||||
"E303", "E304", "E501", "E702", "E703", "E731", "W191",
|
||||
"W291", "W293", "UP039", "C416", "RET506", "RET507", "A",
|
||||
"FURB110", "RUF018", "F541", "F841"
|
||||
]
|
||||
]
|
||||
|
||||
[format]
|
||||
exclude = ["*"]
|
||||
|
||||
Vendored
+2
@@ -2,6 +2,7 @@ import gc
|
||||
from tinygrad import Tensor, UOp, Device, nn
|
||||
from tinygrad.shape.shapetracker import views_to_valid_uop
|
||||
from tinygrad.engine.realize import method_cache, get_program
|
||||
from tinygrad.schedule.indexing import apply_movement_op
|
||||
from test.test_tiny import TestTiny
|
||||
|
||||
def uops_allocated(): return sum([isinstance(x, UOp) for x in gc.get_objects()])
|
||||
@@ -69,6 +70,7 @@ if __name__ == "__main__":
|
||||
# these caches will keep uops alive
|
||||
method_cache.clear()
|
||||
views_to_valid_uop.cache_clear()
|
||||
apply_movement_op.cache_clear()
|
||||
Tensor._device_seeds.clear()
|
||||
Tensor._device_rng_counters.clear()
|
||||
|
||||
|
||||
+1
-4
@@ -2,7 +2,7 @@ import time, math, unittest, functools, platform, warnings
|
||||
import numpy as np
|
||||
from typing import List, Callable
|
||||
import torch
|
||||
from tinygrad.helpers import getenv, IMAGE, DEBUG, CI, Context, TRANSCENDENTAL, CPU_LLVM, AMD_LLVM
|
||||
from tinygrad.helpers import getenv, IMAGE, DEBUG, CI, Context, CPU_LLVM, AMD_LLVM
|
||||
from tinygrad import Tensor, Device, dtypes
|
||||
from tinygrad.tensor import _to_np_dtype
|
||||
from tinygrad.device import is_dtype_supported
|
||||
@@ -901,7 +901,6 @@ class TestOps(unittest.TestCase):
|
||||
def test_abs_exact(self):
|
||||
helper_test_op(None, torch.abs, Tensor.abs, vals=[[-1.,0,1]])
|
||||
|
||||
@unittest.skipIf(TRANSCENDENTAL and Device.DEFAULT=="AMD", "TODO: remu crashes")
|
||||
def test_log(self):
|
||||
helper_test_op([(45,65)], torch.log, Tensor.log)
|
||||
helper_test_op(None, torch.log, Tensor.log, vals=[[math.inf, -math.inf, math.nan]])
|
||||
@@ -911,7 +910,6 @@ class TestOps(unittest.TestCase):
|
||||
helper_test_op(None, torch.log2, Tensor.log2, vals=[[math.inf, -math.inf, math.nan]])
|
||||
helper_test_op([()], torch.log2, Tensor.log2)
|
||||
|
||||
@unittest.skipIf(TRANSCENDENTAL and Device.DEFAULT=="AMD", "TODO: remu crashes")
|
||||
def test_exp(self):
|
||||
helper_test_op([(45,65)], torch.exp, Tensor.exp)
|
||||
helper_test_op(None, torch.exp, Tensor.exp, vals=[[math.inf, -math.inf, math.nan]])
|
||||
@@ -1549,7 +1547,6 @@ class TestOps(unittest.TestCase):
|
||||
helper_test_op([(3,4,5,6)], lambda x: torch.stack(torch.std_mean(x, axis=(1,2))),
|
||||
lambda x: Tensor.stack(*x.std_mean(axis=(1,2))))
|
||||
|
||||
@unittest.skip("TODO: this fails because of loaded nan in mul folding")
|
||||
def test_std_mean_loaded_nan(self):
|
||||
helper_test_op([(1,0,3,0,5)], lambda x: torch.stack(torch.std_mean(x, axis=(1,3))),
|
||||
lambda x: Tensor.stack(*x.std_mean(axis=(1,3))))
|
||||
|
||||
+30
-31
@@ -1,5 +1,15 @@
|
||||
import unittest
|
||||
from tinygrad import Tensor, UOp, GlobalCounters, Context
|
||||
from tinygrad import Tensor, UOp
|
||||
from tinygrad.uop.ops import AxisType, Ops
|
||||
|
||||
class TestOuterworldReduce(unittest.TestCase):
|
||||
def test_reduce(self):
|
||||
x = Tensor.ones(5, 5).contiguous()
|
||||
a = UOp.range(5, -1, AxisType.REDUCE)
|
||||
out = x[a]
|
||||
# TODO: syntax for this
|
||||
t = Tensor(UOp(Ops.REDUCE, dtype=out.uop.dtype, src=(out.uop, a), arg=Ops.ADD))
|
||||
self.assertListEqual(t.tolist(), [5.,5.,5.,5.,5.])
|
||||
|
||||
class TestOuterworld(unittest.TestCase):
|
||||
def test_range_plus_1(self):
|
||||
@@ -13,6 +23,17 @@ class TestOuterworld(unittest.TestCase):
|
||||
|
||||
self.assertTrue((t+1==cpy).all().item())
|
||||
|
||||
def test_range_plus_1_transpose(self):
|
||||
t = Tensor.arange(100).reshape(10,10).realize()
|
||||
|
||||
# passthrough ranges
|
||||
a = UOp.range(10, -1)
|
||||
sel = t[a] + 1
|
||||
assert sel.shape == (10,)
|
||||
cpy = sel.reshape(10, 1).expand(10, a).contiguous().realize()
|
||||
|
||||
self.assertTrue(((t+1).T==cpy).all().item())
|
||||
|
||||
def test_flip_range(self):
|
||||
t = Tensor.rand(10, 10).realize()
|
||||
|
||||
@@ -37,39 +58,17 @@ class TestOuterworld(unittest.TestCase):
|
||||
out.realize()
|
||||
self.assertTrue((out==20).all().item())
|
||||
|
||||
@unittest.skip("opts don't work")
|
||||
def test_triple_gemm(self):
|
||||
x = Tensor.rand(1, 16).realize()
|
||||
W = Tensor.rand(3, 16, 16).realize()
|
||||
def test_fancy_vmap(self):
|
||||
def f(x,y): return x+y
|
||||
|
||||
manual = (x @ W[0] @ W[1] @ W[2]).contiguous().realize()
|
||||
x = Tensor.arange(9).reshape(3,3).contiguous()
|
||||
y = Tensor.arange(9).reshape(3,3).contiguous()
|
||||
|
||||
a = UOp.range(3, -1)
|
||||
x = x.assign(x @ W[a])
|
||||
out = x.contiguous(a)[-1].contiguous().realize()
|
||||
|
||||
self.assertTrue((manual==out).all().item())
|
||||
|
||||
def test_setitem_pyrange(self):
|
||||
with Context(DEBUG=0):
|
||||
t = Tensor.rand(10).realize()
|
||||
o = Tensor.empty(10)
|
||||
GlobalCounters.reset()
|
||||
for i in range(10):
|
||||
o[i] = t[i]
|
||||
o.realize()
|
||||
self.assertTrue((t==o).all().item())
|
||||
|
||||
@unittest.skip("TODO: fix this")
|
||||
def test_setitem(self):
|
||||
with Context(DEBUG=0):
|
||||
t = Tensor.rand(10).realize()
|
||||
o = Tensor.empty(10)
|
||||
GlobalCounters.reset()
|
||||
i = UOp.range(10, -1)
|
||||
o[i] = t[i]
|
||||
o.contiguous(i).realize()
|
||||
self.assertTrue((t==o).all().item())
|
||||
out = f(x[:,a], y[a,:])
|
||||
# TODO: this should support flatten
|
||||
out = out.reshape(1, 3).expand(a, 3).contiguous().realize()
|
||||
self.assertListEqual([[0,4,8],[4,8,12],[8,12,16]], out.tolist())
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1526,7 +1526,7 @@ class TestSchedule(unittest.TestCase):
|
||||
# run_schedule(check_schedule(out, 1))
|
||||
run_schedule(check_schedule(out, 4))
|
||||
np.testing.assert_allclose(out.numpy(), np.pad(np.log2(np.abs(np.pad(np.log2(a.numpy()), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum() + \
|
||||
b.numpy())), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(), atol=3e-4, rtol=1e-6)
|
||||
b.numpy())), ((0, 1), (0, 1), (0, 1)), constant_values=1.0).sum(), atol=3e-4, rtol=1e-5)
|
||||
|
||||
def test_shrink_pad_safe(self):
|
||||
a = Tensor.ones((3, )).contiguous().realize()
|
||||
|
||||
@@ -15,7 +15,6 @@ from tinygrad.codegen.late.expander import migrate_indexing, expander, pm_pre_ex
|
||||
from tinygrad.codegen.late.devectorizer import load_store_folding, load_store_indexing, devectorize, pm_reduce, \
|
||||
ReduceContext, correct_load_store, pm_render
|
||||
from tinygrad.codegen.late.linearize import block_create, pm_blockend_merge, block_merge, pm_finalize, BlockContext
|
||||
from tinygrad.codegen.late.control_flow import pm_add_endrange_endif, pm_control_flow, CFGContext, linearize
|
||||
from tinygrad.codegen.opt.postrange import pm_postrange_opt
|
||||
from tinygrad.codegen.simplify import pm_simplify_ranges, pm_reduce_simplify, pm_flatten_range, pm_split_ranges
|
||||
from tinygrad.schedule.rangeify import pm_add_buffers, rangeify_codegen
|
||||
@@ -102,15 +101,11 @@ def _get_rewrites_for_renderer(opts:Renderer, optimize:bool, linearizer:bool, _Q
|
||||
pm_final_rewrite = pm_decomp+pm_render+extra_matcher
|
||||
ret.append(RewriteStep(pm_final_rewrite, lambda _: opts.device, name="final rewrite"))
|
||||
|
||||
# add control flow to the graph
|
||||
ret.append(RewriteStep(pm_add_endrange_endif, name="add endrange/endif"))
|
||||
ret.append(RewriteStep(pm_control_flow, CFGContext, name="add control flow starts", bottom_up=True))
|
||||
# return the list (with optional linearizer)
|
||||
return ret + (rewrites_for_linearizer if linearizer else [])
|
||||
|
||||
# return the list
|
||||
return ret
|
||||
|
||||
def full_rewrite_to_sink(sink:UOp, opts:Renderer|None=None, optimize:bool=True) -> UOp:
|
||||
return apply_rewrites(sink, get_rewrites_for_renderer(opts if opts is not None else Renderer(), optimize))
|
||||
def full_rewrite_to_sink(sink:UOp, opts:Renderer|None=None, optimize:bool=True, linearizer:bool=False) -> UOp:
|
||||
return apply_rewrites(sink, get_rewrites_for_renderer(opts if opts is not None else Renderer(), optimize, linearizer))
|
||||
|
||||
def full_rewrite(sink:UOp, opts:Renderer|None=None) -> list[UOp]:
|
||||
"""
|
||||
@@ -124,6 +119,6 @@ def full_rewrite(sink:UOp, opts:Renderer|None=None) -> list[UOp]:
|
||||
Linear program in UOps.
|
||||
"""
|
||||
|
||||
lst = linearize(full_rewrite_to_sink(sink, opts, optimize=sink.tag is None))
|
||||
lst = list(full_rewrite_to_sink(sink, opts, optimize=sink.tag is None, linearizer=True).arg.lst)
|
||||
if __debug__: type_verify(lst)
|
||||
return lst
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
from tinygrad.uop.ops import UOp, Ops, PatternMatcher, UPat
|
||||
from tinygrad.helpers import dedup
|
||||
from collections import defaultdict
|
||||
from itertools import groupby
|
||||
from functools import reduce
|
||||
import heapq
|
||||
|
||||
def linearize(u:UOp) -> list[UOp]:
|
||||
lst = list(u.toposort())
|
||||
in_this_block = set(lst)
|
||||
local_children: defaultdict[UOp, list[UOp]] = defaultdict(list)
|
||||
in_degree:dict[UOp, int] = {}
|
||||
priorities:dict[UOp, int] = {}
|
||||
|
||||
# get local children and assign priorities
|
||||
# NOTE: this requires the lst be locally toposorted
|
||||
for u in reversed(lst):
|
||||
in_degree[u] = 0
|
||||
for s in u.src:
|
||||
if s in in_this_block:
|
||||
local_children[s].append(u)
|
||||
in_degree[u] += 1
|
||||
# put loads in the beginning of the block and prevent priority inversion. hack for BARRIER grouping too
|
||||
priority = [0] + [priorities[x] for x in local_children[u]]
|
||||
if u.op is Ops.LOAD: priority.append(-1000)
|
||||
if u.op is Ops.BARRIER: priority.append(-1500)
|
||||
# ranges are scheduled as late as possible so anything that can be outside is
|
||||
#if u.op is Ops.RANGE: priority = [2000]
|
||||
# move defines and consts to the top
|
||||
if u.op in {Ops.DEFINE_GLOBAL, Ops.DEFINE_LOCAL, Ops.DEFINE_REG, Ops.DEFINE_VAR, Ops.SPECIAL, Ops.CONST}: priority.append(-2000)
|
||||
priorities[u] = min(priority)
|
||||
|
||||
# number the uops in "ideal" order
|
||||
nkey = {u:i for i,u in enumerate(sorted(lst, key=lambda x: (priorities[x],)+x.tuplize))}
|
||||
|
||||
# then force then to be toposorted in as close to the ideal order as possible
|
||||
heapq.heapify(heap:=[(nkey[u],u) for u in lst if in_degree[u] == 0])
|
||||
newlst = []
|
||||
while heap:
|
||||
newlst.append(u:=heapq.heappop(heap)[1])
|
||||
for v in local_children[u]:
|
||||
in_degree[v] -= 1
|
||||
if in_degree[v] == 0: heapq.heappush(heap, (nkey[v],v))
|
||||
|
||||
assert len(newlst) == len(lst), f"len mismatch {len(newlst)} != {len(lst)}"
|
||||
return newlst
|
||||
|
||||
def add_endrange(x:UOp):
|
||||
if not ((x.op is Ops.LOAD and x.src[-1].op is Ops.STORE) or all(s.op is Ops.STORE and any(n.op is Ops.RANGE for n in s.src) for s in x.src)):
|
||||
return None
|
||||
src: list[UOp] = []
|
||||
for k,g in groupby(x.src, key=lambda k: tuple(dedup(s for s in k.src if s.op is Ops.RANGE))):
|
||||
if not k: src.extend(g)
|
||||
else: src.extend(reduce(lambda acc,rng: (UOp(Ops.ENDRANGE, src=(rng,) + acc),), reversed(k), tuple(g))) # type: ignore
|
||||
return x.replace(src=tuple(src))
|
||||
|
||||
def add_endif(x:UOp):
|
||||
groups = {k: tuple(g) for k,g in groupby(x.src, key=lambda k: k.src[2] if len(k.src) >= 3 and k.src[2].op is Ops.IF else k)}
|
||||
if not any(k.op is Ops.IF for k in groups): return None
|
||||
return x.replace(src=tuple(UOp(Ops.ENDIF, src=(k,) + g) if k.op is Ops.IF else k for k,g in groups.items()))
|
||||
|
||||
# some Ops.IF aren't closed by an Ops.STORE, in that case the Ops.SINK closes it
|
||||
def close_ifs(x:UOp):
|
||||
consumers = x.get_consumer_map()
|
||||
if (y:=next((s for s in consumers if s.op is Ops.IF and all(n.op is not Ops.ENDIF for n in consumers[s])), None)) is not None:
|
||||
return x.replace(src=(UOp(Ops.ENDIF, src=(y,) + x.src),))
|
||||
return None
|
||||
|
||||
pm_add_endrange_endif = PatternMatcher([
|
||||
(UPat((Ops.SINK, Ops.NOOP, Ops.LOAD), name="x"), add_endrange),
|
||||
(UPat((Ops.SINK, Ops.ENDRANGE, Ops.BARRIER), name="x"), add_endif),
|
||||
(UPat(Ops.SINK, name="x"), close_ifs),
|
||||
])
|
||||
|
||||
class CFGContext:
|
||||
def __init__(self, sink:UOp):
|
||||
# there are 3 relationships between ranges:
|
||||
# nested, meaning endrange y is a dependency of endrange x and range x is a dependency of endrange y
|
||||
# dependent, meaning endrange y is a dependency of endrange x and range x is not a dependency of endrange y
|
||||
# independent, endrange y is not a dependency of endrange x
|
||||
deps: dict[UOp, set[UOp]] = {}
|
||||
nesting: dict[UOp, UOp] = {}
|
||||
for u in sink.toposort():
|
||||
deps[u] = set().union(*(deps[s] for s in u.src))
|
||||
if u.op in (Ops.ENDRANGE, Ops.ENDIF):
|
||||
for n in [x for x in deps[u] if x.op in (Ops.ENDRANGE, Ops.ENDIF) and u.src[0] in deps[x] and x not in nesting]: nesting[n] = u
|
||||
if u.op is Ops.SINK:
|
||||
for n in [x for x in deps[u] if x.op in (Ops.ENDRANGE, Ops.ENDIF) and x not in nesting]: nesting[n] = u
|
||||
if u.op in (Ops.RANGE, Ops.ENDRANGE, Ops.IF, Ops.ENDIF): deps[u] |= {u}
|
||||
|
||||
self.edges: dict[UOp, UOp] = {}
|
||||
siblings: dict[UOp, list[UOp]] = {}
|
||||
for k,vv in nesting.items(): siblings.setdefault(vv, []).append(k)
|
||||
for k,v in siblings.items():
|
||||
# range/if that have dependencies on other siblings need to run after them
|
||||
order = sorted(v, key=lambda x: len([y for y in v if y in deps[x]]))
|
||||
zipped = zip(order, order[1:]) if k.op is Ops.SINK else zip([k.src[0]] + order, order)
|
||||
for x,y in zipped: self.edges[y.src[0]] = x
|
||||
|
||||
pm_control_flow = PatternMatcher([
|
||||
(UPat((Ops.RANGE, Ops.IF), src=(UPat(),), name="x"), lambda ctx,x: x.replace(src=x.src+(y,)) if (y:=ctx.edges.get(x)) is not None else None),
|
||||
(UPat(Ops.IF, src=(UPat(), UPat(Ops.BARRIER)), name="x"), lambda ctx,x: x.replace(src=x.src+(y,)) if (y:=ctx.edges.get(x)) is not None else None),
|
||||
])
|
||||
@@ -19,7 +19,7 @@ class Opt:
|
||||
axis_letters = {AxisType.GLOBAL: "g", AxisType.THREAD: "t", AxisType.LOCAL: "l", AxisType.WARP: "w", AxisType.LOOP: "L", AxisType.UPCAST: "u",
|
||||
AxisType.GROUP_REDUCE: "G", AxisType.REDUCE: "R", AxisType.UNROLL: "r"}
|
||||
axis_colors = {AxisType.GLOBAL: "blue", AxisType.THREAD: "BLUE", AxisType.LOCAL: "cyan", AxisType.WARP: "CYAN", AxisType.LOOP: "WHITE",
|
||||
AxisType.UPCAST: "yellow", AxisType.GROUP_REDUCE: "green", AxisType.REDUCE: "red", AxisType.UNROLL: "magenta"}
|
||||
AxisType.UPCAST: "yellow", AxisType.GROUP_REDUCE: "RED", AxisType.REDUCE: "red", AxisType.UNROLL: "magenta"}
|
||||
|
||||
class KernelOptError(Exception): pass
|
||||
def check(cond:bool, msg:str=""):
|
||||
|
||||
@@ -39,7 +39,7 @@ pm_gradient = PatternMatcher([
|
||||
(UPat(Ops.EXPAND, name="ret"), lambda ctx, ret: (ctx.r(Ops.ADD, tuple(i for i,(si,so) in enumerate(zip(ret.src[0].shape, ret.arg)) if si!=so)),)),
|
||||
(UPat(Ops.MULTI, name="ret"), lambda ctx, ret: ctx.shard(ret.device, ret.axis).src),
|
||||
# there's no gradient for bitcast
|
||||
(UPat(Ops.BITCAST), lambda ctx: (None,)),
|
||||
(UPat(Ops.BITCAST), lambda: (None,)),
|
||||
])
|
||||
|
||||
def _deepwalk(root:UOp, targets:set[UOp]) -> list[UOp]:
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ def suppress_finalizing(func):
|
||||
if not getattr(sys, 'is_finalizing', lambda: True)(): raise # re-raise if not finalizing
|
||||
return wrapper
|
||||
|
||||
def unwrap_class_type(cls_t:T): return cls_t.func if isinstance(cls_t, functools.partial) else cls_t
|
||||
def unwrap_class_type(cls_t): return cls_t.func if isinstance(cls_t, functools.partial) else cls_t
|
||||
|
||||
def pluralize(st:str, cnt:int): return f"{cnt} {st}"+('' if cnt == 1 else 's')
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from typing import Literal, Callable, cast
|
||||
import os, math, sys
|
||||
from collections import defaultdict, Counter
|
||||
from tinygrad.codegen.opt import tc
|
||||
from tinygrad.uop.ops import GroupOp, Ops, UOp, PatternMatcher, UPat, sint_to_uop, range_str
|
||||
from tinygrad.uop.ops import GroupOp, Ops, UOp, PatternMatcher, UPat, range_str
|
||||
from tinygrad.helpers import strip_parens, getenv, prod, dedup, AMX, CPU_COUNT
|
||||
from tinygrad.dtype import ImageDType, dtypes, DType, PtrDType, AddrSpace, truncate
|
||||
from tinygrad.renderer import Renderer
|
||||
@@ -112,7 +112,7 @@ class CStyleLanguage(Renderer):
|
||||
buftypes = [(name, self.render_dtype(dtype, mutable)+self.buffer_suffix if isinstance(dtype, (ImageDType, PtrDType)) else
|
||||
self.arg_int_prefix if dtype == dtypes.int else None) for name,(dtype,mutable) in bufs]
|
||||
local_dims = [u.src[0] for u in uops if u.op is Ops.SPECIAL and u.arg[0] == "l"]
|
||||
launch_bounds = sint_to_uop(prod(local_dims)).vmax
|
||||
launch_bounds = prod([d.vmax for d in local_dims])
|
||||
prg = ''.join([f"{self.kernel_typedef.format(launch_bounds=launch_bounds)} {function_name}(",] +
|
||||
[', '.join([f'{t} {name}' for name,t in buftypes] + self.extra_args)] +
|
||||
[") {\n" + tmp] + ['\n'.join(kernel), "\n}"])
|
||||
|
||||
@@ -4,7 +4,7 @@ from tinygrad.codegen.opt import tc
|
||||
from tinygrad.renderer import Renderer
|
||||
from tinygrad.renderer.cstyle import AMDRenderer
|
||||
from tinygrad.uop.decompositions import xexp2, xlog2
|
||||
from tinygrad.uop.ops import UOp, PatternMatcher, UPat, Ops, GroupOp, sint_to_uop, range_str
|
||||
from tinygrad.uop.ops import UOp, PatternMatcher, UPat, Ops, GroupOp, range_str
|
||||
from tinygrad.dtype import dtypes, DType, PtrDType, truncate
|
||||
from tinygrad.helpers import prod, AMX
|
||||
|
||||
@@ -226,7 +226,7 @@ class AMDLLVMRenderer(LLVMRenderer):
|
||||
def _render_footer(self, uops: list[UOp]) -> str:
|
||||
# TODO: this is copied from cstyle
|
||||
local_dims = [u.src[0] for u in uops if u.op is Ops.SPECIAL and u.arg[0] == "l"]
|
||||
requiredMaxThreadsPerBlock = sint_to_uop(prod(local_dims)).vmax
|
||||
requiredMaxThreadsPerBlock = prod([d.vmax for d in local_dims])
|
||||
attributes = ["alwaysinline", "nounwind", '"no-builtins"',
|
||||
f'"amdgpu-flat-work-group-size"="1,{requiredMaxThreadsPerBlock}"', '"no-trapping-math"="true"']
|
||||
return 'attributes #0 = { ' + ' '.join(attributes) + ' }'
|
||||
|
||||
@@ -2,7 +2,7 @@ from typing import cast, Callable
|
||||
import struct
|
||||
from collections import defaultdict
|
||||
from tinygrad.codegen.opt import tc
|
||||
from tinygrad.uop.ops import Ops, UOp, PatternMatcher, UPat, GroupOp, sint_to_uop
|
||||
from tinygrad.uop.ops import Ops, UOp, PatternMatcher, UPat, GroupOp
|
||||
from tinygrad.dtype import dtypes, DType, PtrDType, AddrSpace
|
||||
from tinygrad.renderer import Renderer
|
||||
from tinygrad.renderer.cstyle import CUDARenderer
|
||||
@@ -157,7 +157,7 @@ class PTXRenderer(Renderer):
|
||||
def fmt(line): return line if line[0]=="$" else "\t" + line.replace(" ", "\t" if len(line.split(" ")[0]) > 7 else "\t\t", 1)
|
||||
kernel = '\n'.join(map(fmt, [f".reg .{reg.split('_')[-2]} %{reg}<{cnt}>;" for reg,cnt in regs] + kernel + ["ret;"]))
|
||||
local_dims = [u.src[0] for u in uops if u.op is Ops.SPECIAL and u.arg[0] == "l"]
|
||||
launch_bounds = sint_to_uop(prod(local_dims)).vmax
|
||||
launch_bounds = prod([d.vmax for d in local_dims])
|
||||
params = ',\n\t'.join([f".param .{'u64' if dtype.__class__ == PtrDType else self.types[dtype]} {name}" for name,dtype in bufs])
|
||||
return f"{self.kernel_prefix.format(launch_bounds=launch_bounds)} {function_name} (\n\t{params}\n)\n.maxntid {launch_bounds}\n{{\n{kernel}\n}}"
|
||||
|
||||
|
||||
@@ -174,12 +174,14 @@ sqtt_version__enumvalues = {
|
||||
6: 'SQTT_VERSION_2_3',
|
||||
7: 'SQTT_VERSION_2_4',
|
||||
11: 'SQTT_VERSION_3_2',
|
||||
12: 'SQTT_VERSION_3_3',
|
||||
}
|
||||
SQTT_VERSION_NONE = 0
|
||||
SQTT_VERSION_2_2 = 5
|
||||
SQTT_VERSION_2_3 = 6
|
||||
SQTT_VERSION_2_4 = 7
|
||||
SQTT_VERSION_3_2 = 11
|
||||
SQTT_VERSION_3_3 = 12
|
||||
sqtt_version = ctypes.c_uint32 # enum
|
||||
|
||||
# values for enumeration 'sqtt_file_chunk_type'
|
||||
@@ -336,6 +338,8 @@ sqtt_gfxip_level__enumvalues = {
|
||||
7: 'SQTT_GFXIP_LEVEL_GFXIP_10_1',
|
||||
9: 'SQTT_GFXIP_LEVEL_GFXIP_10_3',
|
||||
12: 'SQTT_GFXIP_LEVEL_GFXIP_11_0',
|
||||
13: 'SQTT_GFXIP_LEVEL_GFXIP_11_5',
|
||||
16: 'SQTT_GFXIP_LEVEL_GFXIP_12',
|
||||
}
|
||||
SQTT_GFXIP_LEVEL_NONE = 0
|
||||
SQTT_GFXIP_LEVEL_GFXIP_6 = 1
|
||||
@@ -346,6 +350,8 @@ SQTT_GFXIP_LEVEL_GFXIP_9 = 5
|
||||
SQTT_GFXIP_LEVEL_GFXIP_10_1 = 7
|
||||
SQTT_GFXIP_LEVEL_GFXIP_10_3 = 9
|
||||
SQTT_GFXIP_LEVEL_GFXIP_11_0 = 12
|
||||
SQTT_GFXIP_LEVEL_GFXIP_11_5 = 13
|
||||
SQTT_GFXIP_LEVEL_GFXIP_12 = 16
|
||||
sqtt_gfxip_level = ctypes.c_uint32 # enum
|
||||
|
||||
# values for enumeration 'sqtt_memory_type'
|
||||
@@ -806,12 +812,16 @@ elf_gfxip_level__enumvalues = {
|
||||
51: 'EF_AMDGPU_MACH_AMDGCN_GFX1010',
|
||||
54: 'EF_AMDGPU_MACH_AMDGCN_GFX1030',
|
||||
65: 'EF_AMDGPU_MACH_AMDGCN_GFX1100',
|
||||
67: 'EF_AMDGPU_MACH_AMDGCN_GFX1150',
|
||||
78: 'EF_AMDGPU_MACH_AMDGCN_GFX1200',
|
||||
}
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX801 = 40
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX900 = 44
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1010 = 51
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1030 = 54
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1100 = 65
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1150 = 67
|
||||
EF_AMDGPU_MACH_AMDGCN_GFX1200 = 78
|
||||
elf_gfxip_level = ctypes.c_uint32 # enum
|
||||
class struct_sqtt_file_chunk_spm_db(Structure):
|
||||
pass
|
||||
@@ -1607,7 +1617,8 @@ __all__ = \
|
||||
'ApiCmdUpdateBuffer', 'ApiCmdWaitEvents', 'ApiCmdWriteTimestamp',
|
||||
'ApiInvalid', 'ApiRayTracingSeparateCompiled',
|
||||
'EF_AMDGPU_MACH_AMDGCN_GFX1010', 'EF_AMDGPU_MACH_AMDGCN_GFX1030',
|
||||
'EF_AMDGPU_MACH_AMDGCN_GFX1100', 'EF_AMDGPU_MACH_AMDGCN_GFX801',
|
||||
'EF_AMDGPU_MACH_AMDGCN_GFX1100', 'EF_AMDGPU_MACH_AMDGCN_GFX1150',
|
||||
'EF_AMDGPU_MACH_AMDGCN_GFX1200', 'EF_AMDGPU_MACH_AMDGCN_GFX801',
|
||||
'EF_AMDGPU_MACH_AMDGCN_GFX900', 'EventCmdBlitImage',
|
||||
'EventCmdBuildAccelerationStructuresIndirectKHR',
|
||||
'EventCmdBuildAccelerationStructuresKHR',
|
||||
@@ -1671,7 +1682,8 @@ __all__ = \
|
||||
'SQTT_FILE_CHUNK_TYPE_SQTT_DESC', 'SQTT_FILE_MAGIC_NUMBER',
|
||||
'SQTT_FILE_VERSION_MAJOR', 'SQTT_FILE_VERSION_MINOR',
|
||||
'SQTT_GFXIP_LEVEL_GFXIP_10_1', 'SQTT_GFXIP_LEVEL_GFXIP_10_3',
|
||||
'SQTT_GFXIP_LEVEL_GFXIP_11_0', 'SQTT_GFXIP_LEVEL_GFXIP_6',
|
||||
'SQTT_GFXIP_LEVEL_GFXIP_11_0', 'SQTT_GFXIP_LEVEL_GFXIP_11_5',
|
||||
'SQTT_GFXIP_LEVEL_GFXIP_12', 'SQTT_GFXIP_LEVEL_GFXIP_6',
|
||||
'SQTT_GFXIP_LEVEL_GFXIP_7', 'SQTT_GFXIP_LEVEL_GFXIP_8',
|
||||
'SQTT_GFXIP_LEVEL_GFXIP_8_1', 'SQTT_GFXIP_LEVEL_GFXIP_9',
|
||||
'SQTT_GFXIP_LEVEL_NONE', 'SQTT_GPU_NAME_MAX_SIZE',
|
||||
@@ -1697,9 +1709,9 @@ __all__ = \
|
||||
'SQTT_QUEUE_TYPE_COMPUTE', 'SQTT_QUEUE_TYPE_DMA',
|
||||
'SQTT_QUEUE_TYPE_UNIVERSAL', 'SQTT_QUEUE_TYPE_UNKNOWN',
|
||||
'SQTT_SA_PER_SE', 'SQTT_VERSION_2_2', 'SQTT_VERSION_2_3',
|
||||
'SQTT_VERSION_2_4', 'SQTT_VERSION_3_2', 'SQTT_VERSION_NONE',
|
||||
'UserEventObjectName', 'UserEventPop', 'UserEventPush',
|
||||
'UserEventTrigger', 'elf_gfxip_level',
|
||||
'SQTT_VERSION_2_4', 'SQTT_VERSION_3_2', 'SQTT_VERSION_3_3',
|
||||
'SQTT_VERSION_NONE', 'UserEventObjectName', 'UserEventPop',
|
||||
'UserEventPush', 'UserEventTrigger', 'elf_gfxip_level',
|
||||
'rgp_sqtt_marker_event_type', 'rgp_sqtt_marker_general_api_type',
|
||||
'rgp_sqtt_marker_identifier', 'rgp_sqtt_marker_user_event_type',
|
||||
'sqtt_api_type', 'sqtt_engine_type',
|
||||
|
||||
+25
-16
@@ -130,8 +130,9 @@ class AMDComputeQueue(HWQueue):
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_USERDATA_2, *data_ints[i:i+2])
|
||||
|
||||
def sqtt_config(self, tracing:bool):
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_CTRL, draw_event_en=1, spi_stall_en=1, sq_stall_en=1, reg_at_hwm=2, hiwater=1,
|
||||
rt_freq=self.soc.SQ_TT_RT_FREQ_4096_CLK, util_timer=self.soc.SQ_TT_UTIL_TIMER_250_CLK, mode=int(tracing))
|
||||
trace_ctrl = {'rt_freq': self.soc.SQ_TT_RT_FREQ_4096_CLK} if self.dev.target < (12,0,0) else {}
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_CTRL, draw_event_en=1, spi_stall_en=1, sq_stall_en=1, reg_at_hwm=2, hiwater=1, util_timer=1,
|
||||
mode=int(tracing), **trace_ctrl)
|
||||
|
||||
# Magic values from mesa/src/amd/vulkan/radv_sqtt.c:radv_emit_spi_config_cntl and src/amd/common/ac_sqtt.c:ac_sqtt_emit_start
|
||||
def sqtt_start(self, buf0s:list[HCQBuffer], se_mask:int):
|
||||
@@ -140,24 +141,35 @@ class AMDComputeQueue(HWQueue):
|
||||
# One buffer for one SE, mesa does it with a single buffer and ac_sqtt_get_data_offset, but this is simpler and should work just as well
|
||||
for se in range(len(buf0s)):
|
||||
self.wreg(self.gc.regGRBM_GFX_INDEX, se_index=se, instance_broadcast_writes=1)
|
||||
buf0_lo, buf0_hi = data64_le(buf0s[se].va_addr>>12)
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_BUF0_SIZE, base_hi=buf0_hi, size=buf0s[se].size>>12)
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_BUF0_BASE, base_lo=buf0_lo)
|
||||
buf0_lo, buf0_hi = data64_le(buf0s[se].va_addr >> 12)
|
||||
if self.dev.target >= (12,0,0):
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_BUF0_SIZE, size=buf0s[se].size >> 12)
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_BUF0_BASE_LO, base_lo=buf0_lo)
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_BUF0_BASE_HI, base_hi=buf0_hi)
|
||||
else:
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_BUF0_SIZE, base_hi=buf0_hi, size=buf0s[se].size >> 12)
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_BUF0_BASE, base_lo=buf0_lo)
|
||||
# NOTE: SQTT can only trace instructions on one simd per se, this selects first simd in first wgp in first sa.
|
||||
# For RGP to display instruction trace it has to see it on first SE. Howerver ACE/MEC/whatever does the dispatching starting with second se,
|
||||
# and on amdgpu/non-AM it also does weird things with dispatch order inside se: around 7 times out of 10 it starts from the last cu, but
|
||||
# sometimes not, especially if the kernel has more than one wavefront which means that kernels with small global size might get unlucky and
|
||||
# be dispatched on something else and not be seen in instruction tracing tab. You can force the wavefronts of a kernel to be dispatched on the
|
||||
# CUs you want to by disabling other CUs via bits in regCOMPUTE_STATIC_THREAD_MGMT_SE<x> and trace even kernels that only have one wavefront.
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_MASK, wtype_include=self.soc.SQ_TT_WTYPE_INCLUDE_CS_BIT, simd_sel=0, wgp_sel=0, sa_sel=0)
|
||||
cs_wtype = (1 << 6) if self.dev.target >= (12,0,0) else self.soc.SQ_TT_WTYPE_INCLUDE_CS_BIT
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_MASK, wtype_include=cs_wtype, simd_sel=0, wgp_sel=0, sa_sel=0)
|
||||
reg_include = self.soc.SQ_TT_TOKEN_MASK_SQDEC_BIT | self.soc.SQ_TT_TOKEN_MASK_SHDEC_BIT | self.soc.SQ_TT_TOKEN_MASK_GFXUDEC_BIT | \
|
||||
self.soc.SQ_TT_TOKEN_MASK_COMP_BIT | self.soc.SQ_TT_TOKEN_MASK_CONTEXT_BIT | self.soc.SQ_TT_TOKEN_MASK_CONTEXT_BIT
|
||||
token_exclude = 1 << self.soc.SQ_TT_TOKEN_EXCLUDE_PERF_SHIFT
|
||||
self.soc.SQ_TT_TOKEN_MASK_COMP_BIT | self.soc.SQ_TT_TOKEN_MASK_CONTEXT_BIT
|
||||
token_exclude = (1 << self.soc.SQ_TT_TOKEN_EXCLUDE_PERF_SHIFT) if self.dev.target < (12,0,0) else 0
|
||||
|
||||
# disable tracing
|
||||
if not (se_mask >> se) & 0b1:
|
||||
token_exclude |= 1 << self.soc.SQ_TT_TOKEN_EXCLUDE_VMEMEXEC_SHIFT | 1 << self.soc.SQ_TT_TOKEN_EXCLUDE_ALUEXEC_SHIFT | \
|
||||
# gfx12 doesn't have enums with all fields, so it's hardcoded, but it's the same as gfx11.
|
||||
token_exclude |= (1 << self.soc.SQ_TT_TOKEN_EXCLUDE_VMEMEXEC_SHIFT | 1 << self.soc.SQ_TT_TOKEN_EXCLUDE_ALUEXEC_SHIFT | \
|
||||
1 << self.soc.SQ_TT_TOKEN_EXCLUDE_VALUINST_SHIFT | 1 << self.soc.SQ_TT_TOKEN_EXCLUDE_IMMEDIATE_SHIFT | \
|
||||
1 << self.soc.SQ_TT_TOKEN_EXCLUDE_INST_SHIFT
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_TOKEN_MASK, reg_include=reg_include, token_exclude=token_exclude, bop_events_token_include=1)
|
||||
1 << self.soc.SQ_TT_TOKEN_EXCLUDE_INST_SHIFT) if self.dev.target < (12,0,0) else 0x927
|
||||
|
||||
token_mask = {} if self.dev.target < (12,0,0) else {'exclude_barrier_wait': 1}
|
||||
self.wreg(self.gc.regSQ_THREAD_TRACE_TOKEN_MASK, reg_include=reg_include, token_exclude=token_exclude, bop_events_token_include=1, **token_mask)
|
||||
# Enable SQTT
|
||||
self.sqtt_config(tracing=True)
|
||||
# Restore global broadcasting
|
||||
@@ -178,9 +190,6 @@ class AMDComputeQueue(HWQueue):
|
||||
# Wait for FINISH_PENDING==0
|
||||
self.pkt3(self.pm4.PACKET3_WAIT_REG_MEM, self.pm4.WAIT_REG_MEM_FUNCTION(WAIT_REG_MEM_FUNCTION_EQ),
|
||||
self.gc.regSQ_THREAD_TRACE_STATUS.addr[0], 0, 0, self.gc.regSQ_THREAD_TRACE_STATUS.fields_mask('finish_pending'), 4)
|
||||
# Wait for FINISH_DONE!=0
|
||||
self.pkt3(self.pm4.PACKET3_WAIT_REG_MEM, self.pm4.WAIT_REG_MEM_FUNCTION(WAIT_REG_MEM_FUNCTION_NEQ),
|
||||
self.gc.regSQ_THREAD_TRACE_STATUS.addr[0], 0, 0, self.gc.regSQ_THREAD_TRACE_STATUS.fields_mask('finish_done'), 4)
|
||||
# Disable SQTT
|
||||
self.sqtt_config(tracing=False)
|
||||
# Wait for BUSY==0
|
||||
@@ -804,7 +813,7 @@ class AMDDevice(HCQCompiled):
|
||||
# SQTT is disabled by default because of runtime overhead and big file sizes (~200mb to Tensor.full() two 4096x4096 tensors and matmul them)
|
||||
self.sqtt_enabled = PROFILE and bool(getenv("SQTT", 0))
|
||||
if self.sqtt_enabled:
|
||||
if self.target[0] != 11: raise RuntimeError(f'SQ Thread Tracing is not supported on gc:{self.target}')
|
||||
if self.target[0] < 11: raise RuntimeError(f'SQ Thread Tracing is not supported on gc:{self.target}')
|
||||
if not self.is_am() and (ppfeaturemask:=int(FileIOInterface('/sys/module/amdgpu/parameters/ppfeaturemask', os.O_RDONLY).read(), 16))&0x8000:
|
||||
raise RuntimeError("SQTT can't be enabled because of hardware bug, to workaround either use AMD_IFACE=PCI or add "
|
||||
f"ppfeaturemask={(ppfeaturemask&~0x8000):#x} (current {ppfeaturemask=:#x} & ~PP_GFXOFF_MASK) to amdgpu module parameters\n"
|
||||
@@ -872,7 +881,7 @@ class AMDDevice(HCQCompiled):
|
||||
self.synchronize()
|
||||
if DEBUG >= 2: print(f'{self.device}: Saving SQTT in profile...')
|
||||
for i,buf0 in enumerate(self.sqtt_buffers):
|
||||
wptr = ((struct.unpack('<I', wptrs[i*4:i*4+4])[0] & 0x1FFFFFFF) - ((buf0.va_addr//32) & 0x1FFFFFFF)) * 32
|
||||
wptr = ((struct.unpack('<I', wptrs[i*4:i*4+4])[0] & 0x1FFFFFFF) - (((buf0.va_addr//32) & 0x1FFFFFFF) if self.target < (12,0,0) else 0)) * 32
|
||||
if DEBUG >= 2: print(f'\t{self.device}: SE {i} blob size {wptr:#x}')
|
||||
assert wptr >= 0 and wptr <= buf0.size, f"{wptr} > {buf0.size}, should never happen"
|
||||
# When sqtt buffer overflows, wptr stops at the last dword
|
||||
|
||||
@@ -16,7 +16,7 @@ elif OSX:
|
||||
else:
|
||||
LLVM_PATH = ctypes.util.find_library('LLVM')
|
||||
# use newer LLVM if possible
|
||||
for ver in reversed(range(14, 20+1)):
|
||||
for ver in reversed(range(14, 21+1)):
|
||||
if LLVM_PATH is not None: break
|
||||
LLVM_PATH = ctypes.util.find_library(f'LLVM-{ver}')
|
||||
if LLVM_PATH is None:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Iterator, Sequence
|
||||
from typing import Iterator
|
||||
import functools, operator, itertools
|
||||
from dataclasses import dataclass, field
|
||||
from tinygrad.dtype import dtypes, AddrSpace
|
||||
@@ -41,7 +41,7 @@ class BufferizeOpts:
|
||||
@dataclass
|
||||
class IndexingContext:
|
||||
realize_map: dict[UOp, None] = field(default_factory=dict)
|
||||
range_map: dict[UOp, tuple[list[UOp], list[UOp]]] = field(default_factory=dict)
|
||||
range_map: dict[UOp, tuple[tuple[UOp, ...], tuple[UOp, ...]]] = field(default_factory=dict)
|
||||
|
||||
# create ranges
|
||||
range_idx: Iterator[int] = field(default_factory=itertools.count)
|
||||
@@ -103,30 +103,31 @@ pm_apply_rangeify = PatternMatcher([
|
||||
])
|
||||
|
||||
# this is the definition of the movement ops
|
||||
def apply_movement_op(x:UOp, rngs:Sequence[UOp]) -> list[UOp]:
|
||||
match x.op:
|
||||
case Ops.SHRINK: rngs = [a if ss == 0 else a+ss for a,(ss,_) in zip(rngs, x.arg)]
|
||||
case Ops.PERMUTE: rngs = [rngs[p] for p in argsort(x.arg)]
|
||||
case Ops.FLIP: rngs = [((s-1)-a) if f else a for a,s,f in zip(rngs, x.shape, x.arg)]
|
||||
case Ops.EXPAND: rngs = [a if in_sh == out_sh else a.const_like(0) for a,in_sh,out_sh in zip(rngs, x.src[0].shape, x.shape)]
|
||||
@functools.cache
|
||||
def apply_movement_op(op:Ops, in_shape:tuple[sint,...], arg:tuple, rngs:tuple[UOp, ...]) -> tuple[UOp, ...]:
|
||||
match op:
|
||||
case Ops.SHRINK: rngs = tuple(a if ss == 0 else a+ss for a,(ss,_) in zip(rngs, arg))
|
||||
case Ops.PERMUTE: rngs = tuple(rngs[p] for p in argsort(arg))
|
||||
case Ops.FLIP: rngs = tuple(((s-1)-a) if f else a for a,s,f in zip(rngs, in_shape, arg))
|
||||
case Ops.EXPAND: rngs = tuple(a if in_sh == out_sh else a.const_like(0) for a,in_sh,out_sh in zip(rngs, in_shape, arg))
|
||||
case Ops.PAD:
|
||||
# TODO: why is multiple graph_rewrites faster than one here?
|
||||
rngs = [r if (s == 0 and e == 0) else graph_rewrite(((r >= s) & (r < (sh-e))).where(r-s, UOp.invalid()), sym, name="pad")
|
||||
for r,sh,(s,e) in zip(rngs, x.shape, x.arg)]
|
||||
rngs = tuple(r if (s == 0 and e == 0) else graph_rewrite(((r >= s) & (r < (sh+s))).where(r-s, UOp.invalid()), sym, name="pad")
|
||||
for r,sh,(s,e) in zip(rngs, in_shape, arg))
|
||||
case Ops.RESHAPE:
|
||||
acc = 1
|
||||
axes_in:list[UOp] = []
|
||||
for s,src in list(zip(x.shape, rngs))[::-1]:
|
||||
for s,src in list(zip(arg, rngs))[::-1]:
|
||||
axes_in.append(acc*src)
|
||||
acc *= s
|
||||
combined_axes = sum(axes_in, start=UOp.const(dtypes.index, 0))
|
||||
axes_out:list[UOp] = []
|
||||
for s in x.src[0].shape[::-1]:
|
||||
for s in in_shape[::-1]:
|
||||
axes_out.append(combined_axes % s)
|
||||
combined_axes //= s
|
||||
# this simplify is doing a lot of heavy lifting. this is the replacement for the reshape view merging code
|
||||
rngs = list(graph_rewrite(UOp.sink(*axes_out[::-1]), symbolic, name="reshape").src)
|
||||
case _: raise RuntimeError(f"{x.op} is not a MovementOp")
|
||||
rngs = graph_rewrite(UOp.sink(*axes_out[::-1]), symbolic, name="reshape").src
|
||||
case _: raise RuntimeError(f"{op} is not a MovementOp")
|
||||
return rngs
|
||||
|
||||
@cpu_profile(TracingKey("run_rangeify"), "TINY")
|
||||
@@ -157,7 +158,7 @@ def run_rangeify(tsink:UOp, debug:bool=False) -> tuple[UOp, IndexingContext]:
|
||||
consumer_rngs = [rctx.range_map[c][0] for c in consumer_map[x] if c in rctx.range_map]
|
||||
if x in rctx.realize_map:
|
||||
# if this is in the realize_map, we create new ranges (at the output)
|
||||
out_rngs = [rctx.new_range(s) if not isinstance(s, UOp) or s.op is not Ops.RANGE else s for s in x.shape]
|
||||
out_rngs = tuple(rctx.new_range(s) if not isinstance(s, UOp) or s.op is not Ops.RANGE else s for s in x.shape)
|
||||
# all ranges are ended now
|
||||
ending_ranges[x] = False
|
||||
elif x.op in {Ops.MSTACK, Ops.MSELECT}:
|
||||
@@ -181,15 +182,16 @@ def run_rangeify(tsink:UOp, debug:bool=False) -> tuple[UOp, IndexingContext]:
|
||||
|
||||
# TODO: in RANGEIFY > 1 all_all_same isn't required
|
||||
all_all_same = all(same_rngs for _,_,same_rngs in rngs_valids)
|
||||
out_rngs = []
|
||||
_out_rngs = []
|
||||
for i,(local_rngs,valids,same_rngs) in enumerate(rngs_valids):
|
||||
# we compare the ranges without their valids
|
||||
if all_all_same:
|
||||
# the new valid is the OR of all the children valids
|
||||
minimum_valid = functools.reduce(operator.or_, valids, UOp.const(dtypes.bool, False))
|
||||
out_rngs.append(graph_rewrite(minimum_valid.where(local_rngs[0], UOp.invalid()), symbolic, name="minimum_valid"))
|
||||
_out_rngs.append(graph_rewrite(minimum_valid.where(local_rngs[0], UOp.invalid()), symbolic, name="minimum_valid"))
|
||||
else:
|
||||
out_rngs.append(rctx.new_range(x.shape[i]))
|
||||
_out_rngs.append(rctx.new_range(x.shape[i]))
|
||||
out_rngs = tuple(_out_rngs)
|
||||
|
||||
# we have to realize here if there's new ranges
|
||||
if not all_all_same: rctx.realize_map[x] = None
|
||||
@@ -203,18 +205,16 @@ def run_rangeify(tsink:UOp, debug:bool=False) -> tuple[UOp, IndexingContext]:
|
||||
# 2. newly created for REDUCE_AXIS
|
||||
# 3. passed through for everything else
|
||||
|
||||
rngs = out_rngs # rngs is the input ranges
|
||||
rngs = out_rngs # rngs is the input ranges # pylint: disable=possibly-used-before-assignment
|
||||
|
||||
# apply movement ops
|
||||
if x.op in GroupOp.Movement: rngs = apply_movement_op(x, rngs)
|
||||
if x.op in GroupOp.Movement: rngs = apply_movement_op(x.op, x.src[0].shape, x.arg, rngs)
|
||||
# if the EXPAND is used to inject a range, we don't mark it as ending_ranges. otherwise we do.
|
||||
if x.op is Ops.EXPAND and all(isinstance(y, int) or y.op is not Ops.RANGE for y in x.shape): ending_ranges[x] = True
|
||||
|
||||
# REDUCE_AXIS creates ranges for the axes it is reducing
|
||||
if x.op is Ops.REDUCE_AXIS:
|
||||
rngs = rngs[:]
|
||||
for i,s in enumerate(x.src[0].shape):
|
||||
if i in x.arg[1]: rngs[i] = rctx.new_range(s, axistype=AxisType.REDUCE)
|
||||
rngs = tuple(rctx.new_range(s, axistype=AxisType.REDUCE) if i in x.arg[1] else r for i,(r,s) in enumerate(zip(rngs, x.src[0].shape)))
|
||||
|
||||
if debug:
|
||||
print("***" if x in rctx.realize_map else " ", len(consumer_map[x]), f"{str(x.op):20s}",
|
||||
|
||||
@@ -103,7 +103,7 @@ earliest_rewrites = PatternMatcher([
|
||||
# movement op on INDEX as a PatternMatcher
|
||||
pm_mops = PatternMatcher([
|
||||
(UPat(GroupOp.Movement, name="r").f(Ops.INDEX, allow_any_len=True, name="idx"),
|
||||
lambda r,idx: r.src[0].index(*apply_movement_op(r, idx.src[1:]), dtype=idx.dtype, arg=idx.arg)),
|
||||
lambda r,idx: r.src[0].index(*apply_movement_op(r.op, r.src[0].shape, r.arg, idx.src[1:]), dtype=idx.dtype, arg=idx.arg)), # type: ignore
|
||||
])
|
||||
|
||||
# *****************
|
||||
@@ -442,7 +442,7 @@ def tag_uop(ctx:list[UOp], x:UOp):
|
||||
add_tags = PatternMatcher([
|
||||
# don't tag BUFFERs, they are global
|
||||
(UPat(GroupOp.All-{Ops.BUFFER, Ops.CONST, Ops.DEVICE, Ops.UNIQUE, Ops.DEFINE_VAR, Ops.BIND,
|
||||
Ops.MSTACK, Ops.MSELECT}.union(GroupOp.Movement), name="x"), tag_uop),
|
||||
Ops.MSTACK, Ops.MSELECT, Ops.RANGE}.union(GroupOp.Movement), name="x"), tag_uop),
|
||||
(UPat({Ops.MSTACK, Ops.MSELECT}, name="x"), lambda ctx,x: None if all(s.op is Ops.BUFFER for s in x.src) else tag_uop(ctx, x)),
|
||||
])
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
from typing import TypeVar
|
||||
from tinygrad.uop import Ops
|
||||
from tinygrad.helpers import T
|
||||
from tinygrad.dtype import dtypes
|
||||
from tinygrad.dtype import dtypes, ConstType
|
||||
|
||||
TMathTrait = TypeVar("TMathTrait", bound="MathTrait")
|
||||
class MathTrait:
|
||||
# required to implement
|
||||
def alu(self:T, op:Ops, *src) -> T: raise NotImplementedError
|
||||
def const_like(self:T, b) -> T: raise NotImplementedError
|
||||
def alu(self:TMathTrait, op:Ops, *src:TMathTrait) -> TMathTrait: raise NotImplementedError
|
||||
def const_like(self:TMathTrait, b:ConstType) -> TMathTrait: raise NotImplementedError
|
||||
|
||||
# great functions you get!
|
||||
def ufix(self, x): return self.const_like(x) if not isinstance(x, MathTrait) else x
|
||||
def _binop(self, op, x, reverse): return self.ufix(x).alu(op, self) if reverse else self.alu(op, self.ufix(x))
|
||||
def ufix(self:TMathTrait, x:ConstType|TMathTrait) -> TMathTrait: return self.const_like(x) if not isinstance(x, MathTrait) else x
|
||||
def _binop(self:TMathTrait, op:Ops, x:TMathTrait|ConstType, reverse:bool) -> TMathTrait:
|
||||
return self.ufix(x).alu(op, self) if reverse else self.alu(op, self.ufix(x))
|
||||
def logical_not(self): return self.ne(True)
|
||||
def neg(self):
|
||||
if (dtype:=getattr(self, 'dtype')) is None: raise TypeError(f"MathTraits __neg__ requires a dtype, {self=}")
|
||||
|
||||
@@ -240,7 +240,6 @@ class UOp(MathTrait, metaclass=UOpMetaClass):
|
||||
if s in ret: del ret[s]
|
||||
else:
|
||||
for s in self.src: ret.update(s.ranges)
|
||||
if self.op is Ops.ENDRANGE: del ret[self.src[0]]
|
||||
return ret
|
||||
|
||||
@property
|
||||
|
||||
@@ -108,6 +108,9 @@ tensor_uop_spec = buffer_spec+assign_spec+PatternMatcher([
|
||||
(UPat(Ops.COPY, name="copy", src=(UPat.var("x"), UPat(Ops.DEVICE)), arg=None), lambda copy,x: copy.dtype == x.dtype),
|
||||
(UPat(Ops.ALLREDUCE, name="red", src=(UPat.var("x"), UPat(Ops.DEVICE))), lambda red,x: red.dtype == x.dtype and isinstance(red.arg, Ops)),
|
||||
(UPat(Ops.MULTI, name="multi"), lambda multi: all(x.dtype == multi.dtype for x in multi.src) and isinstance(multi.arg, int)),
|
||||
|
||||
# REDUCE with an outerworld range
|
||||
(UPat(Ops.REDUCE, src=(UPat(),), allow_any_len=True, name="x"), lambda x: all(y.dtype == dtypes.index for y in x.src[1:])),
|
||||
])
|
||||
|
||||
# ***** uop type spec *****
|
||||
@@ -153,8 +156,7 @@ spec = PatternMatcher([
|
||||
(UPat(Ops.DEFINE_REG, src=()), lambda: True),
|
||||
(UPat(Ops.DEFINE_VAR, name="x"), lambda x: isinstance(x.arg[1], int) and isinstance(x.arg[2], int)),
|
||||
|
||||
(UPat(Ops.RANGE, src=(UPat.var("x"),), name="rng", allow_any_len=True),
|
||||
lambda rng,x: rng.dtype == x.dtype and isinstance(rng.arg, tuple) and len(rng.arg) >= 2 and \
|
||||
(UPat(Ops.RANGE, src=(UPat.var("x"),), name="rng"), lambda rng,x: rng.dtype == x.dtype and isinstance(rng.arg, tuple) and len(rng.arg) >= 2 and \
|
||||
all(isinstance(ra, int) for ra in rng.arg[0:-1]) and isinstance(rng.arg[-1], AxisType)),
|
||||
(UPat(Ops.SPECIAL, src=(UPat.var("x"),), name="s"), lambda s,x: s.dtype == x.dtype == dtypes.int32 and isinstance(s.arg, str)),
|
||||
|
||||
@@ -191,7 +193,7 @@ spec = PatternMatcher([
|
||||
(UPat((Ops.IDIV, Ops.MOD), name="x"), lambda x: None if dtypes.is_int(x.dtype) else False),
|
||||
(UPat(GroupOp.ALU, name="x"), lambda x: all(x.dtype.base == y.dtype.base for y in x.src)),
|
||||
|
||||
(UPat(Ops.ENDRANGE, dtype=dtypes.void, src=(UPat(Ops.RANGE),), allow_any_len=True), lambda: True),
|
||||
(UPat(Ops.ENDRANGE, dtype=dtypes.void, src=(UPat(Ops.RANGE),)), lambda: True),
|
||||
|
||||
# WMMA has a <a, b, acc>
|
||||
(UPat(Ops.WMMA, src=(UPat(), UPat(), UPat()), name="x"), lambda x: isinstance(x.arg, tuple) and len(x.arg) == 8),
|
||||
@@ -199,8 +201,9 @@ spec = PatternMatcher([
|
||||
(UPat(Ops.UNROLL, name="x"), lambda x: x.src[0].dtype.count == prod(y[1] for y in x.arg)),
|
||||
|
||||
# if has a <gate, barrier?>
|
||||
(UPat(Ops.IF, dtype=dtypes.void, src=(UPat(),), allow_any_len=True), lambda: True),
|
||||
(UPat(Ops.ENDIF, dtype=dtypes.void, src=(UPat(Ops.IF),), allow_any_len=True), lambda: True),
|
||||
(UPat(Ops.IF, dtype=dtypes.void, src=(UPat(),)), lambda: True),
|
||||
(UPat(Ops.IF, dtype=dtypes.void, src=(UPat(), UPat(Ops.BARRIER))), lambda: True),
|
||||
(UPat(Ops.ENDIF, dtype=dtypes.void, src=(UPat(Ops.IF),)), lambda: True),
|
||||
|
||||
(UPat(Ops.REDUCE_AXIS, name="x"), lambda x: isinstance(x.arg, tuple) and len(x.arg) >= 2 and x.arg[0] in {Ops.ADD, Ops.MUL, Ops.MAX}),
|
||||
(UPat(Ops.GEP, src=(UPat.var("src"),), name="gep"), lambda gep,src: gep.dtype == src.dtype.scalar()),
|
||||
|
||||
@@ -14,8 +14,9 @@ const darkenHex = (h, p = 0) =>
|
||||
).toString(16).padStart(6, '0')}`;
|
||||
|
||||
const ANSI_COLORS = ["#b3b3b3", "#ff6666", "#66b366", "#ffff66", "#6666ff", "#ff66ff", "#66ffff", "#ffffff"];
|
||||
const ANSI_COLORS_LIGHT = ["#d9d9d9","#ff9999","#99cc99","#ffff99","#9999ff","#ff99ff","#ccffff","#ffffff"];
|
||||
const parseColors = (name, defaultColor="#ffffff") => Array.from(name.matchAll(/(?:\u001b\[(\d+)m([\s\S]*?)\u001b\[0m)|([^\u001b]+)/g),
|
||||
([_, code, colored_st, st]) => ({ st: colored_st ?? st, color: code != null ? ANSI_COLORS[(parseInt(code)-30+60)%60] : defaultColor }));
|
||||
([_, code, colored_st, st]) => ({ st: colored_st ?? st, color: code != null ? (code>=90 ? ANSI_COLORS_LIGHT : ANSI_COLORS)[(parseInt(code)-30+60)%60] : defaultColor }));
|
||||
|
||||
const rect = (s) => (typeof s === "string" ? document.querySelector(s) : s).getBoundingClientRect();
|
||||
|
||||
@@ -170,7 +171,7 @@ function tabulate(rows) {
|
||||
return root;
|
||||
}
|
||||
|
||||
var data, focusedDevice, canvasZoom, zoomLevel = d3.zoomIdentity;
|
||||
var data, focusedDevice, focusedShape, canvasZoom, zoomLevel = d3.zoomIdentity;
|
||||
async function renderProfiler() {
|
||||
displayGraph("profiler");
|
||||
d3.select(".metadata").html("");
|
||||
@@ -281,7 +282,7 @@ async function renderProfiler() {
|
||||
const html = document.createElement("div");
|
||||
const rows = [["DType", dtype], ["Len", formatUnit(sz)], ["Size", formatUnit(nbytes, "B")], ["Lifetime", formatTime(dur)]];
|
||||
const info = html.appendChild(tabulate(rows).node());
|
||||
const arg = {tooltipText:info.outerHTML, html};
|
||||
const arg = {tooltipText:info.outerHTML, html, key:`${k}-${num}`};
|
||||
shapes.push({ x, y0:y.map(yscale), y1:y.map(y0 => yscale(y0+nbytes)), arg, fillColor:cycleColors(colorScheme.BUFFER, shapes.length) });
|
||||
}
|
||||
// generic polygon merger
|
||||
@@ -350,6 +351,7 @@ async function renderProfiler() {
|
||||
for (let i=x.length-1; i>=0; i--) ctx.lineTo(x[i], offsetY+e.y1[i]);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = e.fillColor; ctx.fill();
|
||||
if (focusedShape && e.arg?.key === focusedShape) { ctx.lineWidth = 1.4; ctx.strokeStyle = "#c9a8ff"; ctx.stroke(); }
|
||||
continue;
|
||||
}
|
||||
// contiguous rect
|
||||
@@ -443,6 +445,7 @@ async function renderProfiler() {
|
||||
e.preventDefault();
|
||||
const foundRect = findRectAtPosition(e.clientX, e.clientY);
|
||||
if (foundRect?.step != null) return setCtxWithHistory(foundRect.ctx, foundRect.step);
|
||||
if (foundRect?.key != focusedShape) { focusedShape = foundRect?.key; render(zoomLevel); }
|
||||
return document.querySelector(".metadata").replaceChildren(foundRect?.html ?? "");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user