2026-05-21 12:53:13 +03:00
|
|
|
Blaise list micro-benchmark — results log
|
|
|
|
|
==========================================
|
|
|
|
|
|
|
|
|
|
Each entry records the median of two consecutive runs of
|
2026-05-21 16:37:22 +03:00
|
|
|
tests/bench_lists.pas, taken on the machine identified in the entry
|
|
|
|
|
header. Add new entries below the previous one (newest at the
|
|
|
|
|
bottom) so the file forms a chronological trend.
|
2026-05-21 12:53:13 +03:00
|
|
|
|
2026-05-21 16:37:22 +03:00
|
|
|
Workloads (all in one program):
|
|
|
|
|
- TStringList vs TList<String>
|
|
|
|
|
- TObjectList vs TList<TObject>
|
2026-05-21 12:53:13 +03:00
|
|
|
|
|
|
|
|
Phases (all ms wall-clock):
|
|
|
|
|
insert 1,000,000 Adds (item_<i> strings or fresh TItem instances)
|
|
|
|
|
sequential Get(i) over the full list (linear sweep)
|
|
|
|
|
random 1,000,000 indexed reads in a pseudo-random pattern
|
|
|
|
|
(deterministic LCG, seed=42)
|
|
|
|
|
for..in full enumeration via the for-in protocol
|
|
|
|
|
(strings benchmark only; objects benchmark omits this)
|
|
|
|
|
IndexOf/scan 1,000 lookups on the list (TList<T> uses a hand-rolled
|
|
|
|
|
linear scan since it has no IndexOf method). Note: the
|
|
|
|
|
string benchmark searches a 100,000-item list; the object
|
|
|
|
|
benchmark searches the full 1,000,000-item list.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
|
2026-05-21 — Graeme @ AMD Ryzen 7 5800X (Linux 6.14)
|
|
|
|
|
Blaise compiler v0.9.0-dev
|
|
|
|
|
Commit: 93358ed (fix codegen ARC for ^TClass writes)
|
|
|
|
|
|
|
|
|
|
--- bench_strings ---
|
|
|
|
|
TStringList TList<String>
|
|
|
|
|
insert 1_000_000 98 ms 90 ms
|
|
|
|
|
sequential Get(i) x Count 10 ms 10 ms
|
|
|
|
|
random Get(i) x 1_000_000 19 ms 19 ms
|
|
|
|
|
for..in over 1_000_000 15 ms 11 ms
|
|
|
|
|
IndexOf / hand-rolled scan x 1000 590 ms 277 ms
|
|
|
|
|
|
|
|
|
|
--- bench_objects ---
|
|
|
|
|
TObjectList TList<TObject>
|
|
|
|
|
insert 1_000_000 36 ms 41 ms
|
|
|
|
|
sequential Get(i) x Count 4 ms 6 ms
|
|
|
|
|
random Get(i) x 1_000_000 10 ms 13 ms
|
|
|
|
|
IndexOf / hand-rolled scan x 1000 16 ms 131 ms
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
* TList<String> beats TStringList across every phase; the
|
|
|
|
|
TStringList.IndexOf cost includes sorted-mode/duplicates checks
|
|
|
|
|
the hand-rolled scan does not, hence the 2x gap.
|
|
|
|
|
* TObjectList beats TList<TObject> on writes and reads (raw pointer
|
|
|
|
|
storage vs. typed ^T with ARC), and is dramatically faster on
|
|
|
|
|
IndexOf because the call goes via a direct pointer scan in a
|
|
|
|
|
single function instead of one method call per element.
|
|
|
|
|
* The compiler currently miscompiles a program that instantiates
|
|
|
|
|
the same generic class with two different concrete arguments
|
|
|
|
|
(e.g. TList<String> and TList<TObject> in one program) — that is
|
2026-05-21 16:30:24 +03:00
|
|
|
why the benchmark is split into two files. (Fixed in a later
|
|
|
|
|
commit via per-instance AST body cloning.)
|
2026-05-21 16:37:22 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
|
2026-05-21 — Graeme @ AMD Ryzen 7 5800X (Linux 6.14)
|
|
|
|
|
Blaise compiler v0.9.0-dev
|
|
|
|
|
Commit: b6736d3 (fix semantic: clone method bodies per generic instance)
|
|
|
|
|
|
|
|
|
|
--- merged into a single program (tests/bench_lists.pas) ---
|
|
|
|
|
TStringList TList<String>
|
|
|
|
|
insert 1_000_000 99 ms 89 ms
|
|
|
|
|
sequential Get(i) x Count 10 ms 10 ms
|
|
|
|
|
random Get(i) x 1_000_000 20 ms 19 ms
|
|
|
|
|
for..in over 1_000_000 15 ms 11 ms
|
|
|
|
|
IndexOf / hand-rolled scan x 1000 681 ms 287 ms
|
|
|
|
|
|
|
|
|
|
TObjectList TList<TObject>
|
|
|
|
|
insert 1_000_000 36 ms 40 ms
|
|
|
|
|
sequential Get(i) x Count 4 ms 6 ms
|
|
|
|
|
random Get(i) x 1_000_000 9 ms 13 ms
|
|
|
|
|
IndexOf / hand-rolled scan x 1000 16 ms 131 ms
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
* Multi-instance generic miscompile is fixed — TList<String> and
|
|
|
|
|
TList<TObject> now co-exist in one program, so the two split
|
|
|
|
|
benchmarks have been merged into tests/bench_lists.pas.
|
|
|
|
|
* Numbers are within noise of the previous split-program run on
|
|
|
|
|
the same hardware; combining the workloads does not perturb
|
|
|
|
|
timings.
|
2026-05-21 19:28:44 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
|
2026-05-21 — Graeme @ AMD Ryzen 7 5800X (Linux 6.14)
|
|
|
|
|
Blaise compiler v0.9.0-dev
|
|
|
|
|
Commit: ffe09b3 (perf codegen: elide _ClassRelease on provably-nil slots)
|
|
|
|
|
|
|
|
|
|
TStringList TList<String>
|
|
|
|
|
insert 1_000_000 96 ms 90 ms
|
|
|
|
|
sequential Get(i) x Count 11 ms 11 ms
|
|
|
|
|
random Get(i) x 1_000_000 19 ms 19 ms
|
|
|
|
|
for..in over 1_000_000 15 ms 11 ms
|
|
|
|
|
IndexOf / hand-rolled scan x 1000 600 ms 283 ms
|
|
|
|
|
|
|
|
|
|
TObjectList TList<TObject>
|
|
|
|
|
insert 1_000_000 37 ms 42 ms
|
|
|
|
|
sequential Get(i) x Count 4 ms 5 ms
|
|
|
|
|
random Get(i) x 1_000_000 9 ms 12 ms
|
|
|
|
|
IndexOf / hand-rolled scan x 1000 16 ms 90 ms
|
|
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
* Big win on the TList<TObject> hand-rolled scan (~30% faster:
|
|
|
|
|
131 ms -> 90 ms). Source: nil-slot _ClassRelease elision in the
|
|
|
|
|
codegen — TList<T>.Get's `Result := Src^` no longer emits a
|
|
|
|
|
release-on-nil pair, saving one cross-TU call per Get.
|
|
|
|
|
* Smaller incidental wins on Sequential Get (6->5 ms) and Random
|
|
|
|
|
Get (13->12 ms) for TList<TObject> from the same change.
|
|
|
|
|
* TList<String> hand-rolled scan unchanged at 283 ms; the elision
|
|
|
|
|
applies to class-typed assignments only. Strings get the same
|
|
|
|
|
treatment if we extend the optimisation to strings in a follow-up.
|
|
|
|
|
* TStringList.IndexOf dropped 681->600 ms. Cause uncertain; may
|
|
|
|
|
just be run-to-run noise.
|