perf(fivesql2): gate VIEW temp cleanup on actual view usage

After the SELECT WA cache landed, pprof showed HbFileExists → os.Stat
at 28% of remaining CPU — the RunSelect cleanup loop was stat-ing
__view_<table>.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) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 13:27:49 +09:00
parent f27c96c7f0
commit 9bb361b2e0
2 changed files with 17 additions and 6 deletions

View File

@@ -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--

View File

@@ -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