Een eerlijke load/save-benchmark voor PDF Library for Delphi meet LoadFromFile en SaveToFile met QueryPerformanceCounter, bewaart de ruwe ticks en de counterfrequentie, draait baseline en kandidaat in afwisselende A/B-, B/A-, A/B-paren, weigert te starten zolang de CPU-belasting boven 25% zit, verwerpt elk resultaat waarvan de range-tot-median-spreiding boven 15% uitkomt, en gooit elke timing weg waarvan de opgeslagen PDF de structurele, renderings- of semantische validatie niet doorstaat. Die lijst leest als bureaucratie tot de eerste keer dat een claim van "20% sneller" bij een herhaling verdampt. Hieronder staat hoe de specifieke corpus-probe en zijn vergelijkingsrunner daar terecht zijn gekomen, inclusief de run waarin de machine simpelweg te druk was om iets te meten en de harness dat terecht meldde
Waarom rapporteert een Delphi-PDF-benchmark nul seconden?
Een PDF-loadbenchmark rapporteert nul seconden wanneer zijn klok grover tikt dan de bewerking die hij meet, en GetTickCount64 is precies dat soort klok: hij geeft milliseconden terug, maar op Windows loopt hij alleen op wanneer de system timer-interrupt afgaat, doorgaans elke 15,6 ms. De FPC-port van de huge-file-benchmarkdemo in PDF Library for Delphi gebruikte hem omdat TStopwatch in die toolchain niet beschikbaar is, en hij registreert verstreken tijd op drie decimalen. Het laden van een kleine CAD-tekening of een kort getagd document is ruim binnen één timerstap klaar, dus de demo drukte soms 0.000 af voor een load die duidelijk echt werk deed
function ElapsedSeconds(StartTick: QWord): Double;
begin
Result:= (GetTickCount64- StartTick)/ 1000.0;
end;
// binnen de bewerkingslus
Lib:= TPDFlib.Create;
try
Lib.OnProgress:= Reporter.Progress;
Started:= GetTickCount64;
LoadCode:= Lib.LoadFromFile(InputFile, Password);
...
Een nul is erger dan een onnauwkeurig getal, want elke vergelijking die u erop bouwt deelt erdoor. De gepaarde vergelijkingsrunner behandelt elke arm met een nul-minimum als inconclusief met als reden "Zero duration prevents a meaningful ratio", wat de correcte weigering is, maar het betekent ook dat de demo-timings precies daar waar korte bestanden leven een meetgat achterlieten. Dezelfde demo installeert bovendien een OnProgress-callback, dus zijn timings bevatten callback-overhead die een zuivere load/save-meting niet zou mogen dragen, en gearchiveerde democijfers zijn niet uitwisselbaar met wat later ook maar gemeten wordt
LoadFromFile en SaveToFile timen met QueryPerformanceCounter
De specifieke console-probe, Tests/CorpusLoadSave.dpr, meet twee bewerkingen per invoerbestand met QueryPerformanceCounter: LoadFromFile plus het lezen van PageCount, en LoadFromFile plus PageCount plus SaveToFile. Elke bewerking krijgt een verse TPDFlib-instantie en geen progress-callback, en de constructor en destructor van de instantie vallen buiten de getimede regio, net als het CSV-schrijven en alle uitvoervalidatie. De counter wordt gelezen vlak vóór de load en vlak na de laatste library-aanroep, en LastErrorCode wordt pas na de tweede meting opgehaald
Lib:= TPDFlib.Create;
try
if not QueryPerformanceCounter(Started) then
raise Exception.Create('Performance counter unavailable');
Code:= Lib.LoadFromFile(WideString(SourceFile), '');
if Code= 1 then
begin
Pages:= Lib.PageCount;
if Save then Code:= Lib.SaveToFile(WideString(SavedFile));
end;
if not QueryPerformanceCounter(Finished) then
raise Exception.Create('Performance counter unavailable');
ErrorCode:= Lib.LastErrorCode;
finally
Lib.Free;
end;
Ticks:= Finished- Started;
if Ticks< 0 then
raise Exception.Create('Performance counter moved backwards');
De probe schrijft het ruwe tickaantal en de counterfrequentie naast de afgeleide seconden weg, opgemaakt met negen decimalen en een vaste . als decimaalteken, zodat iedereen het quotiënt uit de CSV kan narekenen in plaats van het te vertrouwen. Op de FPC Win64-build laadde het CAD-voorbeeld in 8.888 ticks bij 10.000.000 ticks per seconde, geregistreerd als 0.000888800 seconden — een waarneming die de oude timer naar nul zou hebben afgerond. De probe knipt opzettelijk geen korte waarden af, vervangt ze niet door een minimale duur en trekt geen geschatte timeroverhead af, en hij schrijft bij een falende library-aanroep nog steeds beide rijen weg met een exitcode ongelijk aan nul. Negen cijfers zijn wel geen nauwkeurigheid: meer geregistreerde precisie zegt niets over herhaalbaarheid, en ruisige of nul-waarnemingen moeten verderop in de keten nog steeds geweigerd worden
if not QueryPerformanceFrequency(Frequency) or (Frequency<= 0) then
raise Exception.Create('Performance counter frequency unavailable');
NumberFormat:= TFormatSettings.Create;
NumberFormat.DecimalSeparator:= '.';
...
Rows.Add(CSV(ExtractFileName(SourceFile))+ ','+ CSV(Operation)+ ','+
FormatFloat('0.000000000', Ticks/ Frequency, NumberFormat)+ ','+
IntToStr(Code)+ ','+ IntToStr(ErrorCode)+ ','+ IntToStr(Pages)+ ','+
IntToStr(Ticks)+ ','+ IntToStr(Frequency));
Wat maakt een timingvergelijking van PDF load/save betrouwbaar?
Een timingvergelijking tussen twee builds van PDF Library for Delphi is pas betrouwbaar wanneer startvolgorde, startvoorwaarden en spreiding allemaal gecontroleerd en geregistreerd worden, dus de vergelijkingsrunner plant minstens drie paren in de volgorde A/B, B/A, A/B. De baseline altijd eerst draaien geeft de kandidaat heimelijk een warmere filecache en een andere thermische toestand; de volgorde afwisselen spreidt die bias over beide armen in plaats van ze aan één toe te rekenen. Vóór elke arm hasht de runner het volledige invoerbestand met SHA-256, wat zowel verifieert dat er niets veranderd is als dezelfde bytes voor beide armen voorleest, en hij hasht de twee executables en de validatietools na elke run opnieuw, zodat een opnieuw gebouwde binary niet midden in een reeks kan glippen
Daarna bemonstert de runner het CPU-gebruik van de hele machine één keer per seconde en start hij de arm pas wanneer een sample op 25% of lager uitkomt, met hoogstens 30 seconden wachten voordat de poging als geweigerd geregistreerd wordt. Die gate bewaakt de startvoorwaarde en niets anders: hij isoleert de machine niet tijdens de run, en energiebeheer, thermische throttling, achtergrondwerk en OS-caching kunnen de cijfers nog steeds verschuiven. De tweede filter is daarom statistisch in de meest letterlijke zin. Per bewerking berekent de runner range gedeeld door mediaan voor de baseline-arm, de kandidaat-arm en de verdeling van gepaarde kandidaat/baseline-ratio's, en komt één van de drie boven 0.15 uit dan wordt het resultaat als noisy gelabeld in plaats van als bevinding gerapporteerd
Waarom bewijst een same-binary-controle herhaalbaarheid en niet snelheid?
Een same-binary-controle draait identieke executables als baseline en kandidaat, dus een ratio rond 1.0 kan alleen bewijzen dat de meetopzet zichzelf herhaalt; hij kan nooit laten zien dat een implementatie sneller werd. De eerste strikte controle op 2026-09-21 gebruikte de high-resolution FPC Win64-probe op een toegelaten getagde handleiding van 70 pagina's, en alle zes starts werden geweigerd omdat de CPU-samples tussen 26.5% en 93.8% zaten. Het rapport bevatte mislukkingen en geen totalen, en dat is precies de uitkomst die u wilt wanneer de machine druk is. Een hertest diezelfde dag met byte-identieke invoer, dezelfde probe-executable en onveranderde drempels accepteerde alle zes starts binnen 3 seconden; elke range-tot-median-spreiding viel tussen 0.019 en 0.054, en de ratio-medianen waren 1.0084 voor LoadFromFile en 0.9872 voor LoadFromFile + SaveToFile
Dat paar cijfers stelt een gekwalificeerd observatievenster vast en niets meer. Wanneer de twee binaries verschillen wordt een stabiele run als descriptive comparison gelabeld, met de expliciete noot dat de ratio's waarnemingen zijn, geen statistische significantie of een sneller-claim. De discipline telt het zwaarst wanneer u gerichte optimalisaties valideert zoals die in PDF Library for Delphi profileren en hot paths vervangen door hash-indexen worden beschreven: een profiler vertelt u waar de tijd heengaat, maar pas een gecontroleerde gepaarde run op echte documenten vertelt u of de wijziging het contact met de hele pipeline overleefde. Nog één grens die de moeite van hardop noemen waard is — normal-save omvat het laden, en de peak working set die de runner registreert geldt het hele proces, dus daarvan is geen byte geheugen toe te schrijven aan het opslaan alleen
Drie uitvoergates en een matrix van vier compilers
Geen enkele PDF Library for Delphi-timing telt tenzij het bestand dat hij produceerde drie onafhankelijke gates passeert, want een save die snel een kapotte PDF schrijft is geen snellere save. De benchmark controleert eerst dat beide bewerkingen 1 teruggaven en het toegelaten paginanaantal meldden, en valideert daarna de ene opgeslagen PDF in deze volgorde:
- Structuur: een onafhankelijke PDF-checker moet het opgeslagen bestand zonder fouten of waarschuwingen doorlaten
- Rendering: elke pagina wordt in zijn standaardtoestand gerenderd, en de set per-pagina image-SHA-256's moet exact matchen met de referentierendering van de toegelaten bron
- Nonvisuele semantiek: een aparte semantische vergelijking met de bron dekt geselecteerde eigenschappen die pixels niet kunnen tonen, waaronder optional-content- en meetstructuren binnen hun gedocumenteerde scope
Met die gates op hun plek draaide de volledige lokale corpusmatrix de probe op FPC Win32, FPC Win64, Delphi Win32 en Delphi Win64 over 12 toegelaten PDF's met 1.612 bronpagina's, wat 48 sample/target-paren en 6.448 gevalideerde uitvoerpagina's opleverde zonder geselecteerde semantische verschillen. Alle 96 bewerkingsmetingen hielden positieve ruwe counterwaarden over die consistent waren met hun gerapporteerde seconden, en die waarden worden opzettelijk niet samengevoegd tot een cross-compiler-snelheidstabel, want de matrix is functioneel bewijs, geen gecontroleerde vergelijking. Het load/save-pad claimt ook niet elke embedded image te decoderen, handtekeningen te valideren, XFA uit te voeren of PDF/UA te certificeren; moet u rendering-throughput beoordelen in plaats van load/save-kosten, dan zijn de concurrency-constraints in parallelle paginarendering en thread safety in PDF Library for Delphi het betere startpunt
De praktische les is kort: bewaar ruwe counters, wissel de volgorde af, bewaak de start, weiger ruisige spreiding, en time nooit een uitvoer die u niet hebt gevalideerd. Die regels zijn wat PDF Library for Delphi toestaat om "geen meetbare verandering" net zo vertrouwd te zeggen als "sneller", en dezelfde probe-source compileert onveranderd op Delphi en FPC voor Win32 en Win64. De library, zijn load/save-API en de ondersteunde compilers bekijkt u op de productpagina van PDF Library for Delphi