En ærlig last/lagre-benchmark for PDF Library for Delphi måler LoadFromFile og SaveToFile med QueryPerformanceCounter, beholder rå tikker og tellerfrekvens, kjører baseline og kandidat i alternerende A/B-, B/A-, A/B-par, nekter å starte mens CPU-belastningen ligger over 25 %, avviser ethvert resultat der spredningen range-til-median overstiger 15 %, og kaster hver måling hvis lagrede PDF svikter på strukturell, renderings- eller semantisk validering. Listen leser seg ut som byråkrati til første gang en «20 % raskere»-påstand fordamper ved en ny kjøring. Det som følger, er hvordan den dedikerte corpus-proben og dens sammenligningsrunner kom dit, inkludert kjøringen der maskinen rett og slett var for opptatt til å måle noe som helst, og testriggen sa det korrekt
Hvorfor rapporterer en Delphi PDF-benchmark null sekunder?
En PDF-lastebenchmark rapporterer null sekunder når klokken tikker mere grovkornet enn operasjonen den måler, og GetTickCount64 er nøyaktig den slags klokke: den returnerer millisekunder, men på Windows flytter den seg bare når system-timer-avbruddet utløses, vanligvis hvert 15,6 ms. FPC-porten av huge-file-benchmark-demoen i PDF Library for Delphi brukte den fordi TStopwatch ikke er tilgjengelig i den verktøykjeden, og den registrerer forløpt tid med tre desimaler. Å laste en liten CAD-tegning eller et kort tagget dokument fullføres godt innenfor ett timersteg, så demoen skrev noen ganger ut 0.000 for en lasting som åpenbart gjorde ekte arbeid
function ElapsedSeconds(StartTick: QWord): Double;
begin
Result:= (GetTickCount64- StartTick)/ 1000.0;
end;
// inne i operasjonsløkken
Lib:= TPDFlib.Create;
try
Lib.OnProgress:= Reporter.Progress;
Started:= GetTickCount64;
LoadCode:= Lib.LoadFromFile(InputFile, Password);
...
En null er verre enn et upresist tall, fordi hver sammenligning du bygger på den, deler på den. Den parrede sammenligningsrunneren behandler enhver arm med null som minimum inkonklusiv med begrunnelsen «Zero duration prevents a meaningful ratio», som er den korrekte nektelsen, men det betyr også at demo-målingene etterlot et målegap nøyaktig der korte filer lever. Samme demo installerer også en OnProgress-callback, så målingene dens inkluderer callback-overhead som en ren last/lagre-måling ikke bør bære, og arkiverte demo-tall er ikke utskiftbare med noe målt senere
Måling av LoadFromFile og SaveToFile med QueryPerformanceCounter
Den dedikerte konsollproben, Tests/CorpusLoadSave.dpr, måler to operasjoner per inputfil med QueryPerformanceCounter: LoadFromFile pluss lesing av PageCount, og LoadFromFile pluss PageCount pluss SaveToFile. Hver operasjon får en fersk TPDFlib-instans og ingen progress-callback, og instansens konstruktør og destruktor ligger utenfor det målte området, det gjør også CSV-skriving og all utdatavalidering. Telleren leses umiddelbart før lasting og umiddelbart etter siste bibliotekkall, og LastErrorCode hentes først etter den andre avlesningen
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');
Proben skriver rå tikktall og tellerfrekvens ved siden av de utledede sekundene, formatert med ni desimaler og et fast . som desimalskiller, så hvem som helst kan regne ut kvotienten fra CSV-en i stedet for å stole på den. På FPC Win64-bygget lastet CAD-utvalget på 8 888 tikker ved 10 000 000 tikker per sekund, registrert som 0.000888800 sekunder — en observasjon den gamle timeren ville ha rundet av til null. Proben klipper med vilje ikke korte verdier, erstatter ikke en minimumsvarighet og trekker ikke fra et estimert timer-overhead, og den skriver fortsatt begge rader med en exitkode ulik null når et bibliotekkall feiler. Ni sifre er likevel ikke nøyaktighet: mer registrert presisjon sier ingenting om repeterbarhet, og støyete eller null-observasjoner må fortsatt avvises lenger ned i kjeden
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));
Hva gjør en sammenligning av PDF last/lagre-målinger pålitelig?
En målingssammenligning mellom to bygg av PDF Library for Delphi er bare pålitelig når startrekkefølge, startbetingelser og spredning alle er kontrollert og registrert, så sammenligningsrunneren planlegger minst tre par i A/B-, B/A-, A/B-rekkefølge. Å alltid kjøre baselinen først, gir i stillhet kandidaten en varmere filcache og en annen termisk tilstand; å alternere rekkefølgen sprer den skjevheten over begge armer i stedet for å kreditere den til én. Før hver arm hasher runneren den komplette inputfilen med SHA-256, som både verifiserer at ingenting er endret og forleser de samme bytene for hver arm, og den hasher de to kjørbare filene og valideringsverktøyene på nytt etter hver kjøring, så en rekompilert binærfil ikke kan snike seg inn midt i en serie
Runneren sampler deretter CPU-utnyttelse på maskinnivå én gang per sekund og starter armen bare når en sample faller til 25 % eller under, med maks 30 sekunders venting før forsøket registreres som avvist. Den porten kontrollerer startbetingelsen og ingenting annet: den isolerer ikke maskinen under kjøringen, og strømtilstand, termisk throttling, bakgrunnsarbeid og OS-caching kan fortsatt flytte tallene. Derfor er det andre filteret statistisk i sin enkleste forstand. For hver operasjon beregner runneren range delt på median for baseline-armen, kandidat-armen og fordelingen av parrede kandidat/baseline-ratioer, og hvis noen av de tre overstiger 0,15, merkes resultatet som noisy i stedet for å rapporteres som et funn
Hvorfor beviser en same-binary-kontroll repeterbarhet, ikke hastighet?
En same-binary-kontroll kjører identiske kjørbare filer som baseline og kandidat, så en ratio nær 1,0 kan bare bevise at måleoppsettet gjentar seg selv; den kan aldri vise at en implementasjon ble raskere. Den første strenge kontrollen 2026-09-21 brukte FPC Win64-proben med høy oppløsning mot en godkjent 70-siders tagget veiledning, og alle seks startene ble avvist fordi CPU-sampene spente fra 26,5 % til 93,8 %. Rapporten inneholdt feil og ingen aggregater, som er nøyaktig det utfallet du vil ha når maskinen er opptatt. Et nytt forsøk samme dag med byte-identiske input, samme probe-kjørbar og uendrete terskeler aksepterte alle seks starter på under 3 sekunder; hver range-til-median-spredning landet mellom 0,019 og 0,054, og ratio-medianene var 1,0084 for LoadFromFile og 0,9872 for LoadFromFile + SaveToFile
Det tallparet etablerer et kvalifisert observasjonsvindu og ingenting mer. Når de to binærfilene er forskjellige, merkes en stabil kjøring som en beskrivende sammenligning, med den eksplisitte merknaden at ratioene er observasjoner, ikke statistisk signifikans eller en hastighetspåstand. Disiplinen betyr mest når du validerer målrettede optimaliseringer som de som beskrives i profilering av PDF Library for Delphi og utskifting av hot paths med hash-indekser: en profiler forteller deg hvor tiden går, men bare en kontrollert parret kjøring på ekte dokumenter forteller deg om endringen overlevde kontakten med hele pipelinen. En grense til verdt å si høyt — normal-save inkluderer lasting, og peak working set som runneren registrerer, er prosessomfattende, så ingenting av det er minne som kan tilskrives lagring alene
Tre utdataporter og en fire-kompilator-matrise
Ingen måling i PDF Library for Delphi teller med mindre filen den produserte, passerer tre uavhengige porter, for en lagring som raskt skriver en ødelagt PDF, er ikke en raskere lagring. Benchmarken sjekker først at begge operasjoner returnerte 1 og rapporterte det godkjente sideantallet, og validerer deretter den enkelt lagrede PDF-en i denne rekkefølgen:
- Struktur: en uavhengig PDF-sjekker må godta den lagrede filen uten feil eller advarsler
- Rendering: hver side rendres i sin standardtilstand, og per-side bilde-SHA-256-settet må eksakt matche referanserenderingen av den godkjente kilden
- Ikke-visuell semantikk: en egen semantisk sammenligning mot kilden dekker utvalgte egenskaper pikslene ikke kan vise, inkludert optional content- og målestrukturer innenfor deres dokumenterte omfang
Med de portene på plass, kjørte den fulle lokale corpus-matrisen proben på FPC Win32, FPC Win64, Delphi Win32 og Delphi Win64 over 12 godkjente PDF-er med 1 612 kildesider, noe som ga 48 utvalg/mål-par og 6 448 validerte utdatasider uten utvalgte semantiske forskjeller. Alle 96 operasjonsmålingene beholdt positive rå tellerverdier konsistente med de rapporterte sekundene, og disse verdiene aggregeres med vilje ikke inn i en krysskompilator-hastighetstabell, fordi matrisen er funksjonell dokumentasjon snarere enn en kontrollert sammenligning. Last/lagre-stien hevder heller ikke å dekode hvert innebygd bilde, validere signaturer, kjøre XFA eller sertifisere PDF/UA; hvis du må bedømme renderingsgjennomstrømning snarere enn last/lagre-kostnad, er samtidighetsbegrensningene i parallell side-rendering og trådsikkerhet i PDF Library for Delphi det bedre utgangspunktet
Det praktiske læringspunktet er kort: behold rå tellere, alterner rekkefølgen, port startbetingelsen, nekt støyete spredninger, og mål aldri et resultat du ikke har validert. De reglene er det som lar PDF Library for Delphi si «ingen målbar endring» like trygt som «raskere», og samme probe-kildekode kompileres uendret på Delphi og FPC for Win32 og Win64. Du kan se gjennom biblioteket, dets last/lagre-API og de støttede kompilatorene på produktsiden for PDF Library for Delphi