From 4e60bc453f3f8e956f6616708a7e9ca7153be204 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Wed, 1 Jan 2025 17:43:40 +0100 Subject: [PATCH 01/16] Dump graph as dot --- src/hvm.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/hvm.rs b/src/hvm.rs index c0d77313..6e76d7a0 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -914,12 +914,14 @@ impl TMem { //let mut max_rlen = 0; //let mut max_nlen = 0; //let mut max_vlen = 0; + println!("{}\n\n", net.dump(&self.rbag)); // Performs some interactions while self.rbag.len() > 0 { self.interact(net, book); // DEBUG: + println!("{}\n\n", net.dump(&self.rbag)); //println!("{}{}", self.rbag.show(), net.show()); //println!(""); //let rlen = self.rbag.lo.len() + self.rbag.hi.len(); @@ -1071,6 +1073,68 @@ impl<'a> GNet<'a> { s.push_str("==== | ============ |\n"); return s; } + + fn port2node(port: Port) -> String { + match port.get_tag() { + VAR => format!("VAR{:08X}", port.get_val()), + REF => format!("REF{:08X}", port.get_val()), + ERA => format!("ERA{:08X}", port.get_val()), + NUM => format!("NUM{:08X}", port.get_val()), + CON => format!("CON{:08X}", port.get_val()), + DUP => format!("DUP{:08X}", port.get_val()), + OPR => format!("OPR{:08X}", port.get_val()), + SWI => format!("SWI{:08X}", port.get_val()), + _ => panic!("Invalid tag"), + } + } + + fn _dump(&self, port: Port) -> String { + let mut s = String::new(); + + if port.is_nod() { + let node = self.node_load(port.get_val() as usize); + s.push_str(&format!("{} [shape=triangle];\n", GNet::port2node(port))); + s.push_str(&format!("{} -> {};\n", GNet::port2node(port), GNet::port2node(node.get_fst()))); + s.push_str(&format!("{} -> {};\n", GNet::port2node(port), GNet::port2node(node.get_snd()))); + s.push_str(&self._dump(node.get_fst())); + s.push_str(&self._dump(node.get_snd())); + } else { + s.push_str(&format!("{};\n", GNet::port2node(port))); + } + + return s; + } + + pub fn dump(&self, rbag: &RBag) -> String { + let mut s = String::new(); + + s.push_str("digraph {\n"); + s.push_str("edge [arrowhead=inv];\n"); + + s.push_str("{\n"); + s.push_str("edge [dir=both,arrowhead=inv,arrowtail=inv,color=red]; node [color=red];\n"); + for pair in rbag.hi.iter() { + s.push_str(&format!("{} -> {};\n", GNet::port2node(pair.get_fst()), GNet::port2node(pair.get_snd()))); + } + for pair in rbag.lo.iter() { + s.push_str(&format!("{} -> {};\n", GNet::port2node(pair.get_fst()), GNet::port2node(pair.get_snd()))); + } + s.push_str("}\n"); + + for pair in rbag.hi.iter() { + s.push_str(&self._dump(pair.get_fst())); + s.push_str(&self._dump(pair.get_snd())); + } + + for pair in rbag.lo.iter() { + s.push_str(&self._dump(pair.get_fst())); + s.push_str(&self._dump(pair.get_snd())); + } + + s.push_str("}\n"); + + return s; + } } impl Book { From 97ce222b4bdd570ac5d3974513d2d2b5619033f2 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Thu, 2 Jan 2025 15:36:48 +0100 Subject: [PATCH 02/16] Dump as javascript array --- src/hvm.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 16 deletions(-) diff --git a/src/hvm.rs b/src/hvm.rs index 6e76d7a0..15eef105 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -1,6 +1,7 @@ use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; use std::alloc::{alloc, dealloc, Layout}; use std::mem; +use std::fs; // Runtime // ======= @@ -907,6 +908,8 @@ impl TMem { } pub fn evaluator(&mut self, net: &GNet, book: &Book) { + let mut s = String::new(); + // Increments the tick self.tick += 1; @@ -914,14 +917,15 @@ impl TMem { //let mut max_rlen = 0; //let mut max_nlen = 0; //let mut max_vlen = 0; - println!("{}\n\n", net.dump(&self.rbag)); + s.push_str("var dots = [\n"); + s.push_str(&format!("`{}`,\n", net.dump(&self.rbag))); // Performs some interactions while self.rbag.len() > 0 { self.interact(net, book); // DEBUG: - println!("{}\n\n", net.dump(&self.rbag)); + s.push_str(&format!("`{}`,\n", net.dump(&self.rbag))); //println!("{}{}", self.rbag.show(), net.show()); //println!(""); //let rlen = self.rbag.lo.len() + self.rbag.hi.len(); @@ -943,6 +947,9 @@ impl TMem { } + s.push_str("]\n"); + let _ = fs::write("dots.js", s); + // DEBUG: //println!("MAX_RLEN: {}", max_rlen); //println!("MAX_NLEN: {}", max_nlen); @@ -1046,6 +1053,8 @@ impl RBag { } } +static mut unique: AtomicU32 = AtomicU32::new(0); + impl<'a> GNet<'a> { pub fn show(&self) -> String { let mut s = String::new(); @@ -1074,12 +1083,50 @@ impl<'a> GNet<'a> { return s; } - fn port2node(port: Port) -> String { + fn myenter(&self, mut var: Port) -> Port { + while var.get_tag() == VAR { + //let val = self.vars_exchange(var.get_val() as usize, NONE); + let index = var.get_val() as usize; + let val = Port(self.vars[index].0.load(Ordering::Relaxed)); + // If there was no `B'`, stop, as there is no extension + if val == NONE || val == Port(0) { + break; + } + // Otherwise, delete `B` (we own both) and continue as `A ~> B'` + //self.vars_take(var.get_val() as usize); + var = val; + } + return var; + } + + fn _dump_var(&self, var: Port) -> String { + let mut s = String::new(); + + if var.get_tag() == VAR { + let index = var.get_val() as usize; + let val = Port(self.vars[index].0.load(Ordering::Relaxed)); + s.push_str(&format!("{} -> {};\n", self.port2node_var(var, index), self.port2node_var(val, index))); + if val == NONE || val == Port(0) { + return s; + } else { + s.push_str(&self._dump_var(val)); + } + } else { + s.push_str(&self._dump(var)); + } + + return s; + } + + fn _port2node(&self, port: Port, suffix: String) -> String { + if port == NONE { + return format!("NONE_{}", suffix); + } match port.get_tag() { VAR => format!("VAR{:08X}", port.get_val()), - REF => format!("REF{:08X}", port.get_val()), - ERA => format!("ERA{:08X}", port.get_val()), - NUM => format!("NUM{:08X}", port.get_val()), + REF => format!("REF{:08X}_{}", port.get_val(), suffix), + ERA => format!("ERA{:08X}_{}", port.get_val(), suffix), + NUM => format!("NUM{:08X}_{}", port.get_val(), suffix), CON => format!("CON{:08X}", port.get_val()), DUP => format!("DUP{:08X}", port.get_val()), OPR => format!("OPR{:08X}", port.get_val()), @@ -1088,18 +1135,35 @@ impl<'a> GNet<'a> { } } + fn port2node_parent(&self, port: Port, index: u32) -> String { + return self._port2node(port, format!("nod{}", index)); + } + + fn port2node_red(&self, port: Port, index: usize) -> String { + return self._port2node(port, format!("red{}", index)); + } + + fn port2node_var(&self, port: Port, index: usize) -> String { + return self._port2node(port, format!("var{}", index)); + } + + fn port2node(&self, port: Port) -> String { + return self._port2node(port, format!("unq{}", unsafe {unique.fetch_add(1, Ordering::Relaxed)})); + } + fn _dump(&self, port: Port) -> String { let mut s = String::new(); if port.is_nod() { let node = self.node_load(port.get_val() as usize); - s.push_str(&format!("{} [shape=triangle];\n", GNet::port2node(port))); - s.push_str(&format!("{} -> {};\n", GNet::port2node(port), GNet::port2node(node.get_fst()))); - s.push_str(&format!("{} -> {};\n", GNet::port2node(port), GNet::port2node(node.get_snd()))); + let parent = port.get_val(); + s.push_str(&format!("{} [shape=triangle];\n", self.port2node(port))); + s.push_str(&format!("{} -> {};\n", self.port2node(port), self.port2node_parent(node.get_fst(), parent))); + s.push_str(&format!("{} -> {};\n", self.port2node(port), self.port2node_parent(node.get_snd(), parent))); s.push_str(&self._dump(node.get_fst())); s.push_str(&self._dump(node.get_snd())); - } else { - s.push_str(&format!("{};\n", GNet::port2node(port))); + } else if port.is_var() { + s.push_str(&self._dump_var(port)); } return s; @@ -1108,16 +1172,21 @@ impl<'a> GNet<'a> { pub fn dump(&self, rbag: &RBag) -> String { let mut s = String::new(); - s.push_str("digraph {\n"); + s.push_str("strict digraph {\n"); s.push_str("edge [arrowhead=inv];\n"); + s.push_str("{\n"); + s.push_str("edge [color=green]; node [color=green];\n"); + s.push_str(&self._dump_var(ROOT)); + s.push_str("}\n"); + s.push_str("{\n"); s.push_str("edge [dir=both,arrowhead=inv,arrowtail=inv,color=red]; node [color=red];\n"); - for pair in rbag.hi.iter() { - s.push_str(&format!("{} -> {};\n", GNet::port2node(pair.get_fst()), GNet::port2node(pair.get_snd()))); + for (i, pair) in rbag.hi.iter().enumerate() { + s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i), self.port2node_red(pair.get_snd(), i))); } - for pair in rbag.lo.iter() { - s.push_str(&format!("{} -> {};\n", GNet::port2node(pair.get_fst()), GNet::port2node(pair.get_snd()))); + for (i, pair) in rbag.lo.iter().enumerate() { + s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i + 50), self.port2node_red(pair.get_snd(), i + 50))); } s.push_str("}\n"); From ffaa063fbb4710589620b563b7f6b8e2899da5d2 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Thu, 2 Jan 2025 15:37:06 +0100 Subject: [PATCH 03/16] Add viewer html --- index.html | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 00000000..b3eed38a --- /dev/null +++ b/index.html @@ -0,0 +1,76 @@ + + + + + + + + +
+ \ No newline at end of file From 8923e5356790a147f960e170ff37552477c06ace Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Thu, 2 Jan 2025 18:46:47 +0100 Subject: [PATCH 04/16] Decorate leaf nodes --- index.html | 3 ++- src/hvm.rs | 70 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/index.html b/index.html index b3eed38a..51ee9f77 100644 --- a/index.html +++ b/index.html @@ -27,11 +27,12 @@ .transition(function () { return d3.transition("main") .ease(d3.easeLinear) - .duration(120); + .duration(200); }) .logEvents(false) .width(div.clientWidth) .height(div.clientHeight) + .fit(false) .on("initEnd", render); window.addEventListener("keydown", function (e) { diff --git a/src/hvm.rs b/src/hvm.rs index 15eef105..7f5c17f6 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -1099,25 +1099,6 @@ impl<'a> GNet<'a> { return var; } - fn _dump_var(&self, var: Port) -> String { - let mut s = String::new(); - - if var.get_tag() == VAR { - let index = var.get_val() as usize; - let val = Port(self.vars[index].0.load(Ordering::Relaxed)); - s.push_str(&format!("{} -> {};\n", self.port2node_var(var, index), self.port2node_var(val, index))); - if val == NONE || val == Port(0) { - return s; - } else { - s.push_str(&self._dump_var(val)); - } - } else { - s.push_str(&self._dump(var)); - } - - return s; - } - fn _port2node(&self, port: Port, suffix: String) -> String { if port == NONE { return format!("NONE_{}", suffix); @@ -1151,19 +1132,58 @@ impl<'a> GNet<'a> { return self._port2node(port, format!("unq{}", unsafe {unique.fetch_add(1, Ordering::Relaxed)})); } + fn decorate(&self, port: Port, id: String) -> String { + let tag = port.get_tag(); + let val = port.get_val(); + + if port == NONE { + return format!("{} [shape=box, label=\"NONE\"];\n", id); + } + + if port.is_nod() || port.is_var() { + return "".to_string(); + } + + if tag == NUM { + return format!("{} [shape=box, label=\"NUM {}\"];\n", id, crate::ast::Numb(Numb(val).0).show()); + } else if tag == ERA { + return format!("{} [shape=star, label=\"ERA\"];\n", id); + } else if tag == REF { + //let fid = val & 0xFFFFFFF; + //let def = &book.defs[fid]; + return format!("{} [shape=trapezium, label=\"REF\"];\n", id); + } + + return "".to_string(); + } + fn _dump(&self, port: Port) -> String { let mut s = String::new(); if port.is_nod() { let node = self.node_load(port.get_val() as usize); let parent = port.get_val(); - s.push_str(&format!("{} [shape=triangle];\n", self.port2node(port))); + let tag = port.get_tag(); + if tag == OPR { + s.push_str(&format!("{} [shape=triangle, label=\"{}\n{}\"];\n", self.port2node(port), crate::ast::Numb(Numb(node.get_fst().get_val()).0).show(), self.port2node(port))); + } else { + s.push_str(&format!("{} [shape=triangle, label=\"{}\"];\n", self.port2node(port), self.port2node(port))); + } s.push_str(&format!("{} -> {};\n", self.port2node(port), self.port2node_parent(node.get_fst(), parent))); s.push_str(&format!("{} -> {};\n", self.port2node(port), self.port2node_parent(node.get_snd(), parent))); + s.push_str(&self.decorate(node.get_fst(), self.port2node_parent(node.get_fst(), parent))); s.push_str(&self._dump(node.get_fst())); + s.push_str(&self.decorate(node.get_snd(), self.port2node_parent(node.get_snd(), parent))); s.push_str(&self._dump(node.get_snd())); + } else if port.is_var() { - s.push_str(&self._dump_var(port)); + let index = port.get_val() as usize; + let var = Port(self.vars[index].0.load(Ordering::Relaxed)); + s.push_str(&format!("{} -> {};\n", self.port2node_var(port, index), self.port2node_var(var, index))); + s.push_str(&self.decorate(var, self.port2node_var(var, index))); + if var != NONE && var != Port(0) { + s.push_str(&self._dump(var)); + } } return s; @@ -1177,16 +1197,20 @@ impl<'a> GNet<'a> { s.push_str("{\n"); s.push_str("edge [color=green]; node [color=green];\n"); - s.push_str(&self._dump_var(ROOT)); + s.push_str(&self._dump(ROOT)); s.push_str("}\n"); s.push_str("{\n"); s.push_str("edge [dir=both,arrowhead=inv,arrowtail=inv,color=red]; node [color=red];\n"); for (i, pair) in rbag.hi.iter().enumerate() { + s.push_str(&self.decorate(pair.get_fst(), self.port2node_red(pair.get_fst(), i))); + s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i))); s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i), self.port2node_red(pair.get_snd(), i))); } for (i, pair) in rbag.lo.iter().enumerate() { - s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i + 50), self.port2node_red(pair.get_snd(), i + 50))); + s.push_str(&self.decorate(pair.get_fst(), self.port2node_red(pair.get_fst(), i + 100))); + s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i + 100))); + s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i + 100), self.port2node_red(pair.get_snd(), i + 100))); } s.push_str("}\n"); From cd9fe83dd14efe7d38ebacf3ab8f65137131c067 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Thu, 2 Jan 2025 20:25:07 +0100 Subject: [PATCH 05/16] Dump REF names too --- index.html | 4 ++++ src/hvm.rs | 46 +++++++++++++++++++++++----------------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/index.html b/index.html index 51ee9f77..9d1f6800 100644 --- a/index.html +++ b/index.html @@ -33,6 +33,10 @@ .width(div.clientWidth) .height(div.clientHeight) .fit(false) + .tweenPaths(false) + .tweenShapes(false) + .growEnteringEdges(false) + .fade(true) .on("initEnd", render); window.addEventListener("keydown", function (e) { diff --git a/src/hvm.rs b/src/hvm.rs index 7f5c17f6..3882a797 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -918,14 +918,14 @@ impl TMem { //let mut max_nlen = 0; //let mut max_vlen = 0; s.push_str("var dots = [\n"); - s.push_str(&format!("`{}`,\n", net.dump(&self.rbag))); + s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); // Performs some interactions while self.rbag.len() > 0 { self.interact(net, book); // DEBUG: - s.push_str(&format!("`{}`,\n", net.dump(&self.rbag))); + s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); //println!("{}{}", self.rbag.show(), net.show()); //println!(""); //let rlen = self.rbag.lo.len() + self.rbag.hi.len(); @@ -1132,7 +1132,7 @@ impl<'a> GNet<'a> { return self._port2node(port, format!("unq{}", unsafe {unique.fetch_add(1, Ordering::Relaxed)})); } - fn decorate(&self, port: Port, id: String) -> String { + fn decorate(&self, port: Port, id: String, book: &Book) -> String { let tag = port.get_tag(); let val = port.get_val(); @@ -1149,15 +1149,15 @@ impl<'a> GNet<'a> { } else if tag == ERA { return format!("{} [shape=star, label=\"ERA\"];\n", id); } else if tag == REF { - //let fid = val & 0xFFFFFFF; - //let def = &book.defs[fid]; - return format!("{} [shape=trapezium, label=\"REF\"];\n", id); + let fid = (val & 0xFFFFFFF) as usize; + let def: &Def = &book.defs[fid]; + return format!("{} [shape=trapezium, label=\"REF {}\"];\n", id, def.name); } return "".to_string(); } - fn _dump(&self, port: Port) -> String { + fn _dump(&self, port: Port, book: &Book) -> String { let mut s = String::new(); if port.is_nod() { @@ -1171,25 +1171,25 @@ impl<'a> GNet<'a> { } s.push_str(&format!("{} -> {};\n", self.port2node(port), self.port2node_parent(node.get_fst(), parent))); s.push_str(&format!("{} -> {};\n", self.port2node(port), self.port2node_parent(node.get_snd(), parent))); - s.push_str(&self.decorate(node.get_fst(), self.port2node_parent(node.get_fst(), parent))); - s.push_str(&self._dump(node.get_fst())); - s.push_str(&self.decorate(node.get_snd(), self.port2node_parent(node.get_snd(), parent))); - s.push_str(&self._dump(node.get_snd())); + s.push_str(&self.decorate(node.get_fst(), self.port2node_parent(node.get_fst(), parent), book)); + s.push_str(&self._dump(node.get_fst(), book)); + s.push_str(&self.decorate(node.get_snd(), self.port2node_parent(node.get_snd(), parent), book)); + s.push_str(&self._dump(node.get_snd(), book)); } else if port.is_var() { let index = port.get_val() as usize; let var = Port(self.vars[index].0.load(Ordering::Relaxed)); s.push_str(&format!("{} -> {};\n", self.port2node_var(port, index), self.port2node_var(var, index))); - s.push_str(&self.decorate(var, self.port2node_var(var, index))); + s.push_str(&self.decorate(var, self.port2node_var(var, index), book)); if var != NONE && var != Port(0) { - s.push_str(&self._dump(var)); + s.push_str(&self._dump(var, book)); } } return s; } - pub fn dump(&self, rbag: &RBag) -> String { + pub fn dump(&self, rbag: &RBag, book: &Book) -> String { let mut s = String::new(); s.push_str("strict digraph {\n"); @@ -1197,31 +1197,31 @@ impl<'a> GNet<'a> { s.push_str("{\n"); s.push_str("edge [color=green]; node [color=green];\n"); - s.push_str(&self._dump(ROOT)); + s.push_str(&self._dump(ROOT, book)); s.push_str("}\n"); s.push_str("{\n"); s.push_str("edge [dir=both,arrowhead=inv,arrowtail=inv,color=red]; node [color=red];\n"); for (i, pair) in rbag.hi.iter().enumerate() { - s.push_str(&self.decorate(pair.get_fst(), self.port2node_red(pair.get_fst(), i))); - s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i))); + s.push_str(&self.decorate(pair.get_fst(), self.port2node_red(pair.get_fst(), i), book)); + s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i), book)); s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i), self.port2node_red(pair.get_snd(), i))); } for (i, pair) in rbag.lo.iter().enumerate() { - s.push_str(&self.decorate(pair.get_fst(), self.port2node_red(pair.get_fst(), i + 100))); - s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i + 100))); + s.push_str(&self.decorate(pair.get_fst(), self.port2node_red(pair.get_fst(), i + 100), book)); + s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i + 100), book)); s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i + 100), self.port2node_red(pair.get_snd(), i + 100))); } s.push_str("}\n"); for pair in rbag.hi.iter() { - s.push_str(&self._dump(pair.get_fst())); - s.push_str(&self._dump(pair.get_snd())); + s.push_str(&self._dump(pair.get_fst(), book)); + s.push_str(&self._dump(pair.get_snd(), book)); } for pair in rbag.lo.iter() { - s.push_str(&self._dump(pair.get_fst())); - s.push_str(&self._dump(pair.get_snd())); + s.push_str(&self._dump(pair.get_fst(), book)); + s.push_str(&self._dump(pair.get_snd(), book)); } s.push_str("}\n"); From be04320a9eb1ab2808dfca8bd76486dc233a6b8e Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Fri, 10 Jan 2025 15:18:38 +0100 Subject: [PATCH 06/16] Add index via hash --- index.html | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 9d1f6800..88096e11 100644 --- a/index.html +++ b/index.html @@ -13,7 +13,7 @@ height: 100%; } - + @@ -23,6 +23,7 @@ const div = document.getElementById("graph"); var offset = 0; var dotIndex = 0; +readHash(); var graphviz = d3.select("#graph").graphviz() .transition(function () { return d3.transition("main") @@ -59,6 +60,20 @@ offset = 0; }); +window.addEventListener("hashchange", function (e) { + readHash(); + render(); +}); + +function readHash() { + if (window.location.hash) { + dotIndex = parseInt(window.location.hash.slice(1), 10); + if (isNaN(dotIndex)) { + dotIndex = 0; + } + } +} + function render() { graphviz.renderDot(dots[dotIndex]); graphviz.on("end", update); @@ -75,6 +90,7 @@ } else if (dotIndex >= dots.length) { dotIndex -= dots.length; } + window.location.hash = "#" + dotIndex; render(); } From de75f3b47c66de69e0b53f4fbb3e73bf68018fab Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Fri, 10 Jan 2025 15:26:16 +0100 Subject: [PATCH 07/16] Make NUM node shapes more distinctive --- src/hvm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hvm.rs b/src/hvm.rs index 3882a797..6a56ccd6 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -1137,7 +1137,7 @@ impl<'a> GNet<'a> { let val = port.get_val(); if port == NONE { - return format!("{} [shape=box, label=\"NONE\"];\n", id); + return format!("{} [shape=diamond, label=\"NONE\"];\n", id); } if port.is_nod() || port.is_var() { @@ -1145,7 +1145,7 @@ impl<'a> GNet<'a> { } if tag == NUM { - return format!("{} [shape=box, label=\"NUM {}\"];\n", id, crate::ast::Numb(Numb(val).0).show()); + return format!("{} [shape=diamond, label=\"NUM {}\"];\n", id, crate::ast::Numb(Numb(val).0).show()); } else if tag == ERA { return format!("{} [shape=star, label=\"ERA\"];\n", id); } else if tag == REF { From 9f249014b3de2293b6ad8218406feaafb085bd7c Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Tue, 14 Jan 2025 15:25:11 +0100 Subject: [PATCH 08/16] More stable graph --- src/hvm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hvm.rs b/src/hvm.rs index 6a56ccd6..51277daf 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -1165,7 +1165,7 @@ impl<'a> GNet<'a> { let parent = port.get_val(); let tag = port.get_tag(); if tag == OPR { - s.push_str(&format!("{} [shape=triangle, label=\"{}\n{}\"];\n", self.port2node(port), crate::ast::Numb(Numb(node.get_fst().get_val()).0).show(), self.port2node(port))); + s.push_str(&format!("{} [shape=triangle, label=\"{}\\n{}\"];\n", self.port2node(port), crate::ast::Numb(Numb(node.get_fst().get_val()).0).show(), self.port2node(port))); } else { s.push_str(&format!("{} [shape=triangle, label=\"{}\"];\n", self.port2node(port), self.port2node(port))); } @@ -1192,7 +1192,7 @@ impl<'a> GNet<'a> { pub fn dump(&self, rbag: &RBag, book: &Book) -> String { let mut s = String::new(); - s.push_str("strict digraph {\n"); + s.push_str("strict digraph { ordering=\"out\";\n"); s.push_str("edge [arrowhead=inv];\n"); s.push_str("{\n"); From 3bbe2b7d679ab61c368e69f92fb83d0cc7ca1f34 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Tue, 14 Jan 2025 21:44:35 +0100 Subject: [PATCH 09/16] Add option manage RBag as queue --- src/hvm.rs | 54 ++++++++++++++++++++++------------------------------- src/main.rs | 2 +- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/hvm.rs b/src/hvm.rs index 51277daf..e7562acc 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -83,8 +83,11 @@ pub const NONE : Port = Port(0xFFFFFFFF); // RBag pub struct RBag { - pub lo: Vec, pub hi: Vec, + pub gen: Vec, + pub cur_gen: u32, + pub queue: bool, + pub last_pop_idx: usize, } // Global Net @@ -419,36 +422,37 @@ impl Numb { } impl RBag { - pub fn new() -> Self { + pub fn new(queue: bool) -> Self { RBag { - lo: Vec::new(), hi: Vec::new(), + gen: Vec::new(), + cur_gen: 0, + queue, + last_pop_idx: 0, } } pub fn push_redex(&mut self, redex: Pair) { - let rule = Port::get_rule(redex.get_fst(), redex.get_snd()); - if Port::is_high_priority(rule) { - self.hi.push(redex); - } else { - self.lo.push(redex); - } + self.gen.push(self.cur_gen); + self.hi.push(redex); } pub fn pop_redex(&mut self) -> Option { if !self.hi.is_empty() { - self.hi.pop() + self.last_pop_idx = self.hi.len() - 1; + let ret = if self.queue {self.hi.remove(0)} else {self.hi.pop().unwrap()}; + let gen = if self.queue {self.gen.remove(0)} else {self.gen.pop().unwrap()}; + if gen == self.cur_gen { + self.cur_gen += 1; + } + return Some(ret); } else { - self.lo.pop() + return None; } } pub fn len(&self) -> usize { - self.lo.len() + self.hi.len() - } - - pub fn has_highs(&self) -> bool { - !self.hi.is_empty() + self.hi.len() } } @@ -543,7 +547,7 @@ impl<'a> Drop for GNet<'a> { impl TMem { // TODO: implement a TMem::new() fn - pub fn new(tid: u32, tids: u32) -> Self { + pub fn new(tid: u32, tids: u32, queue: bool) -> Self { TMem { tid, tids, @@ -553,7 +557,7 @@ impl TMem { vput: 0, nloc: vec![0; 0xFFF], // FIXME: move to a constant vloc: vec![0; 0xFFF], - rbag: RBag::new(), + rbag: RBag::new(queue), } } @@ -1044,10 +1048,6 @@ impl RBag { for (i, pair) in self.hi.iter().enumerate() { s.push_str(&format!("{:04X} | {} | {}\n", i, pair.get_fst().show(), pair.get_snd().show())); } - s.push_str("~~~~ | ~~~~~~~~~~~~ | ~~~~~~~~~~~~\n"); - for (i, pair) in self.lo.iter().enumerate() { - s.push_str(&format!("{:04X} | {} | {}\n", i + self.hi.len(), pair.get_fst().show(), pair.get_snd().show())); - } s.push_str("==== | ============ | ============\n"); return s; } @@ -1207,11 +1207,6 @@ impl<'a> GNet<'a> { s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i), book)); s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i), self.port2node_red(pair.get_snd(), i))); } - for (i, pair) in rbag.lo.iter().enumerate() { - s.push_str(&self.decorate(pair.get_fst(), self.port2node_red(pair.get_fst(), i + 100), book)); - s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i + 100), book)); - s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i + 100), self.port2node_red(pair.get_snd(), i + 100))); - } s.push_str("}\n"); for pair in rbag.hi.iter() { @@ -1219,11 +1214,6 @@ impl<'a> GNet<'a> { s.push_str(&self._dump(pair.get_snd(), book)); } - for pair in rbag.lo.iter() { - s.push_str(&self._dump(pair.get_fst(), book)); - s.push_str(&self._dump(pair.get_snd(), book)); - } - s.push_str("}\n"); return s; diff --git a/src/main.rs b/src/main.rs index dee219ab..6e81c749 100644 --- a/src/main.rs +++ b/src/main.rs @@ -162,7 +162,7 @@ pub fn run(book: &hvm::Book) { let net = hvm::GNet::new(1 << 29, 1 << 29); // Initializes threads - let mut tm = hvm::TMem::new(0, 1); + let mut tm = hvm::TMem::new(0, 1, false); // Creates an initial redex that calls main let main_id = book.defs.iter().position(|def| def.name == "main").unwrap(); From c9801c9b94175abc52a2f923e58d5c51f27f53a4 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Fri, 17 Jan 2025 12:24:03 +0100 Subject: [PATCH 10/16] Mark next evaluated redex --- src/hvm.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/hvm.rs b/src/hvm.rs index e7562acc..c12fb925 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -454,6 +454,18 @@ impl RBag { pub fn len(&self) -> usize { self.hi.len() } + + pub fn next_eval_idx(&self) -> Option { + if self.hi.len() == 0 { + return None; + } + + if self.queue { + Some(0) + } else { + Some(self.hi.len() - 1) + } + } } impl<'a> GNet<'a> { @@ -1201,8 +1213,22 @@ impl<'a> GNet<'a> { s.push_str("}\n"); s.push_str("{\n"); + s.push_str("edge [dir=both,arrowhead=inv,arrowtail=inv,color=darkviolet]; node [color=darkviolet];\n"); + { + let _i: Option = rbag.next_eval_idx(); + if _i.is_some() { + let i = _i.unwrap(); + let pair = &rbag.hi[i]; + s.push_str(&self.decorate(pair.get_fst(), self.port2node_red(pair.get_fst(), i), book)); + s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i), book)); + s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i), self.port2node_red(pair.get_snd(), i))); + } + } s.push_str("edge [dir=both,arrowhead=inv,arrowtail=inv,color=red]; node [color=red];\n"); for (i, pair) in rbag.hi.iter().enumerate() { + if i == rbag.next_eval_idx().unwrap() { + continue; + } s.push_str(&self.decorate(pair.get_fst(), self.port2node_red(pair.get_fst(), i), book)); s.push_str(&self.decorate(pair.get_snd(), self.port2node_red(pair.get_snd(), i), book)); s.push_str(&format!("{} -> {};\n", self.port2node_red(pair.get_fst(), i), self.port2node_red(pair.get_snd(), i))); From 8ee4ac787496b00689df064bc5b8f53f0198b64e Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Tue, 28 Jan 2025 17:30:16 +0100 Subject: [PATCH 11/16] Remove atomic counter --- src/hvm.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/hvm.rs b/src/hvm.rs index c12fb925..86001c91 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -1065,8 +1065,6 @@ impl RBag { } } -static mut unique: AtomicU32 = AtomicU32::new(0); - impl<'a> GNet<'a> { pub fn show(&self) -> String { let mut s = String::new(); @@ -1141,7 +1139,10 @@ impl<'a> GNet<'a> { } fn port2node(&self, port: Port) -> String { - return self._port2node(port, format!("unq{}", unsafe {unique.fetch_add(1, Ordering::Relaxed)})); + match port.get_tag() { + REF | ERA | VAR => panic!("No leaf nodes allowed"), + _ => self._port2node(port, "".to_string()), + } } fn decorate(&self, port: Port, id: String, book: &Book) -> String { From 7c0b19304225e2a8ab7c4436bc28127369b55775 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Tue, 28 Jan 2025 18:23:25 +0100 Subject: [PATCH 12/16] Evaluate in parallel for visualization and add para factor --- src/hvm.rs | 29 ++++++++++++++++++++++++++++- src/main.rs | 5 ++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/hvm.rs b/src/hvm.rs index 86001c91..d0354ca6 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -105,6 +105,7 @@ pub struct TMem { pub tids: u32, // thread count pub tick: u32, // tick counter pub itrs: u32, // interaction count + pub para: Option, // parallelization factor pub nput: usize, // next node allocation index pub vput: usize, // next vars allocation index pub nloc: Vec, // allocated node locations @@ -466,6 +467,18 @@ impl RBag { Some(self.hi.len() - 1) } } + + pub fn peek_gen(&self) -> Option { + if self.gen.len() == 0 { + return None; + } + + if self.queue { + Some(self.gen[0]) + } else { + Some(self.gen[self.gen.len() -1]) + } + } } impl<'a> GNet<'a> { @@ -565,6 +578,7 @@ impl TMem { tids, tick: 0, itrs: 0, + para: None, nput: 0, vput: 0, nloc: vec![0; 0xFFF], // FIXME: move to a constant @@ -925,6 +939,8 @@ impl TMem { pub fn evaluator(&mut self, net: &GNet, book: &Book) { let mut s = String::new(); + let mut cur_gen: u32 = 0; + let mut sum_redex_per_gen: usize = self.rbag.len(); // Increments the tick self.tick += 1; @@ -938,10 +954,16 @@ impl TMem { // Performs some interactions while self.rbag.len() > 0 { + let gen = self.rbag.peek_gen().unwrap(); + if gen != cur_gen { + s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); + cur_gen = gen; + sum_redex_per_gen += self.rbag.len(); + } + self.interact(net, book); // DEBUG: - s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); //println!("{}{}", self.rbag.show(), net.show()); //println!(""); //let rlen = self.rbag.lo.len() + self.rbag.hi.len(); @@ -963,6 +985,8 @@ impl TMem { } + s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); + s.push_str("]\n"); let _ = fs::write("dots.js", s); @@ -973,6 +997,9 @@ impl TMem { net.itrs.fetch_add(self.itrs as u64, Ordering::Relaxed); self.itrs = 0; + if self.rbag.queue { + self.para = Some(sum_redex_per_gen as f64 / (cur_gen + 2) as f64); + } } } diff --git a/src/main.rs b/src/main.rs index 6e81c749..3d7a5f71 100644 --- a/src/main.rs +++ b/src/main.rs @@ -162,7 +162,7 @@ pub fn run(book: &hvm::Book) { let net = hvm::GNet::new(1 << 29, 1 << 29); // Initializes threads - let mut tm = hvm::TMem::new(0, 1, false); + let mut tm = hvm::TMem::new(0, 1, true); // Creates an initial redex that calls main let main_id = book.defs.iter().position(|def| def.name == "main").unwrap(); @@ -193,4 +193,7 @@ pub fn run(book: &hvm::Book) { println!("- ITRS: {}", itrs); println!("- TIME: {:.2}s", duration.as_secs_f64()); println!("- MIPS: {:.2}", itrs as f64 / duration.as_secs_f64() / 1_000_000.0); + if tm.para.is_some() { + println!("- PARA: {:0.2}", tm.para.unwrap()); + } } From 9da77bf05a5c0ec12f201ba2985679ff9dfec951 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Tue, 28 Jan 2025 19:03:21 +0100 Subject: [PATCH 13/16] Add command line options for graph dumping and parallel evaluation --- src/hvm.rs | 37 ++++++++++++++++++++++++++++--------- src/main.rs | 16 ++++++++++++---- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/hvm.rs b/src/hvm.rs index d0354ca6..c99180c6 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -106,6 +106,7 @@ pub struct TMem { pub tick: u32, // tick counter pub itrs: u32, // interaction count pub para: Option, // parallelization factor + pub dump: Option, // graph dump output pub nput: usize, // next node allocation index pub vput: usize, // next vars allocation index pub nloc: Vec, // allocated node locations @@ -572,13 +573,14 @@ impl<'a> Drop for GNet<'a> { impl TMem { // TODO: implement a TMem::new() fn - pub fn new(tid: u32, tids: u32, queue: bool) -> Self { + pub fn new(tid: u32, tids: u32, queue: bool, dump: bool) -> Self { TMem { tid, tids, tick: 0, itrs: 0, para: None, + dump: if dump {Some(String::new())} else {None}, nput: 0, vput: 0, nloc: vec![0; 0xFFF], // FIXME: move to a constant @@ -937,8 +939,29 @@ impl TMem { } } + fn dump_pre(&mut self, net: &GNet, book: &Book) { + if let Some(ref mut s) = self.dump { + s.push_str("var dots = [\n"); + s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); + } + } + + fn dump_post(&mut self, net: &GNet, book: &Book) { + if let Some(ref mut s) = self.dump { + s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); + s.push_str("]\n"); + + let _ = fs::write("dots.js", s); + } + } + + fn dump(&mut self, net: &GNet, book: &Book) { + if let Some(ref mut s) = self.dump { + s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); + } + } + pub fn evaluator(&mut self, net: &GNet, book: &Book) { - let mut s = String::new(); let mut cur_gen: u32 = 0; let mut sum_redex_per_gen: usize = self.rbag.len(); @@ -949,14 +972,13 @@ impl TMem { //let mut max_rlen = 0; //let mut max_nlen = 0; //let mut max_vlen = 0; - s.push_str("var dots = [\n"); - s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); + self.dump_pre(net, book); // Performs some interactions while self.rbag.len() > 0 { let gen = self.rbag.peek_gen().unwrap(); if gen != cur_gen { - s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); + self.dump(net, book); cur_gen = gen; sum_redex_per_gen += self.rbag.len(); } @@ -985,10 +1007,7 @@ impl TMem { } - s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); - - s.push_str("]\n"); - let _ = fs::write("dots.js", s); + self.dump_post(net, book); // DEBUG: //println!("MAX_RLEN: {}", max_rlen); diff --git a/src/main.rs b/src/main.rs index 3d7a5f71..00b48d77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,15 @@ fn main() { .subcommand( Command::new("run") .about("Interprets a file (using Rust)") - .arg(Arg::new("file").required(true))) + .arg(Arg::new("file").required(true)) + .arg(Arg::new("dump") + .long("dump") + .action(ArgAction::SetTrue) + .help("Dump graph evaluation to dots.js")) + .arg(Arg::new("parallel") + .long("parallel") + .action(ArgAction::SetTrue) + .help("Act as if redexes are evaluated in parallel. Calculates a score of how parallel the graph evaluation is for the given program. Also useful for --dump."))) .subcommand( Command::new("run-c") .about("Interprets a file (using C)") @@ -69,7 +77,7 @@ fn main() { let file = sub_matches.get_one::("file").expect("required"); let code = fs::read_to_string(file).expect("Unable to read file"); let book = ast::Book::parse(&code).unwrap_or_else(|er| panic!("{}",er)).build(); - run(&book); + run(&book, *sub_matches.get_one::("parallel").unwrap(), *sub_matches.get_one::("dump").unwrap()); } Some(("run-c", sub_matches)) => { let file = sub_matches.get_one::("file").expect("required"); @@ -157,12 +165,12 @@ fn main() { } } -pub fn run(book: &hvm::Book) { +pub fn run(book: &hvm::Book, parallel: bool, dump: bool) { // Initializes the global net let net = hvm::GNet::new(1 << 29, 1 << 29); // Initializes threads - let mut tm = hvm::TMem::new(0, 1, true); + let mut tm = hvm::TMem::new(0, 1, parallel, dump); // Creates an initial redex that calls main let main_id = book.defs.iter().position(|def| def.name == "main").unwrap(); From cfc2821917133ba38fbe1b58d284ad67e7fe6a70 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Tue, 28 Jan 2025 20:54:35 +0100 Subject: [PATCH 14/16] Fix graph dump in non-parallel mode --- src/hvm.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/hvm.rs b/src/hvm.rs index c99180c6..fdd3fb66 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -469,15 +469,15 @@ impl RBag { } } - pub fn peek_gen(&self) -> Option { + pub fn next_is_new_gen(&self) -> bool { if self.gen.len() == 0 { - return None; + return false; } if self.queue { - Some(self.gen[0]) + return self.gen[0] == self.cur_gen; } else { - Some(self.gen[self.gen.len() -1]) + return true; } } } @@ -942,7 +942,6 @@ impl TMem { fn dump_pre(&mut self, net: &GNet, book: &Book) { if let Some(ref mut s) = self.dump { s.push_str("var dots = [\n"); - s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); } } @@ -962,8 +961,8 @@ impl TMem { } pub fn evaluator(&mut self, net: &GNet, book: &Book) { - let mut cur_gen: u32 = 0; - let mut sum_redex_per_gen: usize = self.rbag.len(); + let mut gen_count: u32 = 1; + let mut sum_redex_per_gen: u64 = self.rbag.len() as u64; // Increments the tick self.tick += 1; @@ -976,11 +975,10 @@ impl TMem { // Performs some interactions while self.rbag.len() > 0 { - let gen = self.rbag.peek_gen().unwrap(); - if gen != cur_gen { + if self.rbag.next_is_new_gen() { self.dump(net, book); - cur_gen = gen; - sum_redex_per_gen += self.rbag.len(); + gen_count += 1; + sum_redex_per_gen += self.rbag.len() as u64; } self.interact(net, book); @@ -1017,7 +1015,7 @@ impl TMem { net.itrs.fetch_add(self.itrs as u64, Ordering::Relaxed); self.itrs = 0; if self.rbag.queue { - self.para = Some(sum_redex_per_gen as f64 / (cur_gen + 2) as f64); + self.para = Some(sum_redex_per_gen as f64 / gen_count as f64); } } } From 39e2b556b4a9b8cc160adce28a5e7083b75a24e3 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Wed, 29 Jan 2025 15:07:43 +0100 Subject: [PATCH 15/16] Add documentation --- .gitignore | 1 + README.md | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/.gitignore b/.gitignore index 2b9560b3..350e2cab 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ examples/**/main examples/**/*.c examples/**/*.cu .out.hvm +dots.js # nix-direnv /.direnv/ diff --git a/README.md b/README.md index 4821d05e..36796910 100644 --- a/README.md +++ b/README.md @@ -72,3 +72,34 @@ don't worry, it isn't meant to. [Bend](https://github.com/HigherOrderCO/Bend) is the human-readable language and should be used both by end users and by languages aiming to target the HVM. If you're looking to learn more about the core syntax and tech, though, please check the [PAPER](./paper/HVM2.pdf). + +Interaction Net graph dumping and visualization +----------------------------------------------- + +With the rust interpreter (`hvm run`), every evaluation step can be dumped with +the `--dump` command line option. You can dump in sequential mode (only one +redex is evaluated in each step) or, with the `--parallel` option, +in parallel mode (all redexes are evaluated in each step). + +The dump will be written to the file `dots.js` in the current directory. + +You can view the graph dump with `index.html` as follows: + +Copy the file `dots.js` to the HVM directory. Then start a http server in the +HVM directory: + +```sh +cd HVM +python3 -m http.server +``` + +Now you can visit `http://127.0.0.1:8000` and step through the graph with +the wasd keys on your keyboard. + +How parallel is my Bend/HVM program? +------------------------------------ + +With the `--parallel` option, HVM also calculates the average number of regexes +in each step, thus giving a measurement of how parallel a given bend program is. + +You can use the `--parallel` option without the `--dump` option as well. \ No newline at end of file From a39ba1ad37e83de037433edf93be8de4941259f0 Mon Sep 17 00:00:00 2001 From: Lukas Straub Date: Mon, 7 Apr 2025 16:53:11 +0200 Subject: [PATCH 16/16] Add --profile functionality --- .gitignore | 1 + README.md | 20 +++++++------------- src/hvm.rs | 12 +++++++++++- src/main.rs | 10 +++++++--- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 350e2cab..0c710caa 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ examples/**/*.c examples/**/*.cu .out.hvm dots.js +profile.csv # nix-direnv /.direnv/ diff --git a/README.md b/README.md index 36796910..a4b0b7fb 100644 --- a/README.md +++ b/README.md @@ -83,18 +83,9 @@ in parallel mode (all redexes are evaluated in each step). The dump will be written to the file `dots.js` in the current directory. -You can view the graph dump with `index.html` as follows: - -Copy the file `dots.js` to the HVM directory. Then start a http server in the -HVM directory: - -```sh -cd HVM -python3 -m http.server -``` - -Now you can visit `http://127.0.0.1:8000` and step through the graph with -the wasd keys on your keyboard. +You can view the graph dump as follows: Copy the file `dots.js` to the HVM +directory, then open `index.html` in your browser. You can step through +the graph with the wasd keys. How parallel is my Bend/HVM program? ------------------------------------ @@ -102,4 +93,7 @@ How parallel is my Bend/HVM program? With the `--parallel` option, HVM also calculates the average number of regexes in each step, thus giving a measurement of how parallel a given bend program is. -You can use the `--parallel` option without the `--dump` option as well. \ No newline at end of file +You can use the `--parallel` option without the `--dump` option as well. + +With the `--profile` option, a parallelization profile will be written to +`profile.csv`. This file contains the number of regexes per step. \ No newline at end of file diff --git a/src/hvm.rs b/src/hvm.rs index fdd3fb66..125f9c12 100644 --- a/src/hvm.rs +++ b/src/hvm.rs @@ -107,6 +107,7 @@ pub struct TMem { pub itrs: u32, // interaction count pub para: Option, // parallelization factor pub dump: Option, // graph dump output + pub profile: Option, // parallelization profile pub nput: usize, // next node allocation index pub vput: usize, // next vars allocation index pub nloc: Vec, // allocated node locations @@ -573,7 +574,7 @@ impl<'a> Drop for GNet<'a> { impl TMem { // TODO: implement a TMem::new() fn - pub fn new(tid: u32, tids: u32, queue: bool, dump: bool) -> Self { + pub fn new(tid: u32, tids: u32, queue: bool, dump: bool, profile: bool) -> Self { TMem { tid, tids, @@ -581,6 +582,7 @@ impl TMem { itrs: 0, para: None, dump: if dump {Some(String::new())} else {None}, + profile: if profile {Some(String::new())} else {None}, nput: 0, vput: 0, nloc: vec![0; 0xFFF], // FIXME: move to a constant @@ -952,12 +954,20 @@ impl TMem { let _ = fs::write("dots.js", s); } + + if let Some(ref mut s) = self.profile { + let _ = fs::write("profile.csv", s); + } } fn dump(&mut self, net: &GNet, book: &Book) { if let Some(ref mut s) = self.dump { s.push_str(&format!("`{}`,\n", net.dump(&self.rbag, book))); } + + if let Some(ref mut s) = self.profile { + s.push_str(&format!("{}\n", self.rbag.len())); + } } pub fn evaluator(&mut self, net: &GNet, book: &Book) { diff --git a/src/main.rs b/src/main.rs index 00b48d77..67bca468 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,10 @@ fn main() { Command::new("run") .about("Interprets a file (using Rust)") .arg(Arg::new("file").required(true)) + .arg(Arg::new("profile") + .long("profile") + .action(ArgAction::SetTrue) + .help("Write parallelization profile to profile.csv")) .arg(Arg::new("dump") .long("dump") .action(ArgAction::SetTrue) @@ -77,7 +81,7 @@ fn main() { let file = sub_matches.get_one::("file").expect("required"); let code = fs::read_to_string(file).expect("Unable to read file"); let book = ast::Book::parse(&code).unwrap_or_else(|er| panic!("{}",er)).build(); - run(&book, *sub_matches.get_one::("parallel").unwrap(), *sub_matches.get_one::("dump").unwrap()); + run(&book, *sub_matches.get_one::("parallel").unwrap(), *sub_matches.get_one::("parallel").unwrap(), *sub_matches.get_one::("dump").unwrap()); } Some(("run-c", sub_matches)) => { let file = sub_matches.get_one::("file").expect("required"); @@ -165,12 +169,12 @@ fn main() { } } -pub fn run(book: &hvm::Book, parallel: bool, dump: bool) { +pub fn run(book: &hvm::Book, profile: bool, parallel: bool, dump: bool) { // Initializes the global net let net = hvm::GNet::new(1 << 29, 1 << 29); // Initializes threads - let mut tm = hvm::TMem::new(0, 1, parallel, dump); + let mut tm = hvm::TMem::new(0, 1, parallel, dump, profile); // Creates an initial redex that calls main let main_id = book.defs.iter().position(|def| def.name == "main").unwrap();