From 48cd4f9e5c6835b61c8ecf9d409b9ef4d6e09ced Mon Sep 17 00:00:00 2001 From: Charles KWON OhJun Date: Tue, 7 Apr 2026 17:25:38 +0900 Subject: [PATCH] =?UTF-8?q?perf:=20DO=20WHILE/SEEK/DELETE=20WA=20hoisting?= =?UTF-8?q?=20=E2=80=94=20reduce=20per-iteration=20lookups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DO WHILE optimization: - Detect RDD commands in body (SKIP/GO/SEEK/REPLACE/DELETE) - If no USE/SELECT (safe), hoist _dwa/_darea before loop - SKIP/GO/SEEK/DELETE inside loop use cached area variable - Eliminates WA lookup + Current() per iteration SEEK optimization: - Use hoisted area when inside DO WHILE or FOR hoist context - Eliminates WA lookup per SEEK call in tight loops DELETE optimization: - Use hoisted area when available All commands now check g.hoistedDW || g.hoistedFields: - GO TOP/BOTTOM/n → cached area - SKIP n → cached area - SEEK key → cached area + Indexer check - DELETE → cached area - APPEND → cached area (FOR loop) - REPLACE → cached _rdbf + _rfiN (FOR loop) 82/82 stress PASS. 14 packages ALL PASS. CDX SCOPE: 12ms (Harbour 4ms = 3x) NTX SCAN: 24ms (Harbour 5ms = 4.8x) Co-Authored-By: Claude Opus 4.6 (1M context) --- compiler/gengo/gen_cmd.go | 101 +++++++++++++++++++++++++------------- compiler/gengo/gengo.go | 58 +++++++++++++++++++++- 2 files changed, 123 insertions(+), 36 deletions(-) diff --git a/compiler/gengo/gen_cmd.go b/compiler/gengo/gen_cmd.go index e68e997..feaeced 100644 --- a/compiler/gengo/gen_cmd.go +++ b/compiler/gengo/gen_cmd.go @@ -43,79 +43,110 @@ func (g *Generator) emitUseCmd(s *ast.UseCmd, locals localMap) { } func (g *Generator) emitGoCmd(s *ast.GoCmd) { - g.writeln("{") - g.indent++ - g.writeln("wa := t.WA.(*hbrdd.WorkAreaManager)") - g.writeln("if _area := wa.Current(); _area != nil {") - g.indent++ + // Use hoisted area if available + areaVar := "_area" + if g.hoistedDW || g.hoistedFields != nil { + areaVar = g.hoistedAreaVar() + g.writeln(fmt.Sprintf("if %s != nil {", areaVar)) + g.indent++ + } else { + g.writeln("{") + g.indent++ + g.writeln("wa := t.WA.(*hbrdd.WorkAreaManager)") + g.writeln(fmt.Sprintf("if %s := wa.Current(); %s != nil {", areaVar, areaVar)) + g.indent++ + } switch s.Direction { case "TOP": - g.writeln("_area.GoTop()") + g.writeln(fmt.Sprintf("%s.GoTop()", areaVar)) case "BOTTOM": - g.writeln("_area.GoBottom()") + g.writeln(fmt.Sprintf("%s.GoBottom()", areaVar)) default: if s.RecNo != nil { - // Optimize: literal integers skip stack ops if lit, ok := s.RecNo.(*ast.LiteralExpr); ok && lit.Kind == token.INT { - g.writeln(fmt.Sprintf("_area.GoTo(uint32(%s))", lit.Value)) + g.writeln(fmt.Sprintf("%s.GoTo(uint32(%s))", areaVar, lit.Value)) } else { g.emitExpr(s.RecNo) - g.writeln("_area.GoTo(uint32(t.Pop2().AsNumInt()))") + g.writeln(fmt.Sprintf("%s.GoTo(uint32(t.Pop2().AsNumInt()))", areaVar)) } } } g.indent-- g.writeln("}") - g.indent-- - g.writeln("}") + if !g.hoistedDW && g.hoistedFields == nil { + g.indent-- + g.writeln("}") + } } func (g *Generator) emitSkipCmd(s *ast.SkipCmd, locals localMap) { - g.writeln("{") - g.indent++ - g.writeln("wa := t.WA.(*hbrdd.WorkAreaManager)") - g.writeln("if _area := wa.Current(); _area != nil {") - g.indent++ + areaVar := "_area" + if g.hoistedDW || g.hoistedFields != nil { + areaVar = g.hoistedAreaVar() + g.writeln(fmt.Sprintf("if %s != nil {", areaVar)) + g.indent++ + } else { + g.writeln("{") + g.indent++ + g.writeln("wa := t.WA.(*hbrdd.WorkAreaManager)") + g.writeln(fmt.Sprintf("if %s := wa.Current(); %s != nil {", areaVar, areaVar)) + g.indent++ + } if s.Count != nil { - // Optimize: literal skip count avoids stack ops if lit, ok := s.Count.(*ast.LiteralExpr); ok && lit.Kind == token.INT { - g.writeln(fmt.Sprintf("_area.Skip(%s)", lit.Value)) + g.writeln(fmt.Sprintf("%s.Skip(%s)", areaVar, lit.Value)) } else if unary, ok := s.Count.(*ast.UnaryExpr); ok { if lit2, ok2 := unary.X.(*ast.LiteralExpr); ok2 && lit2.Kind == token.INT { - g.writeln(fmt.Sprintf("_area.Skip(-%s)", lit2.Value)) + g.writeln(fmt.Sprintf("%s.Skip(-%s)", areaVar, lit2.Value)) } else { g.emitExpr(s.Count) - g.writeln("_area.Skip(t.Pop2().AsNumInt())") + g.writeln(fmt.Sprintf("%s.Skip(t.Pop2().AsNumInt())", areaVar)) } } else { g.emitExpr(s.Count) - g.writeln("_area.Skip(t.Pop2().AsNumInt())") + g.writeln(fmt.Sprintf("%s.Skip(t.Pop2().AsNumInt())", areaVar)) } } else { - g.writeln("_area.Skip(1)") + g.writeln(fmt.Sprintf("%s.Skip(1)", areaVar)) } g.indent-- g.writeln("}") - g.indent-- - g.writeln("}") + if !g.hoistedDW && g.hoistedFields == nil { + g.indent-- + g.writeln("}") + } +} + +// hoistedAreaVar returns the area variable name for the current hoisting context. +func (g *Generator) hoistedAreaVar() string { + if g.hoistedFields != nil { + return "_rarea" + } + return "_darea" } func (g *Generator) emitSeekCmd(s *ast.SeekCmd, locals localMap) { - g.writeln("{") - g.indent++ - g.writeln("wa := t.WA.(*hbrdd.WorkAreaManager)") - g.writeln("if area := wa.Current(); area != nil {") - g.indent++ + areaVar := "area" + if g.hoistedDW || g.hoistedFields != nil { + areaVar = g.hoistedAreaVar() + g.writeln(fmt.Sprintf("if %s != nil {", areaVar)) + g.indent++ + } else { + g.writeln("{") + g.indent++ + g.writeln("wa := t.WA.(*hbrdd.WorkAreaManager)") + g.writeln(fmt.Sprintf("if %s := wa.Current(); %s != nil {", areaVar, areaVar)) + g.indent++ + } g.emitExpr(s.Key) g.writeln("_key := t.Pop2()") - g.writeln("if _idx, ok := area.(hbrdd.Indexer); ok {") + g.writeln(fmt.Sprintf("if _idx, ok := %s.(hbrdd.Indexer); ok {", areaVar)) g.indent++ - // SoftSeek: from SEEK SOFT keyword OR runtime SET SOFTSEEK if s.SoftSeek { g.writeln("_found, _ := _idx.Seek(_key, true, false)") } else { @@ -127,8 +158,10 @@ func (g *Generator) emitSeekCmd(s *ast.SeekCmd, locals localMap) { g.indent-- g.writeln("}") - g.indent-- - g.writeln("}") + if !g.hoistedDW && g.hoistedFields == nil { + g.indent-- + g.writeln("}") + } } func (g *Generator) emitReplaceCmd(s *ast.ReplaceCmd, locals localMap) { diff --git a/compiler/gengo/gengo.go b/compiler/gengo/gengo.go index 7537765..f34c0da 100644 --- a/compiler/gengo/gengo.go +++ b/compiler/gengo/gengo.go @@ -38,6 +38,7 @@ type Generator struct { staticVars map[string]string // top-level STATIC: upper name → Go var name IsLibrary bool // if true, no main() generated, symbols use unique name hoistedFields []string // field names hoisted outside FOR loop (nil = not hoisting) + hoistedDW bool // DO WHILE has hoisted _dwa/_darea Debug bool // if true, emit t.DebugLine() calls } @@ -465,8 +466,12 @@ func (g *Generator) emitStmt(stmt ast.Stmt, locals localMap) { g.writeln("if _area := _wa.Current(); _area != nil { _area.Append() } }") } case *ast.DeleteCmd: - g.writeln("{ _wa := t.WA.(*hbrdd.WorkAreaManager)") - g.writeln("if _area := _wa.Current(); _area != nil { _area.Delete() } }") + if g.hoistedDW || g.hoistedFields != nil { + g.writeln(fmt.Sprintf("if %s != nil { %s.Delete() }", g.hoistedAreaVar(), g.hoistedAreaVar())) + } else { + g.writeln("{ _wa := t.WA.(*hbrdd.WorkAreaManager)") + g.writeln("if _area := _wa.Current(); _area != nil { _area.Delete() } }") + } case *ast.SelectCmd: g.emitExpr(s.Area) g.writeln("{ _wa := t.WA.(*hbrdd.WorkAreaManager); _v := t.Pop2()") @@ -854,6 +859,18 @@ func (g *Generator) emitIf(s *ast.IfStmt, locals localMap) { } func (g *Generator) emitDoWhile(s *ast.DoWhileStmt, locals localMap) { + // Detect RDD commands in body for WA hoisting + hasRDD := hasRDDCommands(s.Body) + safeToHoist := hasRDD && !hasWorkareaChange(s.Body) + + if safeToHoist && g.hoistedFields == nil { + g.writeln("{") + g.indent++ + g.writeln("_dwa := t.WA.(*hbrdd.WorkAreaManager)") + g.writeln("_darea := _dwa.Current()") + g.hoistedDW = true + } + g.writeln("for {") g.indent++ g.emitExpr(s.Cond) @@ -863,6 +880,43 @@ func (g *Generator) emitDoWhile(s *ast.DoWhileStmt, locals localMap) { } g.indent-- g.writeln("}") + + if safeToHoist && g.hoistedDW { + g.hoistedDW = false + g.indent-- + g.writeln("}") + } +} + +// hasRDDCommands checks if any statement is an RDD operation. +func hasRDDCommands(stmts []ast.Stmt) bool { + for _, s := range stmts { + switch s.(type) { + case *ast.SkipCmd, *ast.GoCmd, *ast.SeekCmd, + *ast.ReplaceCmd, *ast.AppendCmd, *ast.DeleteCmd: + return true + } + } + return false +} + +// hasWorkareaChange checks for USE/SELECT that would invalidate cached area. +func hasWorkareaChange(stmts []ast.Stmt) bool { + for _, s := range stmts { + switch v := s.(type) { + case *ast.UseCmd, *ast.SelectCmd: + return true + case *ast.IfStmt: + if hasWorkareaChange(v.Body) || hasWorkareaChange(v.ElseBody) { + return true + } + case *ast.DoWhileStmt: + if hasWorkareaChange(v.Body) { + return true + } + } + } + return false } // collectReplaceFields scans statements for REPLACE field names.