Major changes since last commit: - FiveSql2 SQL:1999 engine (10,458 LOC) — 43/43 ALL PASS - 21 compiler/runtime bugs fixed (short-circuit AND/OR, FOR LOOP, etc.) - @byref pass-by-reference via RefCell pattern - Mutable closure capture (EnsureLocalRef + RefCell sharing) - RTL: 400 → 479 functions (+79: file, string, datetime, hash, UTF-8) - DateTime/Timestamp fully working (hb_DateTime, hb_Hour/Min/Sec, display) - Reserved word guard (39 keywords blocked from function calls) - AEval arg order fix (element before index) - Closure capture redecl fix (unique _cap_ names per block) - Hash/string indexing in ArrayPush/ArrayPop - Harbour compat test suite: 51/51 - 4 docs: Porting Report, Implementation Plan, Optimization Plan, Commercialization Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
83 lines
2.9 KiB
Plaintext
83 lines
2.9 KiB
Plaintext
/*
|
|
* bench_parser.prg — Parser performance benchmark
|
|
* Compares TSqlParser (original) vs TSqlParser2 (Pratt)
|
|
*
|
|
* Copyright (c) 2025-2026 Charles KWON (Charles KWON OhJun)
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "FiveSqlDef.ch"
|
|
#include "hbclass.ch"
|
|
|
|
#define ITERS 10000
|
|
|
|
PROCEDURE Main()
|
|
|
|
LOCAL aSQL, i, j, t0, t1, oLex, aTokens, oP, h
|
|
LOCAL nT1, nT2, aAllTokens
|
|
|
|
aSQL := { ;
|
|
"SELECT name, salary FROM employees WHERE salary > 5000", ;
|
|
"SELECT e.name, o.product FROM employees e JOIN orders o ON e.id = o.emp_id WHERE o.amount > 100", ;
|
|
"SELECT dept, COUNT(*) AS cnt, AVG(salary) AS avg_sal FROM employees GROUP BY dept HAVING AVG(salary) > 5000 ORDER BY avg_sal DESC", ;
|
|
"SELECT name FROM employees WHERE id IN (SELECT emp_id FROM orders WHERE amount > 500)", ;
|
|
"SELECT name, salary, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn FROM employees", ;
|
|
"SELECT name, CASE WHEN salary > 7000 THEN 'High' WHEN salary > 5000 THEN 'Mid' ELSE 'Low' END AS tier FROM employees WHERE dept = 'Engineering' OR (salary > 6000 AND mgr_id IS NOT NULL)", ;
|
|
"INSERT INTO employees (id, name, dept, salary) VALUES (99, 'Test', 'QA', 5000)", ;
|
|
"UPDATE employees SET salary = salary * 1.1 + 500 WHERE dept = 'Sales' AND salary BETWEEN 4000 AND 6000" ;
|
|
}
|
|
|
|
? "================================================================"
|
|
? " Parser Benchmark: TSqlParser vs TSqlParser2 (Pratt)"
|
|
? " Queries: " + hb_ntos( Len( aSQL ) ) + " Iterations: " + hb_ntos( ITERS )
|
|
? "================================================================"
|
|
?
|
|
|
|
/* Pre-tokenize all queries */
|
|
aAllTokens := {}
|
|
FOR i := 1 TO Len( aSQL )
|
|
oLex := TSqlLexer():New( aSQL[ i ] )
|
|
oLex:Tokenize()
|
|
AAdd( aAllTokens, oLex:GetTokens() )
|
|
NEXT
|
|
|
|
/* Benchmark TSqlParser (original) */
|
|
t0 := hb_MilliSeconds()
|
|
FOR j := 1 TO ITERS
|
|
FOR i := 1 TO Len( aSQL )
|
|
oP := TSqlParser():New( aAllTokens[ i ], {} )
|
|
h := oP:Parse()
|
|
NEXT
|
|
NEXT
|
|
t1 := hb_MilliSeconds()
|
|
nT1 := t1 - t0
|
|
? " TSqlParser (original) : " + Str( nT1, 8 ) + " ms (" + ;
|
|
Str( nT1 * 1000 / ( ITERS * Len( aSQL ) ), 6, 1 ) + " us/parse)"
|
|
|
|
/* Benchmark TSqlParser2 (Pratt) */
|
|
t0 := hb_MilliSeconds()
|
|
FOR j := 1 TO ITERS
|
|
FOR i := 1 TO Len( aSQL )
|
|
oP := TSqlParser2():New( aAllTokens[ i ], {} )
|
|
h := oP:Parse()
|
|
NEXT
|
|
NEXT
|
|
t1 := hb_MilliSeconds()
|
|
nT2 := t1 - t0
|
|
? " TSqlParser2 (Pratt) : " + Str( nT2, 8 ) + " ms (" + ;
|
|
Str( nT2 * 1000 / ( ITERS * Len( aSQL ) ), 6, 1 ) + " us/parse)"
|
|
|
|
?
|
|
IF nT2 > 0 .AND. nT1 > 0
|
|
IF nT1 > nT2
|
|
? " Pratt is " + Str( nT1 / nT2, 5, 2 ) + "x faster"
|
|
ELSEIF nT2 > nT1
|
|
? " Original is " + Str( nT2 / nT1, 5, 2 ) + "x faster"
|
|
ELSE
|
|
? " Same speed"
|
|
ENDIF
|
|
ENDIF
|
|
? "================================================================"
|
|
|
|
RETURN
|