مقاله فنی

ذخیره زیرمجموعه فونت روی دیسک با HotPDF در Delphi

HotPDF می‌تواند زیرمجموعه‌های فونت TrueType و OpenType را روی دیسک نگه دارد و آن‌ها را در میان اسناد و اجراهای پردازش بازاستفاده کند، تا دسته‌ای که ده‌هزار صورت‌حساب را با همان سه فونت رندر می‌کند آن فونت‌ها را یک‌بار زیرمجموعه می‌کند به‌جای ده‌هزار بار. cache با دو ویژگی پیکربندی می‌شود، با یک رکورد بازرسی می‌شود، و روشن گذاشتنش بی‌خطر است: یک شکست cache به زیرمجموعه‌سازی درون‌حافظه‌ای معمولی fallback می‌کند و هرگز سندی را از تولید متوقف نمی‌کند

زیرمجموعه‌سازی برای دلیلی پرهزینه است. ساخت یک زیرمجموعه یعنی پیمایش closure گلیف، بازنویسی loca و glyf، بازسازی cmap و hmtx، و emit یک نگاشت CID که PDF می‌تواند آدرس‌دهی کند. برای یک سند آن هزینه در نویز گم می‌شود. برای یک سرور گزارش که اسناد را در یک حلقه تولید می‌کند، اغلب بزرگ‌ترین بلوک واحد زمان CPU در اجرا است

چه چیزی یک cache hit را ممکن می‌کند

چهار چیز باید مطابقت داشته باشند: محتوای فونت، مجموعه گلیف‌های استفاده‌شده، حالت زیرمجموعه، و شمای cache. هر یکی را از دست بدهید و HotPDF از صفر زیرمجموعه می‌کند، چون یک زیرمجموعه تنها زمانی قابل استفاده مجدد است که از هر حال بایت‌یکسان می‌بود

مجموعه گلیف شرطی است که مردم را غافلگیر می‌کند. دو فاکتور که در یک نام مشتری تفاوت دارند مجموعه گلیف متفاوتی استفاده می‌کنند، و در نتیجه زیرمجموعه‌های متفاوت و entryهای cache متفاوت تولید می‌کنند. cache وقتی سندی درآمد می‌زند که یک مجموعه گلیف را به اشتراک می‌گذارند — صورت‌حساب‌ها از یک قالب ثابت، فرم‌هایی که داده متغیرشان عددی است، کاتالوگ‌هایی که از یک پایگاه‌داده محصول کشیده می‌شوند — و هیچ چیزی نمی‌دهد وقتی هر سند برش متفاوتی از یک فونت بزرگ 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;

از کجا می‌دانید cache کاری می‌کند؟

GetFontSubsetCacheInfo نه شمارنده برمی‌گرداند، و نسبت میان دو تای اول سؤال را مستقیماً پاسخ می‌دهد. HitCount و MissCount نرخ hit را می‌دهند. WriteCount و EvictionCount نشان می‌دهند آیا entryها به اندازه کافی برای بازاستفاده دوام می‌آورند یا توسط بودجه‌ای که خیلی کوچک است هل داده می‌شوند. CurrentBytes و FileCount آنچه الان روی دیسک است را گزارش می‌کنند

سه تای باقی‌مانده آن‌هایی هستند که ارزش هشدار دادن دارند. CorruptCount entryهایی را می‌شمارد که اعتبارسنجی شکست خورد و حذف شدند — چند مورد پس از یک خاموش‌شدن نادرست عادی است، یک جریان پایدار یعنی ذخیره‌سازی نامعتبر است. RejectedCount entryهایی را می‌شمارد که قبل از استفاده رد شدند. WriteFailureCount entryهایی را می‌شمارد که اصلاً نتوانست نوشته شوند، که معمولاً یعنی یک مشکل مجوزها روی پوشه نه هر چیزی درباره فونت‌ها. هیچ‌کدام از این سه تولید سند را متوقف نمی‌کنند، که دقیقاً دلیل آن است که باید به آن‌ها نگاه کنید: یک cache که خاموش هرگز نمی‌نویسد از بیرون همانند cacheی که کار می‌کند به‌نظر می‌رسد، به‌جز صورت‌حساب CPU

خروج، بودجه‌ها و لحظه‌ای که یکی را کوچک می‌کنید

FontSubsetCacheMaxBytes پیش‌فرض 268435456 بایت، یعنی 256 MiB، و می‌تواند در runtime کاهش یابد. کاهش آن خروج least-recently-used فوری را به‌جای انتظار برای نوشتن بعدی راه‌اندازی می‌کند، پس سرویسی که به فشار دیسک واکنش نشان می‌دهد می‌تواند فضای آزاد را در لحظه‌ای که تصمیم می‌گیرد آزاد کند، نه در نقطه بعدی که کنترل نمی‌کند

تنظیم FontSubsetCacheFolder به یک رشته خالی، سطح دیسک را بدون پاک‌کردن هر چیزی که از قبل ذخیره‌شده غیرفعال می‌کند، و بدون تغییر حتی یک بایت خروجی فونت. آن ویژگی است که باید هنگام عیب‌یابی به آن دست ببرید: خاموشش کنید، همان دسته را اجرا کنید، و PDFهای تولیدشده را مقایسه کنید. آن‌ها باید یکسان باشند، چون cache یک نتیجه را ذخیره می‌کند، نه یک خط‌مشی

وقتی یک entry آسیب دیده cache چه می‌کند

آن را حذف می‌کند و به‌طور معمول زیرمجموعه می‌کند. entryهای malformed یا truncated قبل از آنکه زیرمجموعه بتواند به یک stream PDF برسد رد می‌شوند، که بخشی از طراحی است که بیشتر اهمیت دارد: یک entry cache خراب که به یک سند راه می‌یافت PDFای با یک برنامه فونت شکسته تولید می‌کرد، و آن شکست دور از علتش ظاهر می‌شد — در یک viewer، روی ماشین یک مشتری، هفته‌ها بعد

نوشتن‌ها اتمی است، پس یک خواننده هرگز یک entry نیمه‌نوشته‌شده مشاهده نمی‌کند، و یک crash در میانه نوشتن cache را به‌جای مسموم‌شدن سازگار نگه می‌دارد. entryهای subset فشرده داده remapping CID را که دیکشنری‌های فونت PDF/A الزام می‌کنند نگه می‌دارند، پس یک زیرمجموعه cache‌شده هنوز یک زیرمجموعه سازگار است — خروجی آرشیوی نباید برای معتبر ماندن از cache عبور کند

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

پوشه را در یک استقرار واقعی کجا قرار دهید

سه ویژگی این را تصمیم می‌گیرند: پوشه باید توسط حسابی که سرویس با آن اجرا می‌شود قابل نوشتن باشد، باید روی ذخیره‌سازی محلی به‌جای یک share شبکه باشد، و نباید درون دایرکتوری باشد که یک مرحله deploy پاک می‌کند. cache روی یک share هر miss را به یک رفت‌وبرگشت و هر hit را به دو تبدیل می‌کند؛ cache زیر یک پوشه کاربرد که installer بازسازی می‌کند cacheای است که بعد از هر به‌روزرسانی سرد شروع می‌شود

برای سرویس‌های چندنمونه‌ای، به هر نمونه پوشه خودش را بدهید مگر اینکه تأیید کرده باشید ذخیره‌سازی جایگزینی اتمی همزمان را همان‌طور که انتظار دارید مدیریت می‌کند. هزینه یک entry تکراری یک pass زیرمجموعه‌سازی اضافی است؛ هزینه debug یک رقابت cache اشتراکی یک بعدازظهر است

چه زمانی به چیز دیگری دست ببرید

cache کار تکراری را کاهش می‌دهد. کار سند اول را کاهش نمی‌دهد، و به workloadی که مجموعه گلیف‌اش هرگز تکرار نمی‌شود کمک نمی‌کند. اگر خروجی شما توسط یک فونت بزرگ CJK که روی متن غیرقابل پیش‌بینی استفاده می‌شود غالب است، اهرم مؤثرتر closure زیرمجموعه‌سازی خودش است — کدام گلیف‌ها کشیده می‌شوند، و چرا — در یادداشت‌های closure زیرمجموعه فونت و shaping گلیف پوشش داده شده. اگر دسته شما به دلایلی که در نهایت فونت نیست کند است، مرور خروجی گزارش با فونت‌ها و تصاویر نشان می‌دهد بقیه زمان معمولاً کجا می‌رود، و مطالعه موردی bug ترتیب زیرمجموعه فونت EndDoc یادآوری است که درستی زیرمجموعه‌سازی و سرعت زیرمجموعه‌سازی مسئله‌های جدا هستند

HotPDF یک جزء VCL بومی PDF برای Delphi و C++Builder است، و cache زیرمجموعه بخشی از کتابخانه است به‌جای یک سرویس add-on، پس یک سرور گزارش آن را با تنظیم یک مسیر پوشه کسب می‌کند — صفحه جزء HotPDF را برای لیست کامل ویژگی‌های فونت و کارایی ببینید