Files
coolguyandClaude Opus 5 c4662ab627 bench: 증분 루프를 측정으로 증명한다
100k줄 200모듈에서 본문 수정은 downstream을 한 칸도 건드리지 않고(재검사
1개, 0.8ms), 시그니처 수정은 hash가 변한 곳까지만 전파되어(재검사 2개,
2.1ms) downstream 소진성 위반을 실제로 잡는다.

"빠르다"가 아니라 "다시 볼 것이 적다"가 주장이므로, 시간보다 재검사된
모듈 수를 먼저 출력한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 14:12:35 +09:00

129 lines
4.4 KiB
OCaml

(* 빠른 검증 루프는 주장이 아니라 측정이다.
측정하는 것은 두 가지다:
1. 전체 검사 시간 (cold) — 규모가 커져도 파국이 아닌가
2. 증분 재검사 시간 (warm) — 그리고 무엇이 재검사되었는가
두 번째가 본체다. 아키텍처의 주장은 "빠르다"가 아니라 "다시 볼 것이
적다"이고, 그것은 시간이 아니라 재검사된 모듈 수로 먼저 증명된다.
시간은 그 수가 옳다는 것의 따름 결과다. *)
open Coollang
let modules = 200
let fns_per_module = 68
let now () = Unix.gettimeofday ()
let find_sub hay needle =
let n = String.length needle and h = String.length hay in
let rec go i =
if i + n > h then failwith "not found"
else if String.sub hay i n = needle then i
else go (i + 1)
in
go 0
let write file s =
let oc = open_out_bin file in
output_string oc s;
close_out oc
(* i번 모듈은 i-1번 모듈을 가져온다. 사슬이므로 시그니처 변경은 끝까지
전파되어야 하고, 본문 변경은 한 칸도 가면 안 된다. *)
let gen_module i ~body =
let b = Buffer.create 8192 in
if i > 0 then
Buffer.add_string b (Printf.sprintf "import \"m%d\" as Up\n\n" (i - 1));
Buffer.add_string b "pub enum Shape {\n Circle(Int),\n Square(Int),\n}\n\n";
Buffer.add_string b "pub copyable struct Point {\n x: Int,\n y: Int,\n}\n\n";
Buffer.add_string b
(Printf.sprintf
"pub fn area(s: Shape) -> Int {\n\
\ match s {\n\
\ Circle(r) => r * r,\n\
\ Square(w) => w * w,\n\
\ }\n\
}\n\n");
if i > 0 then
Buffer.add_string b
"pub fn up_area(s: Up.Shape) -> Int {\n\
\ match s {\n\
\ Up.Circle(r) => Up.area(s),\n\
\ Up.Square(w) => w * w,\n\
\ }\n\
}\n\n";
for k = 0 to fns_per_module - 1 do
Buffer.add_string b
(Printf.sprintf
"pub fn f%d(p: Point, n: Int) -> Int {\n\
\ let a = p.x + n\n\
\ let b = p.y * %d\n\
\ let c = if a > b { a } else { b }\n\
\ %s\n\
}\n\n"
k (k + 1) body)
done;
Buffer.contents b
let count_lines s =
String.fold_left (fun n c -> if c = '\n' then n + 1 else n) 0 s
let ms t = Printf.sprintf "%.1fms" (t *. 1000.)
let () =
let dir = Filename.concat (Filename.get_temp_dir_name ()) "cool_bench" in
ignore
(Sys.command
(Printf.sprintf "rm -rf %s && mkdir -p %s" (Filename.quote dir)
(Filename.quote dir)));
let path i = Filename.concat dir (Printf.sprintf "m%d.cool" i) in
let lines = ref 0 in
for i = 0 to modules - 1 do
let s = gen_module i ~body:"a + c" in
lines := !lines + count_lines s;
write (path i) s
done;
Printf.printf "모듈 %d개, %d줄 생성\n" modules !lines;
(* 1. cold: 전체 그래프 로드와 검사 *)
let st = Session.create ~root:dir () in
let t0 = now () in
Session.load st (path (modules - 1));
let cold = now () -. t0 in
let errs = Session.errors st in
Printf.printf "cold 전체 검사 %s (오류 %d건)\n" (ms cold) (List.length errs);
if errs <> [] then
List.iter
(fun e -> prerr_endline (Session.string_of_error e))
(List.filteri (fun i _ -> i < 5) errs);
(* 2. warm: 사슬 한가운데 모듈의 본문만 수정 *)
let mid = modules / 2 in
write (path mid) (gen_module mid ~body:"a + c + 1");
let t0 = now () in
let touched = Session.recheck st [ path mid ] in
let warm_body = now () -. t0 in
Printf.printf "warm 본문만 수정 %s (재검사 %d개, 오류 %d건)\n" (ms warm_body)
(List.length touched)
(List.length (Session.errors st));
(* 3. warm: 같은 모듈의 시그니처 수정 — variant 추가 *)
let sig_src =
let s = gen_module mid ~body:"a + c + 1" in
let needle = " Square(Int),\n" in
let i = find_sub s needle in
String.sub s 0 (i + String.length needle)
^ " Tri(Int),\n"
^ String.sub s
(i + String.length needle)
(String.length s - i - String.length needle)
in
write (path mid) sig_src;
let t0 = now () in
let touched = Session.recheck st [ path mid ] in
let warm_sig = now () -. t0 in
let errs = Session.errors st in
Printf.printf "warm 시그니처 수정 %s (재검사 %d개, 오류 %d건)\n" (ms warm_sig)
(List.length touched) (List.length errs);
List.iter (fun e -> print_endline (" " ^ Session.string_of_error e)) errs