Hello,
We have been investigating a severe startup-time issue in one of our RemObjects SDK for Delphi servers that uses Code First services.
Before posting this, we profiled the complete RTTI → RODL generation pipeline and validated multiple hypotheses. One of our initial assumptions turned out to be wrong, so the conclusions below are based entirely on measurements.
Our intention is not to suggest a specific implementation, but rather to verify whether the behavior we found is expected.
Environment
- RemObjects SDK for Delphi 10.0.0.1613
- Delphi 12 Athens
- Reproducible on both Win32 and Win64
Benchmark
To isolate the problem, we created a benchmark consisting only of trivial Code First services.
Each service contains:
- one
[ROService]class - one simple method
- no DTOs
- no HTTP attributes
- no Swagger attributes
- no business logic
The benchmark measures only RTTI → RODL generation:
srv := TMyServer.Create(nil);
reader := srv.RTTIReader;
sw := TStopwatch.StartNew;
reader.ReadRODLResource(Stream, ‘’, True, False);
sw.Stop;
Results:
| Services | Time | Generated RODL |
|---|---|---|
| 250 | 37 ms | 137 KB |
| 500 | 239 ms | 275 KB |
| 1000 | 1619 ms | 550 KB |
| 2000 | 13550 ms | 1101 KB |
| 4000 | 109674 ms | 2203 KB |
The generated RODL grows linearly.
Generation time grows dramatically (approximately O(N²·⁹) in this benchmark).
First hypothesis (discarded)
Initially we suspected the repeated TList.Contains() calls inside FindAllROTypes().
We replaced those lookups with hash-based lookups.
The result was almost no measurable improvement, so that hypothesis turned out to be incorrect.
Profiling
Profiling shows that almost the entire execution time is spent inside the generation loop of TRORTTIRODLReader.DoProcess().
FindAllROTypes() itself represents only a very small fraction of the total runtime.
What appears to be the bottleneck
The dominant cost appears to come from repeated linear lookups performed during the Generate* phase.
For example:
GenerateService()→TRODLLibrary.FindService()GenerateStruct()→TRODLLibrary.FindStruct()GenerateArray()→TRODLLibrary.FindArray()GenerateEnum()→TRODLLibrary.FindEnum()GenerateException()→TRODLLibrary.FindException()
Each Find*() performs a linear scan over a collection that keeps growing during generation.
Proof of concept
As an experiment, we replaced those repeated linear lookups with hash-based lookups during generation.
Results:
| Services | Original | Experimental lookup |
|---|---|---|
| 500 | 239 ms | 30 ms |
| 1000 | 1619 ms | 59 ms |
| 2000 | 13550 ms | 126 ms |
| 4000 | 109674 ms | 291 ms |
The generated RODL remained byte-identical.
We are not suggesting this proof-of-concept as the final implementation.
Our intention is simply to report what appears to be an algorithmic scalability issue and verify whether this matches your understanding of the current implementation.
If useful, we can also provide:
- the benchmark project;
- the profiling instrumentation;
- the experimental patch used during the investigation.
Thank you.