summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ocaml/lib/nock.ml3
-rw-r--r--ocaml/lib/noun.ml54
-rw-r--r--ocaml/test/test_two_stage_boot.ml17
3 files changed, 67 insertions, 7 deletions
diff --git a/ocaml/lib/nock.ml b/ocaml/lib/nock.ml
index 2f52922..ac6a2ad 100644
--- a/ocaml/lib/nock.ml
+++ b/ocaml/lib/nock.ml
@@ -28,7 +28,8 @@ open Noun
let call_count = ref 0
let depth = ref 0
let max_calls = 100
-let max_mug_depth = -1 (* Disabled: need mug caching like C before enabling *)
+let max_mug_depth = -1 (* Disabled: even cached, initial mug of huge ivory pill is slow *)
+(* TODO: Enable selectively after verifying structural correctness *)
(* Helper to generate indentation based on depth *)
let indent () =
diff --git a/ocaml/lib/noun.ml b/ocaml/lib/noun.ml
index f706dca..b87aff5 100644
--- a/ocaml/lib/noun.ml
+++ b/ocaml/lib/noun.ml
@@ -86,15 +86,63 @@ let inc = function
| Atom n -> Atom (Z.succ n)
| Cell _ -> raise Exit
-(** Compute mug (31-bit hash) of a noun
+(** Mug cache using ephemeron (weak key-value table)
+
+ This implements mug caching like C's u3 system:
+ - Uses physical equality (==) for lookups
+ - Weak keys allow GC to collect unused nouns
+ - Values (mugs) are int32, no GC needed
+ - Dramatically improves performance for repeated mug calls
+*)
+module MugCache = struct
+ (* Ephemeron table: weak keys, strong values *)
+ module E = Ephemeron.K1.Make(struct
+ type t = noun
+ let equal = (==) (* Physical equality - same noun object *)
+ let hash = Hashtbl.hash (* OCaml's built-in structural hash *)
+ end)
+
+ let cache : int32 E.t = E.create 10000 (* Initial size *)
+ let hit_count = ref 0
+ let miss_count = ref 0
+
+ let find noun =
+ match E.find_opt cache noun with
+ | Some mug ->
+ hit_count := !hit_count + 1;
+ Some mug
+ | None ->
+ miss_count := !miss_count + 1;
+ None
+
+ let add noun mug = E.add cache noun mug
+
+ let stats () =
+ let total = !hit_count + !miss_count in
+ if total > 0 then
+ Printf.eprintf "[MugCache] hits=%d misses=%d (%.1f%% hit rate)\n%!"
+ !hit_count !miss_count
+ (100.0 *. float_of_int !hit_count /. float_of_int total)
+end
+
+(** Compute mug (31-bit hash) of a noun with caching
This implements Urbit's mug hash function using FNV-1a.
- The mug is cached in the C implementation but we compute it fresh each time.
+ Mugs are cached to avoid recomputation (critical for performance).
For atoms: hash the bytes of the integer representation
For cells: mix the mugs of head and tail
*)
let rec mug noun =
+ (* Check cache first (MugCache.find updates hit/miss counts) *)
+ match MugCache.find noun with
+ | Some cached_mug -> cached_mug
+ | None ->
+ let computed_mug = compute_mug_uncached noun in
+ MugCache.add noun computed_mug;
+ computed_mug
+
+and compute_mug_uncached noun =
(* FNV-1a constants - using hex to avoid signed int32 overflow *)
let fnv_prime = 16777619l in
let fnv_basis = 0x811c9dc5l in (* 2166136261 in decimal *)
@@ -129,7 +177,7 @@ let rec mug noun =
hash_bytes (Bytes.of_string bytes)
| Cell (h, t) ->
- (* Mix the mugs of head and tail *)
+ (* Mix the mugs of head and tail (recursively cached) *)
let h_mug = mug h in
let t_mug = mug t in
mix_mugs h_mug t_mug
diff --git a/ocaml/test/test_two_stage_boot.ml b/ocaml/test/test_two_stage_boot.ml
index da4f17b..62d262d 100644
--- a/ocaml/test/test_two_stage_boot.ml
+++ b/ocaml/test/test_two_stage_boot.ml
@@ -86,14 +86,25 @@ let stage1_ivory_boot env =
(if Noun.is_cell poke_battery then "cell ✓" else "atom ✗")
end;
+ (* Compute mugs of small sub-structures for verification *)
+ Printf.printf " Computing mugs of sub-structures:\n";
+ let slot2_mug = Noun.mug slot2 in
+ let slot3_mug = Noun.mug slot3 in
+ let poke_mug = Noun.mug poke in
+ Printf.printf " Slot 2 mug: 0x%lx\n" slot2_mug;
+ Printf.printf " Slot 3 mug: 0x%lx\n" slot3_mug;
+ Printf.printf " Poke mug: 0x%lx\n" poke_mug;
+
Printf.printf "\n";
Printf.printf "╔═══════════════════════════════════════╗\n";
Printf.printf "║ ✓ STAGE 1 COMPLETE! ║\n";
Printf.printf "╚═══════════════════════════════════════╝\n\n";
- Printf.printf "⚠️ NOTE: Structural checks passed, but cannot verify\n";
- Printf.printf " kernels are identical without mug comparison.\n";
- Printf.printf " Need to implement mug caching first!\n\n";
+ Printf.printf "⚠️ To verify correctness, compare these mugs with C:\n";
+ Printf.printf " Run the C test and check if mugs match!\n\n";
+
+ (* Print cache stats *)
+ Noun.MugCache.stats ();
Some kernel