HotPDF는 TrueType과 OpenType 폰트 서브셋을 디스크에 보관하고 문서와 프로세스 실행을 가로질러 재사용할 수 있으므로, 동일한 세 개의 폰트로 1만 건의 명세서를 렌더링하는 배치는 그 폰트들을 1만 번이 아니라 한 번만 서브세팅합니다. 캐시는 두 프로퍼티로 구성되고 하나의 레코드로 검사되며, 켜둔 채로 두어도 안전합니다. 캐시 실패는 일반적인 인메모리 서브세팅으로 폴백하며 문서 생성을 결코 멈추지 않습니다
서브세팅은 이유가 있어 비용이 큽니다. 서브셋을 구성한다는 것은 글리프 클로저를 따라가고, loca와 glyf를 다시 쓰고, cmap과 hmtx를 재구성하고, PDF가 주소 지정할 수 있는 CID 매핑을 내보내는 것을 의미합니다. 하나의 문서에서는 그 비용이 노이즈 속에 사라집니다. 루프 안에서 문서를 생산하는 보고서 서버에서는 실행 중 가장 큰 단일 CPU 블록인 경우가 흔합니다
캐시 히트를 가능하게 만드는 것
네 가지가 일치해야 합니다. 폰트 콘텐츠, 사용된 글리프 집합, 서브셋 모드, 캐시 스키마입니다. 어느 하나라도 빗나가면 HotPDF는 처음부터 서브세팅하는데, 서브셋은 어차피 바이트 단위로 동일했을 때만 재사용 가능하기 때문입니다
글리프 집합은 사람들을 놀라게 하는 조건입니다. 단일 고객 이름만 다른 두 청구서는 다른 글리프 집합을 사용하며, 따라서 다른 서브셋과 다른 캐시 항목을 만듭니다. 캐시는 문서들이 글리프 레퍼토리를 공유할 때, 즉 고정 템플릿의 명세서, 가변 데이터가 숫자인 양식, 하나의 제품 데이터베이스에서 뽑은 카탈로그에서 보상하고, 모든 문서가 큰 CJK 서체의 다른 조각을 그릴 때는 아무것도 보상하지 않습니다. 어느 쪽 상황인지 가정하기 전에 측정하십시오
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;
캐시가 제대로 동작하는지 어떻게 아는가
GetFontSubsetCacheInfo는 아홉 개의 카운터를 반환하며, 처음 두 개 사이의 비율이 질문에 직접 답합니다. HitCount와 MissCount가 히트율을 줍니다. WriteCount와 EvictionCount는 항목이 재사용될 만큼 충분히 살아남는지, 아니면 너무 작은 예산에 밀려나는지를 보여줍니다. CurrentBytes와 FileCount는 지금 디스크에 있는 것을 보고합니다
나머지 세 개는 경고할 만한 것입니다. CorruptCount는 검증에 실패하여 제거된 항목을 셉니다. 깨끗하지 않은 종료 이후 몇 개는 정상이지만, 꾸준한 흐름은 저장소가 신뢰할 수 없음을 뜻합니다. RejectedCount는 사용 전에 거부된 항목을 셉니다. WriteFailureCount는 전혀 기록할 수 없었던 항목을 세며, 이것은 보통 폰트에 대한 무언가가 아니라 폴더의 권한 문제를 뜻합니다. 셋 모두 문서 생성을 멈추지 않으며, 바로 그래서 여러분이 살펴봐야 합니다. 조용히 결코 기록하지 않는 캐시는 CPU 청구서를 빼면 밖에서 보기에 작동하는 캐시와 동일하게 보입니다
제거, 예산, 예산을 줄이는 순간
FontSubsetCacheMaxBytes의 기본값은 268435456바이트, 즉 256 MiB이며 런타임에 낮출 수 있습니다. 낮추면 다음 기록을 기다리는 대신 즉각적인 최근 최소 사용 제거를 유발하므로, 디스크 압력에 반응하는 서비스가 통제하지 못하는 미래 시점이 아니라 결정하는 순간에 공간을 해제할 수 있습니다
FontSubsetCacheFolder를 빈 문자열로 설정하면 이미 저장된 것을 지우지 않고 폰트 출력의 단일 바이트도 바꾸지 않은 채 디스크 계층을 비활성화합니다. 이것이 트러블슈팅 중에 캐시를 격리하고 싶을 때 손 뻗는 프로퍼티입니다. 끄고, 같은 배치를 실행하고, 생산된 PDF를 비교하십시오. 캐시는 정책이 아니라 결과를 저장하므로 그것들은 동일해야 합니다
항목이 손상되었을 때 캐시가 하는 일
그것을 제거하고 정상적으로 서브세팅합니다. 잘못 형성되거나 잘린 항목은 서브셋이 PDF 스트림에 도달하기 전에 거부되며, 이것이 설계에서 가장 중요한 부분입니다. 문서에 들어온 손상된 캐시 항목은 깨진 폰트 프로그램을 가진 PDF를 만들 것이며, 그 실패는 원인에서 멀리 떨어진 곳, 즉 주 후, 고객의 기계에서, 뷰어 안에서 나타날 것입니다
기록은 원자적이므로 읽는 쪽은 절반 기록된 항목을 관찰하지 않으며, 기록 도중 충돌은 캐시를 독이 된 것이 아니라 일관된 상태로 남깁니다. 컴팩트 서브셋 항목은 PDF/A 폰트 딕셔너리가 요구하는 CID 재매핑 데이터를 유지하므로, 캐시된 서브셋은 여전히 규칙을 준수하는 서브셋입니다. 보존 출력이 유효하게 유지되기 위해 캐시를 우회할 필요는 없습니다
// 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');
실제 배포에서 폴더를 둘 곳
세 가지 프로퍼티가 이것을 결정합니다. 폴더는 서비스가 실행되는 계정이 기록할 수 있어야 하고, 네트워크 공유가 아니라 로컬 저장소에 있어야 하며, 배포 단계가 지워버리는 디렉터리 안에 있어서는 안 됩니다. 공유 위의 캐시는 모든 미스를 왕복으로 만들고 모든 히트를 두 번의 왕복으로 만듭니다. 설치 프로그램이 다시 만드는 애플리케이션 폴더 아래의 캐시는 매 업데이트 후 차가운 상태로 시작하는 캐시입니다
다중 인스턴스 서비스의 경우, 저장소가 기대하는 대로 동시 원자적 교체를 처리하는지 확인하기 전까지는 각 인스턴스에 자체 폴더를 주십시오. 중복된 항목의 비용은 추가 서브세팅 패스 하나이지만, 공유 캐시 경쟁을 디버깅하는 비용은 오후 하나입니다
다른 것을 찾아야 할 때
캐시는 반복 작업을 줄입니다. 첫 문서의 작업을 줄이지는 않으며, 글리프 집합이 결코 반복되지 않는 워크로드를 돕지도 않습니다. 출력이 예측 불가능한 텍스트에 걸쳐 사용된 거대한 CJK 서체 하나가 지배한다면, 더 효과적인 레버는 서브세팅 클로저 자체, 즉 어떤 글리프가 끌려 들어오고 왜 그런지이며, 이는 폰트 서브셋 클로저와 셰이핑 글리프에 관한 글이 다룹니다. 배치가 폰트가 전혀 아닌 이유로 느리다면 폰트와 이미지를 포함한 보고서 출력 산책문이 다른 시간이 보통 어디로 가는지 보여주며, EndDoc 폰트 서브셋 순서 버그 사례 연구는 서브세팅 정확성과 서브세팅 속도가 별개의 문제라는 점을 상기시킵니다
HotPDF는 Delphi와 C++Builder를 위한 네이티브 VCL PDF 컴포넌트이며, 서브셋 캐시는 애드온 서비스가 아니라 라이브러리의 일부이므로 보고서 서버는 하나의 폴더 경로를 설정하는 것만으로 그것을 얻습니다. 전체 폰트와 성능 기능 목록은 HotPDF 컴포넌트 페이지를 보십시오