Technický článek

Ukládání podmnožin písem na disk s HotPDF v Delphi

HotPDF umí uchovávat podmnožiny písem TrueType a OpenType na disku a znovu je využívat napříč dokumenty i napříč běhy procesu, takže dávka, která renderuje deset tisíc výpisů se stejnými třemi písmy, vytvoří jejich podmnožiny jednou místo deset tisíckrát. Mezipaměť se konfiguruje dvěma vlastnostmi, prozkoumává jedním záznamem a je bezpečné ji nechat zapnutou: selhání mezipaměti spadne zpět na běžné in-memory subsetting a nikdy nezastaví produkci dokumentu

Subsetting je náročné, a to z dobrého důvodu. Postavit podmnožinu znamená projít glyph closure, přepsat loca a glyf, přestavět cmap a hmtx a emitovat CID mapování, kterou PDF dokáže adresovat. U jednoho dokumentu se ten náklad ztratí v šumu. U report serveru produkujícího dokumenty ve smyčce to bývá často největší jediný blok CPU času v běhu

Co umožňuje cache hit

Musí se shodovat čtyři věci: obsah písma, sada použitých glyphů, režim subsettingu a schéma mezipaměti. Minete-li jedinou, HotPDF vytvoří podmnožinu od nuly, protože podmnožina je znovupoužitelná jen tehdy, kdyby byla stejná bajt po bajtu

Sada glyphů je podmínka, která lidi překvapí. Dvě faktury lišící se jediným jménem zákazníka používají různé sady glyphů, a tedy produkují různé podmnožiny a různé položky mezipaměti. Mezipaměť se vyplatí, když dokumenty sdílejí glyphový repertoár — výpisy z pevné šablony, formuláře jejichž proměnlivá data jsou číselná, katalogy tažené z jedné produktové databáze — a nevyplatí nic, když každý dokument kreslí jiný výřez velkého CJK písma. Předpokládejte, který případ je váš, až po změření

var
  Pdf: THotPDF;
  Info: THPDFFontSubsetCacheInfo;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.EnableFontSubsetting := True;
    Pdf.FontSubsetCacheFolder := 'C:\ProgramData\Reports\fontcache';
    Pdf.FontSubsetCacheMaxBytes := 64 * 1024 * 1024;   // 64 MiB, default is 256
    // ... generate the batch ...
    Info := Pdf.GetFontSubsetCacheInfo;
    LogFmt('subset cache: %d hits, %d misses, %d bytes in %d files',
      [Info.HitCount, Info.MissCount, Info.CurrentBytes, Info.FileCount]);
  finally
    Pdf.Free;
  end;
end;

Odkud víte, že mezipaměť něco dělá?

GetFontSubsetCacheInfo vrací devět čítačů a poměr mezi prvními dvěma odpovídá na otázku přímo. HitCount a MissCount dávají hit rate. WriteCount a EvictionCount ukazují, zda položky přežijí dost dlouho na znovupoužití, nebo jsou vytlačovány rozpočtem, který je příliš malý. CurrentBytes a FileCount hlásí, co je právě teď na disku

Zbylé tři jsou ty, na které stojí za to nastavit alert. CorruptCount počítá položky, které selhaly ve validaci a byly odstraněny — pár po nečistém vypnutí je normální, ustálený proud znamená, že úložiště je nespolehlivé. RejectedCount počítá položky odmítnuté před použitím. WriteFailureCount počítá položky, které nešlo vůbec zapsat, což obvykle znamená problém s oprávněními na složce spíše než cokoli o písmech. Ani jedna z těchto tří nezastaví generování dokumentu, a právě proto se na ně musíte podívat: mezipaměť, která potichu nikdy nezapisuje, vypadá zvenčí stejně jako mezipaměť, která funguje, jen s rozdílem v účtu za CPU

Evikce, rozpočty a okamžik, kdy jeden zeštíhlíte

FontSubsetCacheMaxBytes má výchozích 268435456 bytů, tedy 256 MiB, a lze jej za běhu snížit. Snížení spouští okamžitou evikci least-recently-used místo aby čekala na další zápis, takže služba reagující na tlak na disk uvolní prostor ve chvíli, kdy se tak rozhodne, ne v nějakém pozdějším, který neřídí

Nastavení FontSubsetCacheFolder na prázdný řetězec vypne diskovou vrstvu bez vyčištění už uloženého a beze změny jediného bajtu výstupu písma. To je vlastnost, po které sáhněte, když chcete mezipaměť izolovat během řešení potíží: vypněte ji, spusťte stejnou dávku a porovnejte vyprodukovaná PDF. Měla by být totožná, protože mezipaměť ukládá výsledek, nikoli politiku

Co mezipaměť udělá, když je položka poškozená

Odstraní ji a vytvoří podmnožinu normálně. Zkomolené nebo useknuté položky jsou odmítnuty dříve, než podmnožina může dosáhnout PDF streamu, což je ta část návrhu, na které záleží nejvíc: poškozená položka mezipaměti, byť se dostala do dokumentu, by vyrobila PDF s rozbitým programem písma a to selhání by se projevilo daleko od své příčiny — v prohlížeči, na stroji zákazníka, o týdny později

Zápisy jsou atomické, takže čtenář nikdy nevidí napůl zapsanou položku a pád uprostřed zápisu zanechá mezipaměť konzistentní místo otrávené. Kompaktní položky podmnožin si ponechávají data CID remapingu, která vyžadují slovníky písem PDF/A, takže cached subset je stále konformní podmnožinou — archivační výstup nemusí mezipaměť obcházet, aby zůstal platný

// Reset the disk tier after a font upgrade or a schema change
Pdf.ClearFontSubsetCache;

// Or move it somewhere writable and let the budget apply immediately
Pdf.SetFontSubsetCacheFolder('D:\cache\fonts');

Kam umístit složku ve skutečném nasazení

Tři vlastnosti to rozhodují: složka musí být zapisovatelná účtem, pod kterým služba běží, měla by ležet na lokálním úložišti spíše než na síťové sdílené složce a neměla by být uvnitř adresáře, který krok nasazení smaže. Mezipaměť na sdílené složce mění každý miss v round trip a každý hit ve dva; mezipaměť pod složkou aplikace, kterou instalátor znovuvytváří, je mezipaměť, která startuje studená po každé aktualizaci

U služeb s více instancemi dejte každé instanci vlastní složku, pokud jste nepotvrdili, že úložiště zvládá souběžné atomické nahrazování tak, jak čekáte. Cena duplikované položky je jeden extra průběh subsettingu; cena ladění race ve sdílené mezipaměti je odpoledne

Kdy sáhnout po něčem jiném

Mezipaměť snižuje opakovanou práci. Nesnižuje práci prvního dokumentu a nepomáhá workloadu, jehož sady glyphů se nikdy neopakují. Pokud dominuje váš výstup jedno obrovské CJK písmo použité napříč nepředvídatelným textem, účinnější pákou je samotný closure subsettingu — které glyphy se stahují a proč — což pokrývají poznámky k closure podmnožiny písma a shaping glyphů. Pokud je dávka pomalá z důvodů, které nakonec nejsou písma vůbec, průvodce výstupem sestavy s písmy a obrázky ukazuje, kam obvykle odchází zbytek času, a případová studie bugu řazení podmnožiny písma při EndDoc připomíná, že správnost subsettingu a rychlost subsettingu jsou oddělené problémy

HotPDF je nativní VCL PDF komponenta pro Delphi a C++Builder a subset cache je součástí knihovny, nikoli doplňkovou službou, takže ji report server získá nastavením jediné cesty ke složce — viz stránka komponenty HotPDF pro úplný seznam funkcí písem a výkonu