Chunk processes run under a 512 MiB soft memory limit (GOMEMLIMIT overrides) so garbage from the SAT-heavy removal loop cannot double the heap; the field runner prints each chunk's wall clock, max RSS, region and context size under MOSAIC_DEBUG and dumps the chunk's params JSON under MOSAIC_CHUNK_DUMP for reproduction; --grow --state-out writes the merged state for chaining rings.
- commit
- 5657bc8ab1df70d17635156b82171878f9968191
- parent
- 7a85496c05a9beebd8f5cc72a58354e60efe4621
- author
- dami <gazey55@gmail.com>
| M | engine/ | 20 | +++++++++++++++++++- |
| M | engine/ | 26 | ++++++++++++++++++++++++++ |
@@ -10,9 +10,11 @@ import (
1010 "fmt"1111 "os"1212 "os/exec"13+ "runtime"1314 "sort"1415 "strings"1516 "sync"17+ "syscall"1618 "time"1719 1820 "mosaic-engine/pack"@@ -159,14 +161,30 @@ func SubprocessRunner(exe string, timeout time.Duration) Runner {
159161 cmd.Stdout = &out160162 cmd.Stderr = &errb161163 cmd.Env = os.Environ()164+ t0 := time.Now()162165 runErr := cmd.Run()163166 if Verbose {164- // the chunk's own failure lines, attributed167+ // the chunk's own failure lines, attributed, then its cost165168 for _, line := range strings.Split(errb.String(), "\n") {166169 if strings.Contains(line, "attempt") || strings.Contains(line, "released") {167170 fmt.Fprintf(os.Stderr, "field: chunk %d: %s\n", p.Chunk, line)168171 }169172 }173+ rss := int64(0)174+ if cmd.ProcessState != nil {175+ if ru, ok := cmd.ProcessState.SysUsage().(*syscall.Rusage); ok {176+ rss = int64(ru.Maxrss)177+ if runtime.GOOS == "darwin" {178+ rss /= 1 << 20 // bytes on darwin179+ } else {180+ rss /= 1 << 10 // kilobytes elsewhere181+ }182+ }183+ }184+ fmt.Fprintf(os.Stderr, "field: chunk %d region %v fixed %d: %s, max rss %d MB\n", p.Chunk, p.Region, len(p.Fixed), time.Since(t0).Round(time.Millisecond), rss)185+ if os.Getenv("MOSAIC_CHUNK_DUMP") != "" {186+ _ = os.WriteFile(fmt.Sprintf("%s/mosaic-chunk-%d.json", os.TempDir(), p.Chunk), in, 0o644)187+ }170188 }171189 if err := runErr; err != nil {172190 msg := strings.TrimSpace(errb.String())@@ -18,6 +18,7 @@ import (
1818 "fmt"1919 "io"2020 "os"21+ "runtime/debug"2122 "time"2223 2324 "mosaic-engine/gen"@@ -47,7 +48,18 @@ func main() {
4748 }4849 }4950 51+// chunk processes run under a soft memory limit so the collector keeps52+// the heap near live data instead of letting garbage double it (GOMEMLIMIT53+// overrides; the hosting box has 2 GB for everything)54+func memoryLimit() {55+ if os.Getenv("GOMEMLIMIT") != "" {56+ return57+ }58+ debug.SetMemoryLimit(512 << 20)59+}60+5061 func runGen(args []string) {62+ memoryLimit()5163 fs := flag.NewFlagSet("gen", flag.ExitOnError)5264 seed := fs.String("seed", "", "seed string; empty reads JSON params from stdin")5365 nonce := fs.String("nonce", "", "commitment nonce (derived from seed when empty)")@@ -94,6 +106,7 @@ func runField(args []string) {
94106 rings := fs.String("rings", "", "with the initial generation: also generate this many rings (or 'all' until the field is exhausted) and emit {base, rings: [delta...]}")95107 state := fs.String("state", "", "with --grow: file holding the field's private output (gen JSON)")96108 ring := fs.Int("ring", 1, "with --grow: how many rings of chunks to add")109+ stateOut := fs.String("state-out", "", "with --grow: also write the merged state (base + delta) to this file, for chaining")97110 timeout := fs.Duration("timeout", 2*time.Hour, "overall budget")98111 chunkTimeout := fs.Duration("chunk-timeout", 30*time.Minute, "budget per chunk process")99112 _ = fs.Parse(args)@@ -124,6 +137,19 @@ func runField(args []string) {
124137 if err != nil {125138 fail(err)126139 }140+ if *stateOut != "" {141+ merged, err := gen.ApplyGrowth(&st, delta)142+ if err != nil {143+ fail(err)144+ }145+ data, err := json.Marshal(merged)146+ if err != nil {147+ fail(err)148+ }149+ if err := os.WriteFile(*stateOut, data, 0o644); err != nil {150+ fail(err)151+ }152+ }127153 emit(delta)128154 return129155 }