Files
five/_FiveSql2/test/bench_bulk.prg
CharlesKWON dd270d5d9d perf: RTL Go-native migration — 27 optimizations, DML up to 70-90x
Systematic pass through PRG hot paths, promoting them to Go RTL while
preserving Harbour/FiveSql2 semantics. Full log in
docs/RTL-Go-Native-Migration.md.

Bench (bench_sql) vs 2026-04-08 baseline
 - B1  SELECT *             2,192 → 114   µs   (19x)
 - B6  INNER JOIN           9,291 → 233   µs   (40x)
 - B7  CTE simple           8,037 → 129   µs   (62x)
 - B9  ROW_NUMBER           3,705 → 265   µs   (14x)
 - B10 RANK PARTITION       4,748 → 309   µs   (15x)
 - B12 INSERT (WA cache)    4,319 →  63   µs   (69x)
 - B13 UPDATE (WA cache)    6,144 →  68   µs   (90x)
 - B15 CTE+WIN+JOIN        18,395 → 1,873 µs   (10x)

Infrastructure
 - HbHash O(1) Index preserving insertion order (Harbour KEEPORDER)
 - HbDeepClone Go RTL (scalar-sharing, immutable hash keys)
 - MEMRDD auto-imported via gengo; all Five programs get mem:name driver
 - SQL plan + pcode caches (s_hPlanCache, s_hDmlPcodeCache)
 - Opt-in SqlWACacheEnable — dbUseArea/Close/Commit batched for DML

SQL engine
 - FiveSql2 lexer ported to Go (byte FSM) with combined automatic
   template parameterization (literals → ?, concat queries share plan)
 - Go RTL: SqlDistinct, SqlGroupRows, SqlWindowPartitions,
   SqlWindowSortPartition, SqlWindowAssignRank, SqlComputeAggSimple,
   SqlBulkInsert, SqlBulkUpdate, SqlExprHasAgg, SqlEvalHaving
 - CTE / subquery / driving-table materialize paths use MEMRDD
 - SqlCoerce/SqlCmp/SqlIsTrue helpers moved from PRG to Go
 - SqlBulkUpdate defers Flush when WA cache active (APFS fsync was
   dominant B13 cost — 1.6ms/call → gone)

Correctness fixes uncovered during migration
 - ASort default path now sorts dates/logicals/timestamps (was no-op)
 - ORDER BY default NULL placement matches PRG SqlRowCompare across
   Go fast path; explicit NULLS FIRST/LAST honored by both paths
 - SqlBulkUpdate respects EXCLUSIVE vs SHARED mode record locks
 - SqlCmp/SqlCmpEq normalize NumInt vs Double (caught by test 6b)

Verification
 - go test ./...              ALL PASS
 - FiveSql2 test_sql1999      43/43
 - tests/compat_harbour       56/56 (+5 new: ASort dates/logicals,
                              AScan int cross-type)
 - Regression test test_null_order.prg for ORDER BY NULL ordering

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 20:20:14 +09:00

93 lines
2.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Large-scale bulk-insert / CTE materialization benchmark.
// Isolates the SqlBulkInsert Go RTL win: rows × cols boundary
// crossings collapse to a single RTL call, so the speedup grows
// linearly with N and M.
#include "FiveSqlDef.ch"
PROCEDURE Main()
LOCAL t0, t1, i, aR, nRows
ErrorBlock( {|e| QOut( "TRAP: " + e:description + " " + e:operation ), Break(e) } )
? "================================================================"
? " FiveSql2 Bulk Insert / Large-CTE Benchmark"
? "================================================================"
?
SetupLarge()
? "--- CTE materialization at scale ---"
/* Big CTE: filter 10,000 rows, materialize, ORDER BY in outer. */
t0 := hb_MilliSeconds()
FOR i := 1 TO 20
aR := five_SQL( ;
"WITH big_cte AS (SELECT id, name, val FROM bench_big WHERE val > 5000) " + ;
"SELECT * FROM big_cte ORDER BY val DESC" )
NEXT
t1 := hb_MilliSeconds()
nRows := 0
IF ValType( aR ) == "A" .AND. Len( aR ) >= 2
nRows := Len( aR[ 2 ] )
ENDIF
R( "BULK_CTE_10k_20iter", t1 - t0, nRows )
/* Subquery-driving-table materialization at scale. */
t0 := hb_MilliSeconds()
FOR i := 1 TO 20
aR := five_SQL( ;
"SELECT a.id, a.val FROM (SELECT id, val FROM bench_big WHERE val > 8000) a " + ;
"ORDER BY a.val" )
NEXT
t1 := hb_MilliSeconds()
nRows := 0
IF ValType( aR ) == "A" .AND. Len( aR ) >= 2
nRows := Len( aR[ 2 ] )
ENDIF
R( "BULK_SUBQ_10k_20iter", t1 - t0, nRows )
CleanupLarge()
?
? "================================================================"
RETURN
STATIC FUNCTION SetupLarge()
LOCAL i
IF hb_FileExists( "bench_big.dbf" )
FErase( "bench_big.dbf" )
ENDIF
dbCreate( "bench_big.dbf", { ;
{ "ID", "N", 10, 0 }, ;
{ "NAME", "C", 30, 0 }, ;
{ "VAL", "N", 10, 0 } ;
} )
USE bench_big.dbf NEW EXCLUSIVE
FOR i := 1 TO 10000
dbAppend()
FieldPut( 1, i )
FieldPut( 2, "Name_" + PadL( hb_ntos( i ), 6, "0" ) )
FieldPut( 3, i )
NEXT
dbCommit()
CLOSE bench_big
RETURN NIL
STATIC FUNCTION CleanupLarge()
dbCloseAll()
FErase( "bench_big.dbf" )
RETURN NIL
STATIC FUNCTION R( cLabel, nMs, nRows )
LOCAL cLine := PadR( cLabel, 28 ) + Str( nMs, 6 ) + " ms"
IF nRows > 0
cLine += " rows=" + hb_ntos( nRows )
ENDIF
? " ", cLine
RETURN NIL