From 9bb361b2e0b585811486d934b42a74722e83f39b Mon Sep 17 00:00:00 2001 From: CharlesKWON Date: Sat, 18 Apr 2026 13:27:49 +0900 Subject: [PATCH] perf(fivesql2): gate VIEW temp cleanup on actual view usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the SELECT WA cache landed, pprof showed HbFileExists → os.Stat at 28% of remaining CPU — the RunSelect cleanup loop was stat-ing __view_.dbf for every table in every query, even on the common view-free path. Track view materialisation with a TSqlIndex.lViewUsed flag set in OpenTable when CheckView produces a temp. The cleanup loop now runs only when the flag is set, then resets it. View-using queries are unaffected. pprof delta: rawsyscalln: 2.14s → 1.41s (48% → 32% of total CPU) os.Stat: 1.24s → 0.49s (28% → 11%) Wall-clock bench numbers stayed within plus-or-minus 3% noise (stats are cheap when the target file does not exist, so CPU savings do not translate directly to end-to-end time) but this removes the next biggest syscall waste visible in the profile. FiveSql2 43/43, Harbour compat 56/56. Co-Authored-By: Claude Opus 4.7 (1M context) --- _FiveSql2/src/TSqlExecutor.prg | 17 +++++++++++------ _FiveSql2/src/TSqlIndex.prg | 6 ++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/_FiveSql2/src/TSqlExecutor.prg b/_FiveSql2/src/TSqlExecutor.prg index 0431f7d..163109b 100644 --- a/_FiveSql2/src/TSqlExecutor.prg +++ b/_FiveSql2/src/TSqlExecutor.prg @@ -1662,12 +1662,17 @@ METHOD RunSelect() CLASS TSqlExecutor ENDIF /* Clean up VIEW temp files — created by TSqlIndex:CheckView when - * a query references a .fsv view. Not tracked elsewhere. */ - FOR i := 1 TO Len( ::aTables ) - IF hb_FileExists( "__view_" + Lower( ::aTables[ i ][ 1 ] ) + ".dbf" ) - FErase( "__view_" + Lower( ::aTables[ i ][ 1 ] ) + ".dbf" ) - ENDIF - NEXT + * a query references a .fsv view. The flag lets us skip the stat + * loop on view-free queries, which the profile showed as ~28% of + * SELECT CPU after the WA cache killed the munmap cost. */ + IF ::oIndex:lViewUsed + FOR i := 1 TO Len( ::aTables ) + IF hb_FileExists( "__view_" + Lower( ::aTables[ i ][ 1 ] ) + ".dbf" ) + FErase( "__view_" + Lower( ::aTables[ i ][ 1 ] ) + ".dbf" ) + ENDIF + NEXT + ::oIndex:lViewUsed := .F. + ENDIF ::nDepth-- diff --git a/_FiveSql2/src/TSqlIndex.prg b/_FiveSql2/src/TSqlIndex.prg index 257576d..c83556b 100644 --- a/_FiveSql2/src/TSqlIndex.prg +++ b/_FiveSql2/src/TSqlIndex.prg @@ -16,6 +16,11 @@ CLASS TSqlIndex + /* Set to .T. when CheckView materialises a __view_* temp for a + * table this query references — lets RunSelect skip its stat+ + * FErase cleanup loop on view-free queries (the common case). */ + DATA lViewUsed INIT .F. + METHOD New() CONSTRUCTOR METHOD DetectRDD( nWA ) METHOD OpenTable( cTable, cAlias, lShared, lReadOnly ) @@ -82,6 +87,7 @@ METHOD OpenTable( cTable, cAlias, lShared, lReadOnly ) CLASS TSqlIndex cViewTmp := ::CheckView( cTable ) IF ! Empty( cViewTmp ) cFileLow := cViewTmp + ::lViewUsed := .T. ENDIF ENDIF