Et ærligt load/save-benchmark for PDF Library for Delphi tider LoadFromFile og SaveToFile med QueryPerformanceCounter, gemmer de rå ticks og counter-frekvensen, kører baseline og kandidat i alternerende A/B-, B/A-, A/B-par, nægter at starte, mens CPU-belastningen ligger over 25%, afviser ethvert resultat, hvis range-to-median-spread overstiger 15%, og smider enhver timing væk, hvis gemte PDF ikke består strukturel, renderings- eller semantisk validering. Listen lyder som bureaukrati, indtil første gang et "20% hurtigere"-claim fordamper ved en genkørsel. Det følgende er, hvordan den dedikerede corpus-probe og dens comparison-runner kom dertil, inklusive den kørsel, hvor maskinen simpelthen var for optaget til at måle noget, og harnessen korrekt sagde netop det
Hvorfor rapporterer et Delphi PDF-benchmark nul sekunder?
Et PDF-load-benchmark rapporterer nul sekunder, når dets ur tick'er mere groft end den operation, det måler, og GetTickCount64 er præcis dén slags ur: det returnerer millisekunder, men på Windows skrider det kun frem, når systemets timer-interrupt udløses, typisk hver 15,6 ms. FPC-porten af huge-file-benchmark-demoen i PDF Library for Delphi brugte den, fordi TStopwatch ikke er tilgængelig i den toolchain, og den registrerer forløbet tid med tre decimaler. Indlæsning af en lille CAD-tegning eller et kort tagget dokument afsluttes godt inden for ét timer-trin, så demoen udskrev nogle gange 0.000 for en indlæsning, som klart gjorde rigtigt arbejde
function ElapsedSeconds(StartTick: QWord): Double;
begin
Result:= (GetTickCount64- StartTick)/ 1000.0;
end;
// inde i operationsløkken
Lib:= TPDFlib.Create;
try
Lib.OnProgress:= Reporter.Progress;
Started:= GetTickCount64;
LoadCode:= Lib.LoadFromFile(InputFile, Password);
...
Et nul er værre end et upræcist tal, fordi hver sammenligning, du bygger på det, dividerer med det. Den parrede comparison-runner behandler enhver arm med et nul-minimum som inkonklusiv med begrundelsen "nul varighed forhindrer et meningsfuldt forholdstal", hvilket er den korrekte nægtelse, men det betyder også, at demo-timingerne efterlod et målehul præcis der, hvor de korte filer lever. Samme demo installerer desuden en OnProgress-callback, så dens timings inkluderer callback-overhead, som en ren load/save-måling ikke bør bære, og arkiverede demo-tal er ikke udskiftelige med noget, der er målt senere
Timing af LoadFromFile og SaveToFile med QueryPerformanceCounter
Den dedikerede console-probe, Tests/CorpusLoadSave.dpr, måler to operationer pr. inputfil med QueryPerformanceCounter: LoadFromFile plus læsning af PageCount, og LoadFromFile plus PageCount plus SaveToFile. Hver operation får en frisk TPDFlib-instans og ingen progress-callback, og instansens constructor og destructor står uden for det timede område, ligesom CSV-skrivning og al output-validering. Counteren læses umiddelbart før indlæsningen og umiddelbart efter det sidste bibliotekskald, og LastErrorCode hentes først efter den anden aflæsning
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 det rå tick-antal og counter-frekvensen ved siden af de afledte sekunder, formateret med ni decimaler og en fast . som decimalseparator, så enhver kan genberegne kvotienten fra CSV-filen i stedet for at stole på den. På FPC Win64-builden loadede CAD-eksemplet på 8.888 ticks ved 10.000.000 ticks i sekundet, registreret som 0.000888800 sekunder — en observation, som det gamle ur ville have rundet til nul. Proben klipper bevidst ikke korte værdier, substituerer ikke en minimumsvarighed og trækker ikke et estimeret timer-overhead fra, og den skriver stadig begge rækker med en ikke-nul exitkode, når et bibliotekskald fejler. Ni cifre er dog ikke det samme som accuracy: mere registreret præcision siger intet om reproducerbarhed, og støjende eller nul-observationer skal stadig afvises downstream
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));
Hvad gør en PDF load/save-timingssammenligning troværdig?
En timingssammenligning mellem to builds af PDF Library for Delphi er kun troværdig, når startrækkefølge, startbetingelser og spread alle er kontrolleret og registreret, så comparison-runneren planlægger mindst tre par i A/B-, B/A-, A/B-rækkefølge. Altid at køre baselinen først giver i stilhed kandidaten en varmere file-cache og en anden termisk tilstand; at alterne rækkefølgen fordeler den bias på begge arme i stedet for at kreditere den til én. Før hver arm hasher runneren den komplette inputfil med SHA-256, hvilket både verificerer, at intet er ændret, og pre-læser de samme bytes til hver af armen, og den hasher de to eksekverbare filer og valideringsværktøjerne igen efter hver kørsel, så en rekompileret binary ikke kan snige sig ind midt i en serie
Runneren sampler derefter maskinomfattende CPU-udnyttelse én gang i sekundet og starter armen kun, når en sample falder til 25% eller derunder, med højest 30 sekunders ventetid, før forsøget registreres som afvist. Den gate styrer startbetingelsen og intet andet: den isolerer ikke maskinen under kørslen, og strømtilstand, termisk throttling, baggrundsarbejde og OS-caching kan stadig flytte tallene. Derfor er det andet filter statistisk i sin mest bogstavelige forstand. For hver operation beregner runneren range divideret med median for baseline-armen, kandidat-armen og fordelingen af parrede kandidat/baseline-ratioer, og hvis en af de tre overstiger 0,15, mærkes resultatet som noisy i stedet for at blive rapporteret som et fund
Hvorfor beviser en same-binary-kontrol gentagelighed, ikke hastighed?
En same-binary-kontrol kører identiske eksekverbare filer som baseline og kandidat, så en ratio nær 1,0 kan kun bevise, at målesetuppet gentager sig selv; den kan aldrig vise, at en implementering blev hurtigere. Den første strikse kontrol 2026-09-21 brugte high-resolution FPC Win64-proben mod en godkendt tagget guide på 70 sider, og alle seks starts blev afvist, fordi CPU-samplerne lå mellem 26,5% og 93,8%. Rapporten indeholdt fejl og ingen aggregater, hvilket præcis er det udfald, du ønsker, når maskinen er optaget. Et genforsøg samme dag med byte-identiske inputs, samme probe-eksekverbare og uændrede tærskler accepterede alle seks starts inden for 3 sekunder; ethvert range-to-median-spread landede mellem 0,019 og 0,054, og ratio-medianerne var 1,0084 for LoadFromFile og 0,9872 for LoadFromFile + SaveToFile
Det talpar etablerer et kvalificeret observationsvindue og ikke mere. Når de to binaries adskiller sig, mærkes en stabil kørsel som en deskriptiv sammenligning, med den eksplicitte note, at ratioerne er observationer, ikke statistisk signifikans eller et speedup-claim. Disciplinen betyder mest, når du validerer målrettede optimeringer som dem, der beskrives i profiling af PDF Library for Delphi og udskiftning af hot paths med hash-indekser: en profiler fortæller dig, hvor tiden går, men kun en kontrolleret parret kørsel på rigtige dokumenter fortæller dig, om ændringen overlevede kontakten med hele pipelinen. Én grænse mere, der er værd at sige højt — normal-save inkluderer indlæsning, og det peak working set, runneren registrerer, er procesomfattende, så ingen af det er hukommelse, der alene kan tilskrives gemningen
Tre output-gates og en fire-compiler-matrix
Ingen PDF Library for Delphi-timing tæller, medmindre filen, den producerede, består tre uafhængige gates, fordi en gemning, der hurtigt skriver en ødelagt PDF, ikke er en hurtigere gemning. Benchmarket tjekker først, at begge operationer returnerede 1 og rapporterede det godkendte sideantal, og validerer derefter den enkelte gemte PDF i denne rækkefølge:
- Struktur: en uafhængig PDF-checker skal bestå den gemte fil uden fejl eller advarsler
- Rendering: hver side renderes i sin standardtilstand, og per-side image SHA-256-sættet skal matche referencerenderingen af den godkendte kilde præcist
- Nonvisuel semantik: en separat semantisk sammenligning mod kilden dækker udvalgte egenskaber, som pixels ikke kan vise, herunder optional-content- og målestrukturer inden for deres dokumenterede omfang
Med de gates på plads kørte den fulde lokale corpus-matrix proben på FPC Win32, FPC Win64, Delphi Win32 og Delphi Win64 over 12 godkendte PDF'er med 1.612 kildesider, hvilket gav 48 sample/target-par og 6.448 validerede outputsider uden udvalgte semantiske forskelle. Alle 96 operationsmålinger bevarede positive rå counter-værdier, der stemte med deres rapporterede sekunder, og de værdier aggregeres bevidst ikke til en cross-compiler-hastighedstabel, fordi matrixen er funktionelt bevis snarere end en kontrolleret sammenligning. Load/save-stien påstår heller ikke at dekode alle embeddede billeder, validere signaturer, eksekvere XFA eller certificere PDF/UA; hvis du skal bedømme rendering-throughput frem for load/save-omkostninger, er concurrency-begrænsningerne i parallel side-rendering og thread safety i PDF Library for Delphi det bedre udgangspunkt
Det praktiske takeaway er kort: gem de rå counters, alternér rækkefølgen, gate starten, afvis noisy spreads, og tim aldrig et output, du ikke har valideret. De regler er, hvad der gør, at PDF Library for Delphi kan sige "ingen målbar ændring" lige så overbevisende som "hurtigere", og samme probe-kilde kompilerer uændret på Delphi og FPC for Win32 og Win64. Du kan gennemse biblioteket, dets load/save-API og de understøttede compilers på produktsiden for PDF Library for Delphi