บทความเทคนิค

แคชสับเซตแบบอักษรบนดิสก์ด้วย HotPDF ใน Delphi

HotPDF สามารถเก็บสับเซตแบบอักษร TrueType และ OpenType บนดิสก์แล้วใช้ซ้ำข้ามเอกสารและข้ามการรันโพรเซส ทำให้ชุดงานที่เรนเดอร์เอกสารสถานะหนึ่งหมื่นฉบับด้วยแบบอักษรเดียวกันสามตัว ทำ subsetting แบบอักษรเหล่านั้นเพียงครั้งเดียวแทนหนึ่งหมื่นครั้ง แคชกำหนดค่าด้วยสองคุณสมบัติ ตรวจสอบด้วย record เดียว และปลอดภัยที่จะปล่อยไว้ ความล้มเหลวของแคชจะกลับไปใช้ subsetting ในหน่วยความจำปกติและไม่หยุดเอกสารจากการถูกผลิตเป็นอันขาด

การทำสับเซตแพงด้วยเหตุผล การสร้างสับเซตหมายถึงการเดิน glyph closure เขียน loca และ glyf ใหม่ สร้าง cmap และ hmtx ใหม่ แล้วปล่อยการแมป CID ที่ PDF จะจัดที่อยู่ได้ สำหรับเอกสารหนึ่งค่าใช้จ่ายนั้นกลืนไปกับสัญญาณรบกวน สำหรับเซิร์ฟเวอร์รายงานที่ผลิตเอกสารในลูป มันมักเป็นบล็อกเวลา CPU เดียวที่ใหญ่ที่สุดในการรัน

อะไรทำให้แคช hit เป็นไปได้

สี่สิ่งต้องตรงกัน คือเนื้อหาแบบอักษร ชุด glyph ที่ใช้ โหมดสับเซต และ schema ของแคช พลาดอันใดอันหนึ่งแล้ว HotPDF จะทำสับเซตใหม่ตั้งแต่ต้น เพราะสับเซตใช้ซ้ำได้ก็ต่อเมื่อมันจะเหมือนกันไบต์ต่อไบต์อยู่แล้วเท่านั้น

ชุด glyph คือเงื่อนไขที่ทำให้คนประหลาดใจ ใบแจ้งหนี้สองใบที่ต่างกันเพียงชื่อลูกค้าหนึ่งชื่อใช้ชุด glyph ต่างกัน จึงผลิตสับเซตและ cache entry ต่างกัน แคชคุ้มค่าเมื่อเอกสารแชร์คลัง glyph ร่วมกัน — สถานะจากเทมเพลตคงที่ ฟอร์มที่ข้อมูลผันแปรเป็นตัวเลข แคตตาล็อกที่ดึงจากฐานข้อมูลผลิตภัณฑ์เดียว — และไม่คุ้มเลยเมื่อทุกเอกสารวาดชิ้นส่วนต่าง ๆ ของแบบอักษร 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 ให้ hit rate WriteCount และ EvictionCount แสดงว่า entry อยู่รอดพอที่จะใช้ซ้ำหรือกำลังถูกดันออกด้วยงบประมาณที่เล็กเกินไป CurrentBytes และ FileCount รายงานสิ่งที่อยู่บนดิสก์ตอนนี้

สามตัวที่เหลือคือสิ่งที่คุ้มค่าจะแจ้งเตือน CorruptCount นับ entry ที่ตก validation และถูกเอาออก — ไม่กี่ตัวหลังการปิดระบบที่ไม่สะอาดเป็นเรื่องปกติ สายที่ต่อเนื่องหมายถึงพื้นที่จัดเก็กไม่น่าเชื่อถือ RejectedCount นับ entry ที่ถูกปฏิเสธก่อนใช้ WriteFailureCount นับ entry ที่เขียนไม่ได้เลย ซึ่งมักหมายถึงปัญหาสิทธิ์บนโฟลเดอร์มากกว่าเรื่องของแบบอักษร ไม่มีสามตัวนี้หยุดการสร้างเอกสาร ซึ่งเป็นเหตุผลว่าทำไมคุณต้องดูมัน แคชที่เงียบ ๆ ไม่เขียนเลยดูเหมือนกันจากภายนอกกับแคชที่ทำงานได้ เว้นเฉพาะบิล CPU

การขับออก งบประมาณ และช่วงเวลาที่คุณย่อขนาดมัน

FontSubsetCacheMaxBytes มีค่าเริ่มต้น 268435456 ไบต์ คือ 256 MiB และสามารถลดได้ที่รันไทม์ การลดกระตุ้นให้เกิด least-recently-used eviction ทันทีแทนที่จะรอการเขียนครั้งถัดไป ทำให้บริการที่ตอบสนองต่อแรงกดดันของดิสก์สามารถคืนพื้นที่ในช่วงเวลาที่มันตัดสินใจ ไม่ใช่ในช่วงเวลาถัดไปที่มันไม่ได้ควบคุม

การตั้ง FontSubsetCacheFolder เป็นสตริงว่างจะปิด disk tier โดยไม่ล้างสิ่งที่จัดเก็บไว้แล้ว และไม่เปลี่ยนไบต์เดียวของเอาต์พุตแบบอักษร นี่คือคุณสมบัติที่ควรเอื้อมไปเมื่อต้องการแยกแคชระหว่างการแก้ไขปัญหา ปิดมัน รันชุดงานเดียวกัน แล้วเปรียบเทียบ PDF ที่ผลิต พวกมันควรเหมือนกันทุกประการ เพราะแคชเก็บผลลัพธ์ ไม่ใช่นโยบาย

สิ่งที่แคชทำเมื่อ entry เสียหาย

มันเอาออกแล้วทำสับเซตตามปกติ entry ที่ผิดรูปแบบหรือถูกตัดทอนถูกปฏิเสธก่อนที่สับเซตจะไปถึง PDF stream ได้ ซึ่งเป็นส่วนของการออกแบบที่สำคัญที่สุด cache entry ที่เสียหายที่เข้าไปในเอกสารได้จะผลิต PDF ที่มี font program พัง และความล้มเหลวนั้นจะปรากฏไกลจากสาเหตุ — ในตัวอ่าน บนเครื่องของลูกค้า หลายสัปดาห์ให้หลัง

การเขียนเป็น atomic ทำให้ตัวอ่านไม่เคยสังเกต entry ที่เขียนครึ่ง ๆ และการล้มเหลวกลางการเขียนทิ้งแคชให้สอดคล้องแทนที่จะเป็นพิษ compact subset entry ยังคงเก็บข้อมูลการแมป CID ที่ font dictionary ของ PDF/A กำหนด สับเซตที่แคชจึงยังคงเป็นสับเซตที่สอดคล้อง — เอาต์พุตเก็บถาวรไม่ต้องบายพาสแคชเพื่อให้ถูกต้อง

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

จะวางโฟลเดอร์ที่ไหนในการปรับใช้จริง

สามคุณสมบัติตัดสินเรื่องนี้ โฟลเดอร์ต้องเขียนได้โดยบัญชีที่บริการรันเป็น มันควรอยู่บนพื้นที่จัดเก็บในเครื่องแทนเครือข่ายแชร์ และไม่ควรอยู่ในไดเรกทอรีที่ขั้นตอนการปรับใช้ล้าง แคชบนแชร์เปลี่ยน miss ทุกครั้งให้เป็น round trip และ hit ทุกครั้งให้เป็นสองรอบ แคชใต้โฟลเดอร์แอปพลิเคชันที่ตัวติดตั้งสร้างใหม่คือแคชที่เริ่มเย็นหลังทุกการอัปเดต

สำหรับบริการหลายอินสแตนซ์ ให้แต่ละอินสแตนซ์มีโฟลเดอร์ของตัวเอง เว้นแต่คุณยืนยันแล้วว่าพื้นที่จัดเก็กจัดการการแทนที่แบบ atomic พร้อมกันตามที่คุณคาดหวัง ต้นทุนของ entry ที่ซ้ำกันคือการทำสับเซตพิเศษหนึ่งรอบ ต้นทุนของการดีบัก race ของแคชที่แชร์คือบ่ายหนึ่งวัน

เมื่อใดควรหาทางเลือกอื่น

แคชลดงานที่ซ้ำกัน มันไม่ได้ลดงานของเอกสารแรก และไม่ช่วยเวิร์กโหลดที่ชุด glyph ไม่เคยซ้ำ หากเอาต์พุตของคุณถูกครอบงำด้วยแบบอักษร CJK ขนาดมหึมาที่ใช้กับข้อความที่คาดเดาไม่ได้ lever ที่ได้ผลมากกว่าคือ closure ของการทำสับเซตเอง — glyph ใดถูกดึงเข้ามา และเพราะเหตุใด — ซึ่งครอบคลุมในบันทึกของ closure ของสับเซตแบบอักษรและการจัดรูป glyph หากชุดงานของคุณช้าด้วยเหตุผลที่กลายเป็นว่าไม่ใช่แบบอักษรเลย คู่มือ เอาต์พุตรายงานพร้อมแบบอักษรและรูปภาพ แสดงว่าเวลาอื่น ๆ มักไปอยู่ที่ใด และกรณีศึกษาของ บั๊กการจัดลำดับสับเซตแบบอักษรที่ EndDoc เป็นเครื่องเตือนใจว่าความถูกต้องของการทำสับเซตและความเร็วของการทำสับเซตเป็นปัญหาแยกกัน

HotPDF เป็นคอมโพเนนต์ PDF แบบ VCL พื้นเมืองสำหรับ Delphi และ C++Builder และ subset cache เป็นส่วนหนึ่งของไลบรารี ไม่ใช่บริการเสริม เซิร์ฟเวอร์รายงานจึงได้มันโดยตั้งเส้นทางโฟลเดอร์เดียว — ดู หน้าคอมโพเนนต์ HotPDF สำหรับรายการคุณสมบัติแบบอักษรและประสิทธิภาพฉบับสมบูรณ์