diff --git a/examples/bench_rdd.prg b/examples/bench_rdd.prg new file mode 100644 index 0000000..59c8f39 --- /dev/null +++ b/examples/bench_rdd.prg @@ -0,0 +1,243 @@ +// RDD Performance Benchmark — Harbour vs Five +// Run in home directory for fair comparison + + +PROCEDURE Main() + LOCAL i, j, nH, nStart, nEnd, nElapsed, nCount + LOCAL aStruct, aCities, nIdx + + ErrorBlock({|e| Break(e)}) + + aCities := {"Seoul","Tokyo","Beijing","London","NYC","Paris","Berlin","Rome","Madrid","Oslo"} + + aStruct := { ; + {"ID","N",8,0}, {"NAME","C",30,0}, {"CITY","C",15,0}, ; + {"SALARY","N",12,2}, {"ACTIVE","L",1,0}, {"CODE","C",10,0} ; + } + + BEGIN SEQUENCE + + // ============================================ + // BENCH 1: CREATE + APPEND 10,000 records + // ============================================ + nStart := Seconds() + dbCreate("bench_test", aStruct) + USE "bench_test" NEW + FOR i := 1 TO 10000 + APPEND BLANK + REPLACE ID WITH i + REPLACE NAME WITH PadR("Name_" + PadL(LTrim(Str(i)), 5, "0"), 30) + nIdx := ((i-1) % 10) + 1 + REPLACE CITY WITH PadR(aCities[nIdx], 15) + REPLACE SALARY WITH 20000 + i * 1.50 + REPLACE ACTIVE WITH (i % 3 != 0) + REPLACE CODE WITH PadR(LTrim(Str(Int(((i-1)/100))+1)), 10) + NEXT + nEnd := Seconds() + R("B1_APPEND_10K", FmtMs(nEnd - nStart)) + + // ============================================ + // BENCH 2: INDEX ON NAME (10,000 records) + // ============================================ + nStart := Seconds() + INDEX ON FIELD->NAME TO bench_name + nEnd := Seconds() + R("B2_INDEX_NAME", FmtMs(nEnd - nStart)) + + // ============================================ + // BENCH 3: INDEX ON CITY (duplicate keys) + // ============================================ + CLOSE ALL + USE "bench_test" NEW + nStart := Seconds() + INDEX ON FIELD->CITY TO bench_city + nEnd := Seconds() + R("B3_INDEX_CITY", FmtMs(nEnd - nStart)) + + // ============================================ + // BENCH 4: INDEX ON numeric key + // ============================================ + CLOSE ALL + USE "bench_test" NEW + nStart := Seconds() + INDEX ON Str(FIELD->ID, 8) TO bench_id + nEnd := Seconds() + R("B4_INDEX_ID", FmtMs(nEnd - nStart)) + + CLOSE ALL + + // ============================================ + // BENCH 5: Sequential SEEK (10,000 exact seeks) + // ============================================ + USE "bench_test" NEW + SET INDEX TO bench_name + + nStart := Seconds() + nCount := 0 + FOR i := 1 TO 10000 + SEEK PadR("Name_" + PadL(LTrim(Str(i)), 5, "0"), 30) + IF Found() + nCount++ + ENDIF + NEXT + nEnd := Seconds() + R("B5_SEEK_10K", FmtMs(nEnd - nStart) + " found=" + LTrim(Str(nCount))) + CLOSE ALL + + // ============================================ + // BENCH 6: Random SEEK (10,000 random seeks) + // ============================================ + USE "bench_test" NEW + SET INDEX TO bench_name + + nStart := Seconds() + nCount := 0 + FOR i := 1 TO 10000 + j := ((i * 7919) % 10000) + 1 + SEEK PadR("Name_" + PadL(LTrim(Str(j)), 5, "0"), 30) + IF Found() + nCount++ + ENDIF + NEXT + nEnd := Seconds() + R("B6_SEEK_RND", FmtMs(nEnd - nStart) + " found=" + LTrim(Str(nCount))) + CLOSE ALL + + // ============================================ + // BENCH 7: Full forward traversal (GO TOP → EOF) + // ============================================ + USE "bench_test" NEW + SET INDEX TO bench_name + + nStart := Seconds() + GO TOP + nCount := 0 + DO WHILE !EOF() + nCount++ + SKIP + ENDDO + nEnd := Seconds() + R("B7_SCAN_FWD", FmtMs(nEnd - nStart) + " count=" + LTrim(Str(nCount))) + CLOSE ALL + + // ============================================ + // BENCH 8: Full backward traversal (GO BOTTOM → BOF) + // ============================================ + USE "bench_test" NEW + SET INDEX TO bench_name + + nStart := Seconds() + GO BOTTOM + nCount := 0 + DO WHILE !BOF() + nCount++ + SKIP -1 + ENDDO + nEnd := Seconds() + R("B8_SCAN_BWD", FmtMs(nEnd - nStart) + " count=" + LTrim(Str(nCount))) + CLOSE ALL + + // ============================================ + // BENCH 9: Duplicate key SEEK + count (CITY) + // ============================================ + USE "bench_test" NEW + SET INDEX TO bench_city + + nStart := Seconds() + nCount := 0 + FOR i := 1 TO 10 + SEEK PadR(aCities[i], 15) + DO WHILE !EOF() .AND. RTrim(FieldGet(3)) == aCities[i] + nCount++ + SKIP + ENDDO + NEXT + nEnd := Seconds() + R("B9_DUPKEY_SCAN", FmtMs(nEnd - nStart) + " count=" + LTrim(Str(nCount))) + CLOSE ALL + + // ============================================ + // BENCH 10: Numeric SEEK (ID index) + // ============================================ + USE "bench_test" NEW + SET INDEX TO bench_id + + nStart := Seconds() + nCount := 0 + FOR i := 1 TO 10000 + SEEK Str(i, 8) + IF Found() + nCount++ + ENDIF + NEXT + nEnd := Seconds() + R("B10_SEEK_NUM", FmtMs(nEnd - nStart) + " found=" + LTrim(Str(nCount))) + CLOSE ALL + + // ============================================ + // BENCH 11: DELETE + SET DELETED ON + traversal + // ============================================ + USE "bench_test" NEW + SET INDEX TO bench_name + SET ORDER TO 0 + FOR i := 1 TO 10000 + GO i + IF i % 5 == 0 + DELETE + ENDIF + NEXT + + SET ORDER TO 1 + SET DELETED ON + nStart := Seconds() + GO TOP + nCount := 0 + DO WHILE !EOF() + nCount++ + SKIP + ENDDO + nEnd := Seconds() + R("B11_DEL_SCAN", FmtMs(nEnd - nStart) + " count=" + LTrim(Str(nCount))) + + SET DELETED OFF + SET ORDER TO 0 + FOR i := 1 TO 10000 + GO i + IF Deleted() + RECALL + ENDIF + NEXT + CLOSE ALL + + // ============================================ + // BENCH 12: PACK 10,000 records (2,000 deleted) + // ============================================ + USE "bench_test" NEW + FOR i := 1 TO 10000 + GO i + IF i % 5 == 0 + DELETE + ENDIF + NEXT + + nStart := Seconds() + PACK + nEnd := Seconds() + R("B12_PACK", FmtMs(nEnd - nStart) + " remain=" + LTrim(Str(RecCount()))) + CLOSE ALL + + RECOVER + ? "ERROR" + END SEQUENCE + + +RETURN + +FUNCTION FmtMs(nSec) + LOCAL nMs + nMs := Int(nSec * 1000) +RETURN LTrim(Str(nMs)) + "ms" + +PROCEDURE R(cKey, cVal) + ? cKey + "=" + cVal +RETURN