perf: EndProcFast — eliminate defer recover() from RTL hot paths

Problem: every RTL function calls defer t.EndProc() which does recover().
50K SEEK loop = 250K recover() calls = ~12ms wasted.

Solution: EndProcFast() skips recover (only needs endFrame restore).
Applied to ALL RTL functions in strings.go, rdd.go, missing.go, database.go.
EndProc() with recover kept for generated PRG code (needs BEGIN SEQUENCE).

Analysis (50K sequential SEEK breakdown):
  Go NTX Seek direct: 7ms (faster than Harbour 27ms!)
  PRG VM overhead:    38ms (Frame + RTL calls + key generation)
  Key generation:     25ms (Str+LTrim+PadL+PadR = 5 RTL Frame/EndProc per iter)

With EndProcFast: RTL overhead reduced ~30%.

CDX SCOPE: 2ms (Harbour 4ms — 2x FASTER!)
82/82 stress PASS. 14 packages ALL PASS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 21:43:39 +09:00
parent 9644b5469a
commit 05ccef05e2
6 changed files with 84 additions and 74 deletions

View File

@@ -250,12 +250,21 @@ func (t *Thread) EndProc() {
if hbErr, ok := r.(*HbError); ok {
t.handleSequenceError(hbErr)
} else {
// Print error to stderr before re-panic
fmt.Fprintf(os.Stderr, "Five runtime error: %v\n", r)
panic(r)
}
}
t.endFrame()
}
// EndProcFast is called by RTL functions that don't need recover().
// ~3x faster than EndProc (no defer recover overhead).
func (t *Thread) EndProcFast() {
t.endFrame()
}
// endFrame restores the previous call frame.
func (t *Thread) endFrame() {
if t.callSP > 0 {
t.callSP--
if t.callSP > 0 {