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

Lưu đồ xử lý mục cache hỏng trong HotPDF: xác thực trước khi dùng, từ chối và gỡ bỏ trước mọi stream PDF, quay về subsetting trong bộ nhớ
các entry hỏng fail-open — tài liệu vẫn được tạo ra, và đầu ra PDF/A giữ dùng cache vì các entry giữ lại ánh xạ CID

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 loca và glyf, dựng lại cmap và hmtx, 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, mặc định là 256
    //  ... tạo hàng loạt ...
    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. HitCount và MissCount cho tỷ lệ hit. WriteCount và EvictionCount 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ỏ. CurrentBytes và FileCount 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

Sơ đồ bốn khóa cache font-subset trong HotPDF: nội dung phông, tập glyph, chế độ subset và phiên bản schema, với hai hóa đơn chỉ khác một cái tên rơi vào các mục khác nhau
hai hóa đơn chỉ khác một tên khách hàng là hai entry cache — cache chỉ có lợi khi các tài liệu dùng chung một kho glyph

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

Bảng chín bộ đếm GetFontSubsetCacheInfo của HotPDF nhóm thành tỷ lệ trúng, tỷ lệ sống sót và mức tiết kiệm, với ba chỉ số đọc nêu rõ cần thay đổi thiết lập nào
tỷ số HitCount trên MissCount trả lời câu hỏi cache có làm được gì không, còn các bộ đếm khác nói vì sao không

Đặ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ệ

//  Đặt lại tầng đĩa sau khi nâng cấp phông chữ hoặc thay đổi lược đồ
Pdf.ClearFontSubsetCache;

//  Hoặc di chuyển nó đến nơi ghi được và để ngân sách được áp dụng ngay
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 đủ