Bài viết kỹ thuật

Lưu bộ đệm tập con font trên đĩa với HotPDF trong Delphi

HotPDF có thể giữ các tập con font TrueType và OpenType trên đĩa và tái sử dụng chúng giữa các tài liệu và giữa các lần chạy tiến trình, nên một lô render mười nghìn sao kê với cùng ba font chỉ cần subset ba font đó một lần thay vì mười nghìn lần. Bộ đệm được cấu hình với hai thuộc tính, kiểm tra bằng một bản ghi, và an toàn để bật thường xuyên: một thất bại của bộ đệm rơi về subsetting trong bộ nhớ thông thường và không bao giờ ngăn một tài liệu được tạo ra

Subsetting tốn kém vì một lý do. Dựng một tập con nghĩa là duyệt glyph closure, ghi lại locaglyf, dựng lại cmaphmtx, và phát ra một ánh xạ CID mà PDF có thể đánh địa chỉ. Đối với một tài liệu, chi phí đó biến mất vào nhiễu nền. Đối với một máy chủ báo cáo sinh tài liệu trong một vòng lặp, nó thường là khối CPU đơn lớn nhất trong cả lần chạy

Điều gì khiến một cache hit khả thi

Bốn thứ phải khớp: nội dung font, tập hợp glyph đã dùng, chế độ subset, và schema bộ đệm. Thiếu bất kỳ cái nào thì HotPDF subset lại từ đầu, vì một tập con chỉ tái sử dụng được khi nó đáng lẽ đã đồng nhất từng byte rồi

Tập glyph là điều kiện khiến mọi người ngạc nhiên. Hai hóa đơn khác biệt ở một tên khách hàng duy nhất dùng các tập glyph khác nhau, nên tạo ra các tập con khác nhau và các mục bộ đệm khác nhau. Bộ đệm sinh lời khi các tài liệu chia sẻ một bộ glyph — sao kê từ một mẫu cố định, biểu mẫu có dữ liệu biến thiên là số, danh mục rút từ một cơ sở dữ liệu sản phẩm — và không sinh gì khi mỗi tài liệu vẽ một lát khác nhau của một typeface CJK lớn. Hãy đo trước khi giả định bạn đang ở trường hợp nào

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;

Làm sao bạn biết bộ đệm đang làm việc gì?

GetFontSubsetCacheInfo trả về chín bộ đếm, và tỷ lệ giữa hai cái đầu tiên trả lời câu hỏi trực tiếp. HitCountMissCount cho tỷ lệ hit. WriteCountEvictionCount cho thấy các mục có sống đủ lâu để tái sử dụng hay đang bị đẩy ra bởi một ngân sách quá nhỏ. CurrentBytesFileCount báo cáo những gì đang ở trên đĩa ngay lúc này

Ba cái còn lại là những cái đáng để cảnh báo. CorruptCount đếm các mục thất bại xác thực và đã bị gỡ — vài cái sau một tắt máy không sạch là bình thường, một dòng ổn định nghĩa là lưu trữ không đáng tin. RejectedCount đếm các mục bị từ chối trước khi dùng. WriteFailureCount đếm các mục không thể ghi được gì cả, thường nghĩa là vấn đề quyền trên thư mục chứ không phải vấn đề gì về font. Không cái nào trong ba cái này dừng việc sinh tài liệu, và đó chính xác là lý do bạn phải tự xem chúng: một bộ đệm âm thầm không bao giờ ghi trông giống hệt từ bên ngoài như một bộ đệm hoạt động, chỉ trừ hóa đơn CPU

Eviction ngân sách và khoảnh khắc bạn thu hẹp một thứ

FontSubsetCacheMaxBytes mặc định 268435456 byte, tức 256 MiB, và có thể hạ xuống tại runtime. Việc hạ xuống kích hoạt eviction ít-dùng-gần-đây-nhất ngay lập tức thay vì đợi lần ghi kế tiếp, nên một dịch vụ phản ứng với áp lực đĩa có thể giải phóng không gian ngay lúc nó quyết định, chứ không phải vào một thời điểm sau đó mà nó không kiểm soát

Đặt FontSubsetCacheFolder thành một chuỗi rỗng vô hiệu hóa tầng đĩa mà không xóa bất cứ thứ gì đã lưu, và không thay đổi một byte nào của đầu ra font. Đó là thuộc tính cần tới khi bạn muốn cô lập bộ đệm trong lúc xử lý sự cố: tắt nó, chạy cùng một lô, và so sánh các PDF sinh ra. Chúng nên đồng nhất, vì bộ đệm lưu một kết quả, không phải một chính sách

Bộ đệm làm gì khi một mục bị hỏng

Nó gỡ bỏ mục đó và subset bình thường. Các mục malformed hoặc truncate bị từ chối trước khi tập con có thể tới một stream PDF, và đó là phần thiết kế quan trọng nhất: một mục bộ đệm bị hỏng mà lọt vào một tài liệu sẽ tạo ra một PDF với một font program hỏng, và sự thất bại đó sẽ lộ ra rất xa so với nguyên nhân — trong một trình xem, trên máy của khách hàng, nhiều tuần sau đó

Các phép ghi là nguyên tử, nên một trình đọc không bao giờ quan sát một mục ghi nửa vời, và một sự cố giữa chừng ghi để bộ đệm nhất quán thay vì bị ngộ độc. Các mục tập con compact giữ lại dữ liệu ánh xạ CID mà các dictionary font PDF/A yêu cầu, nên một tập con lưu trong bộ đệm vẫn là một tập con tuân thủ — đầu ra lưu trữ không phải bỏ qua bộ đệm để giữ tính hợp lệ

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

Đặt thư mục ở đâu trong một triển khai thực tế

Ba thuộc tính quyết định điều này: thư mục phải ghi được bởi tài khoản mà dịch vụ chạy dưới tư cách đó, nó nên nằm trên lưu trữ cục bộ thay vì một chia sẻ mạng, và nó không nên nằm trong một thư mục mà bước triển khai sẽ xóa sạch. Một bộ đệm trên một chia sẻ biến mỗi miss thành một vòng chuyến và mỗi hit thành hai; một bộ đệm dưới một thư mục ứng dụng mà installer tái tạo là một bộ đệm khởi động lạnh sau mỗi lần cập nhật

Đối với các dịch vụ đa instance, hãy cho mỗi instance một thư mục riêng trừ khi bạn đã xác nhận lưu trữ xử lý thay thế nguyên tử đồng thời theo cách bạn trông đợi. Chi phí của một mục trùng lặp là một lần subset thêm; chi phí debug một cuộc đua trên bộ đệm chia sẻ là cả một buổi chiều

Khi nào nên tìm một cái khác

Bộ đệm giảm công việc lặp lại. Nó không giảm công việc của tài liệu đầu tiên, và nó không giúp một khối công việc mà các tập glyph không bao giờ lặp lại. Nếu đầu ra của bạn bị chi phối bởi một typeface CJK khổng lồ duy nhất dùng trên văn bản không thể đoán trước, đòn bẩy hiệu quả hơn là chính bao đóng subset — những glyph nào bị kéo vào, và tại sao — được trình bày trong các ghi chú về bao đóng subset font và glyph shaping. Nếu lô của bạn chậm vì những lý do hóa ra không phải font gì cả, bài viết về đầu ra báo cáo với font và hình ảnh cho thấy nơi khác mà thời gian thường đi, còn nghiên cứu tình huống về bug thứ tự subset font tại EndDoc là một lời nhắc rằng tính đúng đắn của subsetting và tốc độ subsetting là hai vấn đề tách biệt

HotPDF là thành phần PDF VCL bản địa cho Delphi và C++Builder, và bộ đệm subset là một phần của thư viện thay vì một dịch vụ bổ sung, nên một máy chủ báo cáo nhận được nó chỉ bằng cách đặt một đường dẫn thư mục — xem trang thành phần HotPDF để biết danh sách tính năng font và hiệu năng đầy đủ