Artykuł techniczny

Buforowanie podzbiorów czcionek na dysku z HotPDF w Delphi

HotPDF potrafi trzymać podzbiory czcionek TrueType i OpenType na dysku i ponownie ich używać między dokumentami i między uruchomieniami procesu, więc partia renderująca dziesięć tysięcy wyciągów z tymi samymi trzema czcionkami tworzy podzbiory tych czcionek raz zamiast dziesięć tysięcy razy. Pamięć podręczna jest konfigurowana dwiema właściwościami, badana jednym rekordem i bezpieczna do pozostawienia włączonej: porażka pamięci podręcznej spada do normalnego subsetowania w pamięci i nigdy nie zatrzymuje produkcji dokumentu

Subsetowanie jest drogie z powodu. Zbudowanie podzbioru oznacza przejście domknięcia glifów, przepisanie loca i glyf, przebudowę cmap i hmtx oraz wyemitowanie mapowania CID, które PDF może adresować. Dla jednego dokumentu ten koszt znika w szumie. Dla serwera raportów produkującego dokumenty w pętli to często największy pojedynczy blok czasu CPU w przebiegu

Co czyni trafienie pamięci podręcznej możliwym

Cztery rzeczy muszą się zgadzać: treść czcionki, zestaw użytych glifów, tryb subsetowania i schemat pamięci podręcznej. Pomiń jedną, a HotPDF subsetuje od zera, ponieważ podzbiór jest ponownie używalny tylko wtedy, gdy byłby bajtowo identyczny tak czy inaczej

Zestaw glifów to warunek, który zaskakuje ludzi. Dwie faktury różniące się jednym nazwiskiem klienta używają różnych zestawów glifów, więc produkują różne podzbiory i różne wpisy pamięci podręcznej. Pamięć podręczna się opłaca, gdy dokumenty współdzielą repertuar glifów — wyciągi ze stałego szablonu, formularze, których dane zmienne są liczbowe, katalogi ciągnięte z jednej bazy produktów — i nie daje nic, gdy każdy dokument rysuje inny wycinek dużego króju CJK. Zmierz, zanim założysz, w którym przypadku jesteś

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;

Skąd wiesz, że pamięć podręczna cokolwiek robi?

GetFontSubsetCacheInfo zwraca dziewięć liczników, a stosunek dwóch pierwszych odpowiada na pytanie wprost. HitCount i MissCount dają wskaźnik trafień. WriteCount i EvictionCount pokazują, czy wpisy przeżywają wystarczająco długo, by zostać ponownie użyte, czy są wypychane przez budżet zbyt mały. CurrentBytes i FileCount raportują, co jest na dysku w tej chwili

Pozostałe trzy to te, na które warto alertować. CorruptCount liczy wpisy, które nie przeszły walidacji i zostały usunięte — kilka po nieczystym zamknięciu jest normalne, stały strumień oznacza, że pamięć jest niewiarygodna. RejectedCount liczy wpisy odmówione przed użyciem. WriteFailureCount liczy wpisy, których w ogóle nie dało się zapisać, co zazwyczaj oznacza problem z uprawnieniami folderu, a nie cokolwiek o czcionkach. Żadne z tych trzech nie zatrzymuje generowania dokumentu, co jest dokładnie powodem, dla którego musisz na nie patrzeć: pamięć podręczna, która po cichu nigdy nie zapisuje, wygląda z zewnątrz tak samo jak działająca, z wyjątkiem rachunku za CPU

Wykluczanie, budżety i moment, w którym jeden zmniejszasz

FontSubsetCacheMaxBytes domyślnie wynosi 268435456 bajtów, czyli 256 MiB, i może być obniżone w czasie działania. Obniżenie wyzwala natychmiastowe wykluczanie najdawniej używanych zamiast czekać na kolejny zapis, więc usługa reagująca na presję dysku może zwolnić miejsce w momencie, w którym zdecyduje, nie w jakimś późniejszym, którego nie kontroluje

Ustawienie FontSubsetCacheFolder na pusty ciąg znaków wyłącza warstwę dyskową bez czyszczenia tego, co już zapisane, i bez zmiany ani jednego bajtu wyjścia czcionki. To jest właściwość, po którą sięgnąć, gdy chcesz odizolować pamięć podręczną podczas rozwiązywania problemów: wyłącz ją, uruchom tę samą partię i porównaj wyprodukowane PDF-y. Powinny być identyczne, ponieważ pamięć podręczna przechowuje wynik, nie politykę

Co robi pamięć podręczna, gdy wpis jest uszkodzony

Usuwa go i subsetuje normalnie. Zniekształcone lub ucięte wpisy są odrzucane, zanim podzbiór dotrze do strumienia PDF, co jest częścią projektu najważniejszą: uszkodzony wpis pamięci podręcznej, który trafiłby do dokumentu, wyprodukowałby PDF ze zepsutym programem czcionki, a ta porażka ujawniłaby się daleko od swojej przyczyny — w viewerze, na maszynie klienta, tygodnie później

Zapisy są atomowe, więc czytelnik nigdy nie obserwuje wpisu w połowie zapisanego, a awaria w trakcie zapisu zostawia pamięć spójną zamiast zatrutą. Kompaktowe wpisy podzbiorów zachowują dane ponownego mapowania CID, których wymagają słowniki czcionek PDF/A, więc buforowany podzbiór to wciąż zgodny podzbiór — wyjście archiwalne nie musi omijać pamięci podręcznej, by zostać poprawnym

// 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');

Gdzie umieścić folder w realnym wdrożeniu

Trzy właściwości o tym decydują: folder musi być zapisywalny przez konto, na którym działa usługa, powinien leżeć na lokalnej pamięci zamiast na udziale sieciowym i nie powinien być wewnątrz katalogu, który krok wdrożenia czyści. Pamięć podręczna na udziale zamienia każde pudło w podróż w obie strony i każde trafienie w dwie; pamięć podręczna pod folderem aplikacji, który instalator odtwarza, to pamięć podręczna startująca na zimno po każdej aktualizacji

Dla usług wieloinstancyjnych daj każdej instancji własny folder, chyba że potwierdziłeś, że pamięć obsługuje współbieżną atomową wymianę tak, jak oczekujesz. Koszt zduplikowanego wpisu to jeden dodatkowy przebieg subsetowania; koszt debugowania wyścigu współdzielonej pamięci to popołudnie

Kiedy sięgnąć po coś innego

Pamięć podręczna redukuje powtórzoną pracę. Nie redukuje pracy pierwszego dokumentu i nie pomaga obciążeniu, którego zestawy glifów nigdy się nie powtarzają. Jeśli Twoje wyjście jest zdominowane przez jeden ogromny krój CJK użyty na nieprzewidywalnym tekście, skuteczniejszą dźwignią jest samo domknięcie subsetowania — które glify zostają wciągnięte i dlaczego — omówione w notatkach o domknięciu podzbioru czcionki i kształtowaniu glifów. Jeśli Twoja partia jest wolna z powodów, które okazują się wcale nie czcionkami, przewodnik po wyjściu raportu z czcionkami i obrazami pokazuje, gdzie zwykle idzie reszta czasu, a analiza przypadku błędu kolejności podzbioru czcionek w EndDoc przypomina, że poprawność subsetowania i szybkość subsetowania to osobne problemy

HotPDF to natywny komponent PDF VCL dla Delphi i C++Builder, a pamięć podręczna podzbiorów jest częścią biblioteki zamiast usługą dodatkową, więc serwer raportów dostaje ją ustawieniem jednej ścieżki folderu — zobacz stronę komponentu HotPDF, by poznać pełną listę funkcji czcionkowych i wydajnościowych