From 96e461a122cff410909361f9cfa3cd918018d91a Mon Sep 17 00:00:00 2001 From: jackbackrack Date: Fri, 3 Jan 2025 13:26:12 -0800 Subject: [PATCH 1/5] collect bytes and uses from heap and print out sorted results --- compiler/codegen.stanza | 3 ++ compiler/stitcher.stanza | 14 +++++++ compiler/vm-structures.stanza | 1 + core/core.stanza | 76 +++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+) diff --git a/compiler/codegen.stanza b/compiler/codegen.stanza index 96c140bc..da9a5375 100644 --- a/compiler/codegen.stanza +++ b/compiler/codegen.stanza @@ -72,6 +72,7 @@ public defstruct AsmStubs : collect-garbage:Int saved-regs:Tuple saved-fregs:Tuple + heap-statistics:Int class-table:Int global-root-table:Int stackmap-table:Int @@ -128,6 +129,7 @@ public defn AsmStubs (backend:Backend) : next(id-counter) ;collect-garbage:Int saved-regs ;saved-regs:Tuple saved-fregs ;saved-fregs:Tuple + next(id-counter) ;heap-statistics:Int next(id-counter) ;class-table:Int next(id-counter) ;global-root-table:Int next(id-counter) ;stackmap-table:Int @@ -334,6 +336,7 @@ public defn compile-entry-function (emitter:CodeEmitter, stubs:AsmStubs) : #label(safepoint-table) ;safepoint-table:ptr #label(debug-table) ;debug-table:ptr #label(local-var-table) ;local-var-table:ptr + #label(heap-statistics) ;heap-statistics:ptr #label(class-table) ;class-table:ptr #label(global-root-table) ;global-root-table:ptr #label(stackmap-table) ;stackmap-table:ptr diff --git a/compiler/stitcher.stanza b/compiler/stitcher.stanza index 0c8bf64a..d14d6505 100644 --- a/compiler/stitcher.stanza +++ b/compiler/stitcher.stanza @@ -856,6 +856,19 @@ public defn Stitcher (packages:Collection, bindings:Bindings|False, s E $ DefText() E $ Comment("End of Data Tables") + defn emit-heap-statistics-table (code-emitter:CodeEmitter) : + defn E (i:Ins) : emit(code-emitter, i) + E $ Comment("Heap Statistics Table") + E $ DefData() + E $ Label(/heap-statistics(stubs)) + for i in 0 to num-concrete-classes do : + E $ DefLong(0L) ; num-uses + E $ DefLong(0L) ; num-bytes + E $ DefLong(to-long(num-concrete-classes)) + E $ DefLong(-1L) ; mark end of table + E $ DefText() + E $ Comment("End of Heap Statistics Table") + ;Emit class table defn emit-class-table (code-emitter:CodeEmitter) : defn E (i:Ins) : emit(code-emitter, i) @@ -995,6 +1008,7 @@ public defn Stitcher (packages:Collection, bindings:Bindings|False, s ; so they can be dynamically imported by other executables if generate-export-directives-table?(backend(stubs)) : emit-export-table(code-emitter) + emit-heap-statistics-table(code-emitter) defmethod stubs (this) : stubs defmethod core-fn (this, id:FnId) : diff --git a/compiler/vm-structures.stanza b/compiler/vm-structures.stanza index 3bad507f..746da736 100644 --- a/compiler/vm-structures.stanza +++ b/compiler/vm-structures.stanza @@ -31,6 +31,7 @@ public lostanza deftype VMState : var safepoint-table: ptr ;(Permanent State) var debug-table: ptr ;(Permanent State) var local-var-table: ptr ;(Permanent State) + var heap-statistics: ptr ;(Permanent State) var class-table: ptr ;(Permanent State) ;Interpreted Mode Tables var instructions: ptr ;(Permanent State) diff --git a/core/core.stanza b/core/core.stanza index a2a602ac..38aeb6c4 100644 --- a/core/core.stanza +++ b/core/core.stanza @@ -209,6 +209,10 @@ protected lostanza deftype ArrayRecord : num-item-roots:int roots:int ... +protected lostanza deftype HeapStatistics : + var num-uses:long + var num-bytes:long + ;The first fields in VMState are used by the core library ;in both compiled and interpreted mode. The last fields ;are used only in compiled mode. @@ -232,6 +236,7 @@ protected lostanza deftype VMState : safepoint-table: ptr ;(Variable State) debug-table: ptr ;(Variable State) local-var-table: ptr ;(Variable State) + heap-statistics: ptr ;(Variable State) ;Compiled Mode Tables class-table: ptr global-root-table: ptr @@ -2776,6 +2781,77 @@ public lostanza defn min (x:long, y:long) -> long : if x < y : return x else : return y +;============================================================ +;===================== Heap Analyzer ======================== +;============================================================ + +lostanza defn num-concrete-classes (vms:ptr) -> int : + labels: + begin: goto loop(0) + loop (i:int) : + val stat = vms.heap-statistics[i] + if stat.num-bytes < 0L : + return stat.num-uses as int + else : + goto loop(i + 1) + +public defstruct HeapStat : + tag : Int + name : String + num-uses : Long + num-bytes : Long +with: + printer => true + +public defn analyze-heap () -> Long : + val counts = Vector() + val size = do-analyze-heap(counts) + val res = reverse(to-list(lazy-qsort(num-bytes, counts))) + println("Heap size %_" % [size]) + var max-bytes-size = reduce(max, length("Size"), seq({ length(to-string(num-bytes(_))) }, res)) + var max-uses-size = reduce(max, length("Uses"), seq({ length(to-string(num-uses(_))) }, res)) + defn pad (s:String, n:Int) -> String : append-all(cat(repeatedly({ " " }, (n - length(s))), [s])) + println(" %_ %_: %_" % [pad("Size", max-bytes-size), pad("Use", max-uses-size), "Type"]) + for hc in res do : + println(" %_ %_: %_" % [pad(to-string(num-bytes(hc)), max-bytes-size), pad(to-string(num-uses(hc)), max-uses-size), name(hc)]) + size + +lostanza defn do-analyze-heap (counts:ref>) -> ref : + val vms:ptr = call-prim flush-vm() + run-garbage-collector() + val num-classes = num-concrete-classes(vms) + clear(vms.heap-statistics, num-classes * sizeof(HeapStatistics)) + val hsize = do-analyze-heap(vms.heap.start, vms.heap.old-objects-end, vms) + val nursery = nursery-start(addr(vms.heap)) + val nsize = do-analyze-heap(nursery, vms.heap.top, vms) + for (var i:int = 0, i < num-classes, i = i + 1) : + val heap-stat = vms.heap-statistics[i] + val num-uses = heap-stat.num-uses + if num-uses > 0L : + add(counts, HeapStat(new Int{i as int}, String(class-name(i)), new Long{num-uses}, new Long{heap-stat.num-bytes})) + return new Long{ hsize + nsize } + +lostanza defn do-analyze-heap (pstart:ptr, pend:ptr, vms:ptr) -> long : + var p:ptr = pstart + while p < pend : + val tag = [p] as int + val class = vms.class-table[tag].record + var size:long = 0L + if class.item-size == 0 : + size = object-size-on-heap(class.size) + else : + val class = class as ptr + val array = p as ptr + val len = array.slots[0] + val base-size = class.base-size + val item-size = class.item-size + val my-size = base-size + item-size * len + size = object-size-on-heap(my-size) + p = p + size + vms.heap-statistics[tag].num-uses = vms.heap-statistics[tag].num-uses + 1L + vms.heap-statistics[tag].num-bytes = vms.heap-statistics[tag].num-bytes + size + return (pend as long) - (pstart as long) + ;============================================================ ;===================== Debugging ============================ ;============================================================ From bade4c0c194e9366fe227feb27affda5c07228c2 Mon Sep 17 00:00:00 2001 From: jackbackrack Date: Sun, 5 Jan 2025 20:20:29 -0800 Subject: [PATCH 2/5] doc, improve naming, improve stat report --- core/core.stanza | 66 +++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/core/core.stanza b/core/core.stanza index 38aeb6c4..8b34af70 100644 --- a/core/core.stanza +++ b/core/core.stanza @@ -209,7 +209,7 @@ protected lostanza deftype ArrayRecord : num-item-roots:int roots:int ... -protected lostanza deftype HeapStatistics : +protected lostanza deftype HeapStatistic : var num-uses:long var num-bytes:long @@ -236,7 +236,7 @@ protected lostanza deftype VMState : safepoint-table: ptr ;(Variable State) debug-table: ptr ;(Variable State) local-var-table: ptr ;(Variable State) - heap-statistics: ptr ;(Variable State) + heap-statistics: ptr ;(Variable State) ;Compiled Mode Tables class-table: ptr global-root-table: ptr @@ -2785,42 +2785,32 @@ public lostanza defn min (x:long, y:long) -> long : ;===================== Heap Analyzer ======================== ;============================================================ -lostanza defn num-concrete-classes (vms:ptr) -> int : +public defstruct HeapStat : + tag : Int ; type + name : String ; name of type + num-uses : Long ; num references in heap + num-bytes : Long ; num bytes occupied in heap +with: + printer => true + +; clear out statistics for each concrete class +lostanza defn clear-heap-statistics (vms:ptr) -> int : labels: begin: goto loop(0) loop (i:int) : val stat = vms.heap-statistics[i] - if stat.num-bytes < 0L : + if stat.num-bytes < 0L : ; sentinel return stat.num-uses as int else : + vms.heap-statistics[i].num-bytes = 0 + vms.heap-statistics[i].num-uses = 0 goto loop(i + 1) -public defstruct HeapStat : - tag : Int - name : String - num-uses : Long - num-bytes : Long -with: - printer => true - -public defn analyze-heap () -> Long : - val counts = Vector() - val size = do-analyze-heap(counts) - val res = reverse(to-list(lazy-qsort(num-bytes, counts))) - println("Heap size %_" % [size]) - var max-bytes-size = reduce(max, length("Size"), seq({ length(to-string(num-bytes(_))) }, res)) - var max-uses-size = reduce(max, length("Uses"), seq({ length(to-string(num-uses(_))) }, res)) - defn pad (s:String, n:Int) -> String : append-all(cat(repeatedly({ " " }, (n - length(s))), [s])) - println(" %_ %_: %_" % [pad("Size", max-bytes-size), pad("Use", max-uses-size), "Type"]) - for hc in res do : - println(" %_ %_: %_" % [pad(to-string(num-bytes(hc)), max-bytes-size), pad(to-string(num-uses(hc)), max-uses-size), name(hc)]) - size - -lostanza defn do-analyze-heap (counts:ref>) -> ref : +; run gc, collect heap stats while walking each object in nursery and heap +lostanza defn do-analyze-heap (stats:ref>) -> ref : val vms:ptr = call-prim flush-vm() run-garbage-collector() - val num-classes = num-concrete-classes(vms) - clear(vms.heap-statistics, num-classes * sizeof(HeapStatistics)) + val num-classes = clear-heap-statistics(vms) val hsize = do-analyze-heap(vms.heap.start, vms.heap.old-objects-end, vms) val nursery = nursery-start(addr(vms.heap)) val nsize = do-analyze-heap(nursery, vms.heap.top, vms) @@ -2828,9 +2818,10 @@ lostanza defn do-analyze-heap (counts:ref>) -> ref : val heap-stat = vms.heap-statistics[i] val num-uses = heap-stat.num-uses if num-uses > 0L : - add(counts, HeapStat(new Int{i as int}, String(class-name(i)), new Long{num-uses}, new Long{heap-stat.num-bytes})) + add(stats, HeapStat(new Int{i as int}, String(class-name(i)), new Long{num-uses}, new Long{heap-stat.num-bytes})) return new Long{ hsize + nsize } +; collect heap stats while walking each object in consecutive range of memory lostanza defn do-analyze-heap (pstart:ptr, pend:ptr, vms:ptr) -> long : var p:ptr = pstart while p < pend : @@ -2852,6 +2843,23 @@ lostanza defn do-analyze-heap (pstart:ptr, pend:ptr, vms:ptr Long : + val stats = Vector() + val size = do-analyze-heap(stats) + val res = reverse(to-list(lazy-qsort(num-bytes, stats))) + println("Heap size %_" % [size]) + var max-bytes-size = reduce(max, length("Size"), seq({ length(to-string(num-bytes(_))) }, res)) + var max-perc-size = 3 + var max-uses-size = reduce(max, length("Uses"), seq({ length(to-string(num-uses(_))) }, res)) + defn pad (s:String, n:Int) -> String : append-all(cat(repeatedly({ " " }, (n - length(s))), [s])) + println(" %_ %_ %_: %_" % [pad("Size", max-bytes-size), "Perc", pad("Use", max-uses-size), "Type"]) + for hc in res do : + val p = to-int(to-double(num-bytes(hc)) * 100.0 / to-double(size)) + println(" %_ %_%% %_: %_" % [ + pad(to-string(num-bytes(hc)), max-bytes-size), pad(to-string(p), max-perc-size), pad(to-string(num-uses(hc)), max-uses-size), name(hc)]) + size + ;============================================================ ;===================== Debugging ============================ ;============================================================ From 5a1c652f6ec4955d08338e1ddd7c7db40a62857c Mon Sep 17 00:00:00 2001 From: jackbackrack Date: Mon, 6 Jan 2025 10:59:05 -0800 Subject: [PATCH 3/5] bootstrap changes to core --- ci/build-stanza-version.txt | 2 +- core/core.stanza | 232 +++++++++++++++++++++--------------- 2 files changed, 135 insertions(+), 99 deletions(-) diff --git a/ci/build-stanza-version.txt b/ci/build-stanza-version.txt index 71434844..bf293833 100644 --- a/ci/build-stanza-version.txt +++ b/ci/build-stanza-version.txt @@ -7,4 +7,4 @@ # like 1.23.45 # # Use version 0.17.56 to compile 0.18.0 -0.18.96 +0.18.97 diff --git a/core/core.stanza b/core/core.stanza index 8b34af70..ab4ad441 100644 --- a/core/core.stanza +++ b/core/core.stanza @@ -218,32 +218,62 @@ protected lostanza deftype HeapStatistic : ;are used only in compiled mode. ;Permanent state changes in-between each code load. ;Variable state changes in-between each boundary change. -protected lostanza deftype VMState : - ;Compiled and Interpreted Mode - global-offsets: ptr ;(Permanent State) - global-mem: ptr ;(Permanent State) - var sig-handler: long ;(Permanent State) - var current-coroutine-ptr: ptr ;[TODO] Change to long to represent reference. - var stepping-coroutine-ptr: ptr ;[TODO] Change to long to represent reference. - const-table: ptr ;(Permanent State) - const-mem: ptr ;(Permanent State) - data-offsets: ptr ;(Permanent State) - data-mem: ptr ;(Permanent State) - code-offsets: ptr ;(Permanent State) - registers: ptr ;(Permanent State) - system-registers: ptr ;(Permanent State) - var heap: Heap ;(Variable State) - safepoint-table: ptr ;(Variable State) - debug-table: ptr ;(Variable State) - local-var-table: ptr ;(Variable State) - heap-statistics: ptr ;(Variable State) - ;Compiled Mode Tables - class-table: ptr - global-root-table: ptr - stackmap-table: ptr> - stack-trace-table: ptr - extern-table: ptr - extern-defn-table: ptr +#if-defined(BOOTSTRAP) : + + protected lostanza deftype VMState : + ;Compiled and Interpreted Mode + global-offsets: ptr ;(Permanent State) + global-mem: ptr ;(Permanent State) + var sig-handler: long ;(Permanent State) + var current-coroutine-ptr: ptr ;[TODO] Change to long to represent reference. + var stepping-coroutine-ptr: ptr ;[TODO] Change to long to represent reference. + const-table: ptr ;(Permanent State) + const-mem: ptr ;(Permanent State) + data-offsets: ptr ;(Permanent State) + data-mem: ptr ;(Permanent State) + code-offsets: ptr ;(Permanent State) + registers: ptr ;(Permanent State) + system-registers: ptr ;(Permanent State) + var heap: Heap ;(Variable State) + safepoint-table: ptr ;(Variable State) + debug-table: ptr ;(Variable State) + local-var-table: ptr ;(Variable State) + ;Compiled Mode Tables + class-table: ptr + global-root-table: ptr + stackmap-table: ptr> + stack-trace-table: ptr + extern-table: ptr + extern-defn-table: ptr + +#else: + + protected lostanza deftype VMState : + ;Compiled and Interpreted Mode + global-offsets: ptr ;(Permanent State) + global-mem: ptr ;(Permanent State) + var sig-handler: long ;(Permanent State) + var current-coroutine-ptr: ptr ;[TODO] Change to long to represent reference. + var stepping-coroutine-ptr: ptr ;[TODO] Change to long to represent reference. + const-table: ptr ;(Permanent State) + const-mem: ptr ;(Permanent State) + data-offsets: ptr ;(Permanent State) + data-mem: ptr ;(Permanent State) + code-offsets: ptr ;(Permanent State) + registers: ptr ;(Permanent State) + system-registers: ptr ;(Permanent State) + var heap: Heap ;(Variable State) + safepoint-table: ptr ;(Variable State) + debug-table: ptr ;(Variable State) + local-var-table: ptr ;(Variable State) + heap-statistics: ptr ;(Variable State) + ;Compiled Mode Tables + class-table: ptr + global-root-table: ptr + stackmap-table: ptr> + stack-trace-table: ptr + extern-table: ptr + extern-defn-table: ptr lostanza deftype ExternTable : length: long @@ -2785,80 +2815,86 @@ public lostanza defn min (x:long, y:long) -> long : ;===================== Heap Analyzer ======================== ;============================================================ -public defstruct HeapStat : - tag : Int ; type - name : String ; name of type - num-uses : Long ; num references in heap - num-bytes : Long ; num bytes occupied in heap -with: - printer => true +#if-defined(BOOTSTRAP) : -; clear out statistics for each concrete class -lostanza defn clear-heap-statistics (vms:ptr) -> int : - labels: - begin: goto loop(0) - loop (i:int) : - val stat = vms.heap-statistics[i] - if stat.num-bytes < 0L : ; sentinel - return stat.num-uses as int - else : - vms.heap-statistics[i].num-bytes = 0 - vms.heap-statistics[i].num-uses = 0 - goto loop(i + 1) + public defn analyze-heap () -> Long : 0L -; run gc, collect heap stats while walking each object in nursery and heap -lostanza defn do-analyze-heap (stats:ref>) -> ref : - val vms:ptr = call-prim flush-vm() - run-garbage-collector() - val num-classes = clear-heap-statistics(vms) - val hsize = do-analyze-heap(vms.heap.start, vms.heap.old-objects-end, vms) - val nursery = nursery-start(addr(vms.heap)) - val nsize = do-analyze-heap(nursery, vms.heap.top, vms) - for (var i:int = 0, i < num-classes, i = i + 1) : - val heap-stat = vms.heap-statistics[i] - val num-uses = heap-stat.num-uses - if num-uses > 0L : - add(stats, HeapStat(new Int{i as int}, String(class-name(i)), new Long{num-uses}, new Long{heap-stat.num-bytes})) - return new Long{ hsize + nsize } - -; collect heap stats while walking each object in consecutive range of memory -lostanza defn do-analyze-heap (pstart:ptr, pend:ptr, vms:ptr) -> long : - var p:ptr = pstart - while p < pend : - val tag = [p] as int - val class = vms.class-table[tag].record - var size:long = 0L - if class.item-size == 0 : - size = object-size-on-heap(class.size) - else : - val class = class as ptr - val array = p as ptr - val len = array.slots[0] - val base-size = class.base-size - val item-size = class.item-size - val my-size = base-size + item-size * len - size = object-size-on-heap(my-size) - p = p + size - vms.heap-statistics[tag].num-uses = vms.heap-statistics[tag].num-uses + 1L - vms.heap-statistics[tag].num-bytes = vms.heap-statistics[tag].num-bytes + size - return (pend as long) - (pstart as long) - -; public interface to heap analyzer collecting and printing out stats -public defn analyze-heap () -> Long : - val stats = Vector() - val size = do-analyze-heap(stats) - val res = reverse(to-list(lazy-qsort(num-bytes, stats))) - println("Heap size %_" % [size]) - var max-bytes-size = reduce(max, length("Size"), seq({ length(to-string(num-bytes(_))) }, res)) - var max-perc-size = 3 - var max-uses-size = reduce(max, length("Uses"), seq({ length(to-string(num-uses(_))) }, res)) - defn pad (s:String, n:Int) -> String : append-all(cat(repeatedly({ " " }, (n - length(s))), [s])) - println(" %_ %_ %_: %_" % [pad("Size", max-bytes-size), "Perc", pad("Use", max-uses-size), "Type"]) - for hc in res do : - val p = to-int(to-double(num-bytes(hc)) * 100.0 / to-double(size)) - println(" %_ %_%% %_: %_" % [ - pad(to-string(num-bytes(hc)), max-bytes-size), pad(to-string(p), max-perc-size), pad(to-string(num-uses(hc)), max-uses-size), name(hc)]) - size +#else : + + public defstruct HeapStat : + tag : Int ; type + name : String ; name of type + num-uses : Long ; num references in heap + num-bytes : Long ; num bytes occupied in heap + with: + printer => true + + ; clear out statistics for each concrete class + lostanza defn clear-heap-statistics (vms:ptr) -> int : + labels: + begin: goto loop(0) + loop (i:int) : + val stat = vms.heap-statistics[i] + if stat.num-bytes < 0L : ; sentinel + return stat.num-uses as int + else : + vms.heap-statistics[i].num-bytes = 0 + vms.heap-statistics[i].num-uses = 0 + goto loop(i + 1) + + ; run gc, collect heap stats while walking each object in nursery and heap + lostanza defn do-analyze-heap (stats:ref>) -> ref : + val vms:ptr = call-prim flush-vm() + run-garbage-collector() + val num-classes = clear-heap-statistics(vms) + val hsize = do-analyze-heap(vms.heap.start, vms.heap.old-objects-end, vms) + val nursery = nursery-start(addr(vms.heap)) + val nsize = do-analyze-heap(nursery, vms.heap.top, vms) + for (var i:int = 0, i < num-classes, i = i + 1) : + val heap-stat = vms.heap-statistics[i] + val num-uses = heap-stat.num-uses + if num-uses > 0L : + add(stats, HeapStat(new Int{i as int}, String(class-name(i)), new Long{num-uses}, new Long{heap-stat.num-bytes})) + return new Long{ hsize + nsize } + + ; collect heap stats while walking each object in consecutive range of memory + lostanza defn do-analyze-heap (pstart:ptr, pend:ptr, vms:ptr) -> long : + var p:ptr = pstart + while p < pend : + val tag = [p] as int + val class = vms.class-table[tag].record + var size:long = 0L + if class.item-size == 0 : + size = object-size-on-heap(class.size) + else : + val class = class as ptr + val array = p as ptr + val len = array.slots[0] + val base-size = class.base-size + val item-size = class.item-size + val my-size = base-size + item-size * len + size = object-size-on-heap(my-size) + p = p + size + vms.heap-statistics[tag].num-uses = vms.heap-statistics[tag].num-uses + 1L + vms.heap-statistics[tag].num-bytes = vms.heap-statistics[tag].num-bytes + size + return (pend as long) - (pstart as long) + + ; public interface to heap analyzer collecting and printing out stats + public defn analyze-heap () -> Long : + val stats = Vector() + val size = do-analyze-heap(stats) + val res = reverse(to-list(lazy-qsort(num-bytes, stats))) + println("Heap size %_" % [size]) + var max-bytes-size = reduce(max, length("Size"), seq({ length(to-string(num-bytes(_))) }, res)) + var max-perc-size = 3 + var max-uses-size = reduce(max, length("Uses"), seq({ length(to-string(num-uses(_))) }, res)) + defn pad (s:String, n:Int) -> String : append-all(cat(repeatedly({ " " }, (n - length(s))), [s])) + println(" %_ %_ %_: %_" % [pad("Size", max-bytes-size), "Perc", pad("Use", max-uses-size), "Type"]) + for hc in res do : + val p = to-int(to-double(num-bytes(hc)) * 100.0 / to-double(size)) + println(" %_ %_%% %_: %_" % [ + pad(to-string(num-bytes(hc)), max-bytes-size), pad(to-string(p), max-perc-size), pad(to-string(num-uses(hc)), max-uses-size), name(hc)]) + size ;============================================================ ;===================== Debugging ============================ From 67670fa30ac8e30bb633f28a00c2b18c2cf009a5 Mon Sep 17 00:00:00 2001 From: jackbackrack Date: Thu, 9 Jan 2025 14:14:40 -0800 Subject: [PATCH 4/5] move length to front of heapstatistics and split out dumping from collecting of stats --- compiler/stitcher.stanza | 3 +-- core/core.stanza | 58 +++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/compiler/stitcher.stanza b/compiler/stitcher.stanza index d14d6505..7b1a922f 100644 --- a/compiler/stitcher.stanza +++ b/compiler/stitcher.stanza @@ -861,11 +861,10 @@ public defn Stitcher (packages:Collection, bindings:Bindings|False, s E $ Comment("Heap Statistics Table") E $ DefData() E $ Label(/heap-statistics(stubs)) + E $ DefLong(to-long(num-concrete-classes)) for i in 0 to num-concrete-classes do : E $ DefLong(0L) ; num-uses E $ DefLong(0L) ; num-bytes - E $ DefLong(to-long(num-concrete-classes)) - E $ DefLong(-1L) ; mark end of table E $ DefText() E $ Comment("End of Heap Statistics Table") diff --git a/core/core.stanza b/core/core.stanza index ab4ad441..6fb7c819 100644 --- a/core/core.stanza +++ b/core/core.stanza @@ -209,6 +209,10 @@ protected lostanza deftype ArrayRecord : num-item-roots:int roots:int ... +lostanza deftype HeapStatistics : + length: long + entries: HeapStatistic ... + protected lostanza deftype HeapStatistic : var num-uses:long var num-bytes:long @@ -266,7 +270,7 @@ protected lostanza deftype HeapStatistic : safepoint-table: ptr ;(Variable State) debug-table: ptr ;(Variable State) local-var-table: ptr ;(Variable State) - heap-statistics: ptr ;(Variable State) + heap-statistics: ptr ;(Variable State) ;Compiled Mode Tables class-table: ptr global-root-table: ptr @@ -2829,29 +2833,17 @@ public lostanza defn min (x:long, y:long) -> long : with: printer => true - ; clear out statistics for each concrete class - lostanza defn clear-heap-statistics (vms:ptr) -> int : - labels: - begin: goto loop(0) - loop (i:int) : - val stat = vms.heap-statistics[i] - if stat.num-bytes < 0L : ; sentinel - return stat.num-uses as int - else : - vms.heap-statistics[i].num-bytes = 0 - vms.heap-statistics[i].num-uses = 0 - goto loop(i + 1) - ; run gc, collect heap stats while walking each object in nursery and heap lostanza defn do-analyze-heap (stats:ref>) -> ref : val vms:ptr = call-prim flush-vm() run-garbage-collector() - val num-classes = clear-heap-statistics(vms) + val num-classes = vms.heap-statistics.length + clear(addr(vms.heap-statistics.entries), num-classes * sizeof(HeapStatistic)) val hsize = do-analyze-heap(vms.heap.start, vms.heap.old-objects-end, vms) val nursery = nursery-start(addr(vms.heap)) val nsize = do-analyze-heap(nursery, vms.heap.top, vms) for (var i:int = 0, i < num-classes, i = i + 1) : - val heap-stat = vms.heap-statistics[i] + val heap-stat = addr(vms.heap-statistics.entries[i]) val num-uses = heap-stat.num-uses if num-uses > 0L : add(stats, HeapStat(new Int{i as int}, String(class-name(i)), new Long{num-uses}, new Long{heap-stat.num-bytes})) @@ -2875,26 +2867,36 @@ public lostanza defn min (x:long, y:long) -> long : val my-size = base-size + item-size * len size = object-size-on-heap(my-size) p = p + size - vms.heap-statistics[tag].num-uses = vms.heap-statistics[tag].num-uses + 1L - vms.heap-statistics[tag].num-bytes = vms.heap-statistics[tag].num-bytes + size + val stat = addr(vms.heap-statistics.entries[tag]) + stat.num-uses = stat.num-uses + 1L + stat.num-bytes = stat.num-bytes + size return (pend as long) - (pstart as long) + public defstruct HeapStats : + total-size : Long + entries : Tuple + ; public interface to heap analyzer collecting and printing out stats - public defn analyze-heap () -> Long : + public defn analyze-heap () -> HeapStats : val stats = Vector() val size = do-analyze-heap(stats) val res = reverse(to-list(lazy-qsort(num-bytes, stats))) - println("Heap size %_" % [size]) - var max-bytes-size = reduce(max, length("Size"), seq({ length(to-string(num-bytes(_))) }, res)) - var max-perc-size = 3 - var max-uses-size = reduce(max, length("Uses"), seq({ length(to-string(num-uses(_))) }, res)) + HeapStats(size, to-tuple(res)) + + public defn dump (stats:HeapStats) : + println("Heap size %_" % [total-size(stats)]) + var max-bytes-size = reduce(max, length("Size"), seq({ length(to-string(num-bytes(_))) }, entries(stats))) + var max-perc-size = 6 + var max-uses-size = reduce(max, length("Uses"), seq({ length(to-string(num-uses(_))) }, entries(stats))) defn pad (s:String, n:Int) -> String : append-all(cat(repeatedly({ " " }, (n - length(s))), [s])) - println(" %_ %_ %_: %_" % [pad("Size", max-bytes-size), "Perc", pad("Use", max-uses-size), "Type"]) - for hc in res do : - val p = to-int(to-double(num-bytes(hc)) * 100.0 / to-double(size)) + defn pad0 (s:String, n:Int) -> String : append-all(cat([s], repeatedly({ "0" }, (n - length(s))))) + println(" %_ %_ %_: %_" % [pad("Size", max-bytes-size), pad("Perc", max-perc-size), pad("Use", max-uses-size), "Type"]) + for hc in entries(stats) do : + val pt = to-int(to-double(num-bytes(hc)) * 10000.0 / to-double(total-size(stats))) + val p = string-join $ [pt / 100, ".", pad0(to-string(pt - ((pt / 100) * 100)), 2)] println(" %_ %_%% %_: %_" % [ - pad(to-string(num-bytes(hc)), max-bytes-size), pad(to-string(p), max-perc-size), pad(to-string(num-uses(hc)), max-uses-size), name(hc)]) - size + pad(to-string(num-bytes(hc)), max-bytes-size), pad(p, max-perc-size), pad(to-string(num-uses(hc)), max-uses-size), name(hc)]) + ;============================================================ ;===================== Debugging ============================ From 9415cd6ab5ab78fda3efcd2975c97b1f5f1d0bb5 Mon Sep 17 00:00:00 2001 From: jackbackrack Date: Fri, 17 Jan 2025 09:47:40 -0800 Subject: [PATCH 5/5] add HEAP-ANALYZER flag so we can write code ahead of PR review --- compiler/compiler.stanza | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/compiler.stanza b/compiler/compiler.stanza index 825d7765..26e27dfc 100644 --- a/compiler/compiler.stanza +++ b/compiler/compiler.stanza @@ -243,6 +243,7 @@ public defn compile (settings:BuildSettings, system:System) : println("Build target %~ is already up-to-date." % [build-target?(settings*)]) else : setup-system-flags(settings*) + add-flag(`HEAP-ANALYZER) val proj-params = ProjParams(compiler-flags(), optimize?(settings*), debug?(settings*), false, build-from-source?(settings), pkg-cache-dir(settings*)) val proj-manager = ProjManager(proj, proj-params, auxfile) val comp-result = compile(proj-manager, auxfile, build-inputs!(settings*), vm-packages(settings*), asm?(settings*), pkg-dir(settings*),