refactor viz saved context (prereq for tree view) (#6516)

* more styling

* warns

* refactor viz ctx to dataclass

* meh, fine for now

* name ctx

* allow smaller zooms

* more work

* fixup ctx.diffs
This commit is contained in:
qazal
2024-09-15 14:08:55 +08:00
committed by GitHub
parent 893a24f60f
commit 2d53e47b14
3 changed files with 37 additions and 34 deletions
+9 -4
View File
@@ -721,7 +721,12 @@ class PatternMatcher:
TRACK_MATCH_STATS = getenv("TRACK_MATCH_STATS", 2 if getenv("VIZ") else 0)
match_stats:Dict[UPat, List[Union[int, float]]] = dict()
contexts: List[Tuple[Tuple[str, int], UOp, List[Tuple[UOp, UOp, str]]]] = []
@dataclass(frozen=True)
class TrackedRewriteContext:
loc: str # location that called graph_rewrite
sink: UOp # the sink passed into the rewrite
rewrites: List[Tuple[UOp, UOp, str]] # all rewrites of sparents. (before, after, UPat printable)
contexts: List[TrackedRewriteContext] = []
class TrackedPattenMatcher(PatternMatcher):
def __init__(self, patterns:List[Tuple[UPat, Callable]]):
super().__init__(patterns)
@@ -742,7 +747,7 @@ class TrackedPattenMatcher(PatternMatcher):
match_stats[p][2] += (et:=time.perf_counter()-st)
match_stats[p][3] += et
if TRACK_MATCH_STATS >= 3: print(f"{et*1e6:7.2f} us -- ", p.printable())
if TRACK_MATCH_STATS >= 2: contexts[-1][2].append((uop, ret, p.printable()))
if TRACK_MATCH_STATS >= 2: contexts[-1].rewrites.append((uop, ret, p.printable()))
return ret # NOTE: if it returns None, we keep trying to match
match_stats[p][2] += time.perf_counter()-st
return None
@@ -760,7 +765,7 @@ if TRACK_MATCH_STATS:
print(f"{ret[0]:6d} / {ret[1]:7d} -- {ret[3]*1000.:9.2f} / {ret[2]*1000.:9.2f} ms -- TOTAL")
if TRACK_MATCH_STATS >= 2:
with open("/tmp/rewrites.pkl", "wb") as f:
print(f"rewrote {len(contexts)} graphs and applied {sum(len(x[2]) for x in contexts)} rules, saved to /tmp/rewrites.pkl")
print(f"rewrote {len(contexts)} graphs and applied {sum(len(x.rewrites) for x in contexts)} rules, saved to /tmp/rewrites.pkl")
pickle.dump(contexts, f)
if getenv("VIZ"):
import viz.serve
@@ -782,5 +787,5 @@ class RewriteContext:
self.nodes[replace_source] = self.replace[n] = found = self.rewrite(new_x) if (new_x := self.pm.rewrite(x)) else x
return found
def graph_rewrite(sink:UOp, pm:PatternMatcher) -> UOp:
if TRACK_MATCH_STATS >= 2: contexts.append((get_location(), sink, []))
if TRACK_MATCH_STATS >= 2: contexts.append(TrackedRewriteContext(f"{(l:=get_location())[0].split('/')[-1]}:{l[1]}", sink, []))
return RewriteContext(pm).rewrite(sink)
+11 -14
View File
@@ -68,8 +68,6 @@
}
.metadata {
grid-column: span 3;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.uop-list {
@@ -102,9 +100,8 @@
}
.code-block {
max-height: 30%;
min-height: 30%;
background-color: #191919;
overflow-y: auto;
background-color: #191919;
border-radius: 8px;
padding: 8px;
}
@@ -127,12 +124,12 @@
var totalRewrites = 0;
async function main() {
const ret = await (await fetch("/"+currentUOp)).json()
const [location, graphs, blocks, rest] = ret;
const [ctx, rest] = ret;
totalUOps = rest.length-1;
totalRewrites = graphs.length-1;
totalRewrites = ctx.graphs.length-1;
// graph
const g = new dagreD3.graphlib.Graph().setGraph({ rankdir: "LR" }).setDefaultEdgeLabel(function() { return {}; });
const [graph, pattern] = graphs[currentRewrite];
const graph = ctx.graphs[currentRewrite];
for ([k,u] of Object.entries(graph)) {
g.setNode(k, {label: u[0], style: `fill: ${u[4]}; rx: 8; ry: 8;` });
for (src of u[2]) {
@@ -142,7 +139,7 @@
const svg = d3.select("svg");
const inner = svg.select("g");
var zoom = d3.zoom()
.scaleExtent([0.25, 2])
.scaleExtent([0.05, 2])
.on("zoom", () => {
const transform = d3.event.transform;
inner.attr("transform", transform);
@@ -153,21 +150,21 @@
// metadata
const container = document.querySelector(".container.metadata");
container.innerHTML = "";
container.appendChild(Object.assign(document.createElement("pre"), { textContent: location }));
blocks.forEach((b) => {
container.appendChild(Object.assign(document.createElement("pre"), { textContent: ctx.loc }));
ctx.extra.forEach((b) => {
if (b.length == 0) return;
const pre = Object.assign(document.createElement("pre"), { innerHTML: `<code>${b}</code>`, className: "code-block" });
container.appendChild(pre);
})
if (graphs.length > 1) {
if (ctx.graphs.length > 1) {
const rewriteCounter = Object.assign(document.createElement("div"), { className: "rewrite-counter" });
container.appendChild(rewriteCounter)
graphs.forEach((g, i) => {
ctx.graphs.forEach((g, i) => {
const rewriteDiv = Object.assign(document.createElement("div"), { textContent: i, className: "uop-el" });
if (i === currentRewrite) {
rewriteDiv.classList.add("active-uop-el");
if (g[1] != null) {
const [pattern, diff] = g[1]
if (i !== 0) {
const [pattern, diff] = ctx.diffs[i-1];
container.appendChild(Object.assign(document.createElement("pre"), { innerHTML: `<code>${pattern}</code>`, className: "wrap" }));
const diffHtml = diff.map((line) => {
if (line.startsWith("+")) return `<span style="color: #30A46C;">${line}</span>`;
+17 -16
View File
@@ -1,12 +1,12 @@
#!/usr/bin/env python3
from dataclasses import asdict, dataclass
from typing import Dict, List, Optional, Tuple
from typing import Dict, List, Tuple
import pickle, re, os, sys, time, threading, webbrowser, json, difflib
from tinygrad.codegen.uopgraph import linearize_uop
from tinygrad.device import Device
from tinygrad.engine.realize import get_runner
from tinygrad.helpers import getenv
from tinygrad.ops import UOp, UOps
from tinygrad.ops import TrackedRewriteContext, UOp, UOps
from tinygrad.engine.graph import uops_colors, word_wrap
from http.server import HTTPServer, BaseHTTPRequestHandler
@@ -37,22 +37,24 @@ def uop_to_prg(ast:UOp) -> str:
@dataclass(frozen=True)
class UOpRet:
loc: str
graphs: List[Tuple[Dict[int, Tuple[str, str, List[int], str, str]], Optional[Tuple[str, List[str]]]]]
extra: List[str]
loc: str # location that called graph_rewrite
graphs: List[Dict[int, Tuple[str, str, List[int], str, str]]] # a seralized version of UOp graphs
diffs: List[Tuple[str, List[str]]] # the diffs for each rewrite
extra: List[str] # these become code blocks in the UI
def create_graph(ctx:Tuple[Tuple[str, int], UOp, List[Tuple[UOp, UOp, str]]]) -> UOpRet:
loc, start, matches = ctx
graphs: List[Tuple[Dict, Optional[Tuple[str, List[str]]]]] = [(uop_to_json(start), None)]
for first, rewritten, pattern in matches:
def create_graph(ctx:TrackedRewriteContext) -> UOpRet:
graphs = [uop_to_json(ctx.sink)]
diffs = []
for first, rewritten, pattern in ctx.rewrites:
diff = list(difflib.unified_diff(str(first).splitlines(), str(rewritten).splitlines()))
graph = {**graphs[-1][0], **uop_to_json(rewritten)}
graph = {**graphs[-1], **uop_to_json(rewritten)}
for k,v in graph.copy().items():
if any(x == id(first) for x in v[2]):
graph[k] = v[:2]+([id(rewritten) if x == id(first) else x for x in v[2]],)+v[3:]
if k == id(first): del graph[k]
graphs.append((graph, (pattern, diff)))
return UOpRet(f"{loc[0].split('/')[-1]}:{loc[1]}", graphs, [str(start), uop_to_prg(start)] if start.op is UOps.SINK else [str(start)])
graphs.append(graph)
diffs.append((pattern, diff))
return UOpRet(ctx.loc, graphs, diffs, [str(ctx.sink), uop_to_prg(ctx.sink)] if ctx.sink.op is UOps.SINK else [str(ctx.sink)])
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
@@ -72,11 +74,10 @@ class Handler(BaseHTTPRequestHandler):
self.send_response(200)
self.send_header("Content-type", "application/json")
self.end_headers()
with open("/tmp/rewrites.pkl", "rb") as f: contexts = pickle.load(f)
# TOOD: unify this loc_str logic
rest = [f"{x[0][0].split('/')[-1]}:{x[0][1]}" for x in contexts]
with open("/tmp/rewrites.pkl", "rb") as f: contexts: List[TrackedRewriteContext] = pickle.load(f)
rest = [x.loc for x in contexts]
current_graph = create_graph(contexts[int(self.path.split("/")[-1])])
ret = json.dumps(tuple(asdict(current_graph).values())+(rest,)).encode()
ret = json.dumps((asdict(current_graph), rest)).encode()
else:
self.send_response(404)
ret = b""