İçeriğe geç

Bellek ve akış işleme

Spec: ISO 32000-2, §7.5.4 Evidence: Mixed evidence

Büyük bir PDF, büyük bir yığın gerektirmemelidir. Bu sayfa, belge büyüdükçe NextPDF’in işlem belleğini nasıl sınırlı tuttuğunu, bellekte biriktirmek yerine ne zaman diske akış yaptığını ve burada “performans bütçesi”nin ne anlama geldiğini açıklar: manşetlik bir rakam değil, denetlenen bir sözleşme.

PDF biçimi, bir oluşturucuyu büyük bir yığın kullanmaya zorlamaz. Çapraz başvuru tablosu, her dolaylı nesne için bir bayt uzaklığı kaydeder; bu nedenle bir okuyucunun dosyanın tamamını bellekte tutması değil, dosyaya rastgele erişebilmesi gerekir. Bir oluşturucu bu modeli izleyebilir: nesneleri tamamlandıkça yayar ve yalnızca nerede yer aldıklarını hatırlar. Bunun yerine belgenin tamamı son yazma işlemine kadar yığında kalırsa, sayfa sayısı belleği doğrusal olarak yükseltir ve yüz sayfada sorunsuz çalışan bir rapor, elli binde işlemi başarısızlığa uğratır.

Toplu işleme ve işçi iş yüklerinde bu, kararlı bir hizmet ile yük altında öngörülemez biçimde başarısız olan bir hizmet arasındaki farktır. Sınırlı bellek, umut edilecek bir sayı değil, mühendislikle elde edilecek bir tasarım özelliğidir.

  • Akış yazıcısı, bellek belge başına sınırlı kalacak şekilde tasarlanmıştır. Her sayfa, tamamlanır tamamlanmaz çıktıya yazılır. Ardından arabelleği serbest bırakılır.
  • Aksi takdirde nesne sayısıyla birlikte büyüyecek olan kayıt tutma işlemi — çapraz başvuru uzaklıkları ve sayfa ağacının Kids başvuruları — PHP yığınını doldurmak yerine, hemen diske aktarılacak şekilde php://temp/maxmemory:0 ile açılan geçici akışlara yazılır.
  • Tasarım hedefi sayfa başına O(1) yığıntır: sayfalar eklendikçe belgeyi tutmanın maliyeti artmaz. Yazıcı bu mühendislik hedefine göre biçimlendirilmiştir.
  • Bir performans bütçesi, belgeleme sisteminde gerçek ve yapılandırılmış bir kavramdır: denetlenen bir sözleşme olarak ifade edilen, bir duvar saati üst sınırı ve bir tepe bellek üst sınırı. Bir yükümlülük belirtir. Bir kıyaslama sonucu değildir.
  • Somut sayılar, sessizce eskiyebilecekleri metne sabitlenmek yerine, belirtilen bir yöntemle ölçülen bir canlı sinyal olarak ele alınır.

Akış yazıcısı tek bir ilkeye uyar: yayabileceği hiçbir şeyi tutmaz.

  1. Start page A single active cursor; no document-wide page graph in memory.
  2. Finalise page Page content + page object written straight to the output stream.
  3. Release buffer The finalised page buffer is dropped; the heap returns to baseline.
  4. Record offset to disk Xref and Kids entries go to php://temp/maxmemory:0 — immediate disk spill.
  5. Close Pages-tree root, Catalog, and trailer written once at the end.
Akış yazıcısının sayfa başına döngüsü: her sayfa yayılır ve serbest bırakılır, büyüyen kayıt tutma işlemi disk destekli geçici akışlara gönderilir; böylece yığın, sayfa sayısıyla birlikte büyümez.

Diske aktarma ayrıntısı, mekanizmanın dayandığı noktadır. PHP’nin php://temp akışı, az miktarda veriyi bellekte tutar ve bu veri yalnızca bir eşiği aştığında diske aktarılır. Yazıcı, bu geçici akışları maxmemory:0 seçeneğiyle açar; bu seçenek onları hemen diske aktarmaya zorlar — bellek içi eşik sıfırdır. Pratik etkisi, tanımı gereği belgeyle birlikte büyüyen nesne başına kayıt tutma işleminin yığında hiçbir zaman birikmemesidir. Boyutun kısıt olmadığı diskte birikir. Bu seçenek olmadan, varsayılan bellek içi pencerenin diske aktarmadan önce dolması gerekir; bu da sınırlı bellek hedefini, tam da en önemli olduğu anda boşa çıkarır.

Buna karşılık performans bütçesi, hikâyenin diğer yarısıdır. Bu, bir pazarlama iddiası değil, belgeleme sistemine ait bir sözleşmedir. Şema, bir bütçeyi iki sınırlı tam sayı olarak tanımlar: milisaniye cinsinden bir duvar saati üst sınırı ve mebibayt cinsinden bir tepe yerleşik bellek üst sınırı. Bütçe bildiren bir tarif, denetlenebilen bir yükümlülük bildirir; tıpkı türlenmiş bir imzanın, derleyicinin denetleyebileceği bir yükümlülük bildirmesi gibi. Bir bütçenin değeri küçük olmasından değil, belirtilmiş ve uygulanıyor olmasından gelir.

Bu sayfanın kanıt düzeyi Evidence: Mixed evidence olarak işaretlenmiştir ve bu karışım bilinçlidir; çünkü kanıt gerçekten üç türdedir.

  • Kodla desteklenen mekanizma. src/Writer/Streaming/StreamingPdfWriter.php içindeki akış yazıcısı, sayfa başına yay-sonra-bırak döngüsünü belgeler ve uygular; PHP belleği nesne sayısından bağımsız olarak sınırlı kalsın diye xref ve Kids akışlarını anında diske aktarmaya zorlamak için php://temp/maxmemory:0 ile açar. Akışa dayalı, tek imleçli, ağaç tutmayan tasarım aynı zamanda ADR-001’de kaydedilen mimari karardır (işleme hattı en fazla O(derinlik) durum tutar, O(n) düğüm değil).
  • Tasarım ilkesi olarak bütçe. performance_budget alanı, belgeleme şemasının gerçek ve isteğe bağlı bir parçasıdır; açık üst sınırlarla birlikte { wall_ms, peak_mb } olarak tanımlanır. Tasarım gereği uygulanabilir bir sözleşmedir.
  • Canlı sinyal olarak kıyaslama. ADR-001, denetimli büyük belgeye ilişkin tepe bellek ve duvar saati rakamlarının belirtilen bir yöntemle toplanıp kaydedilecek deneysel bir hedef olduğunu açıkça belirtir — metinde öne sürülecek bir sayı değil. Bu nedenle bu sayfa mekanizmayı ve sözleşmeyi belirtir ve somut rakamlar için bu değerleri ölçen yere yönlendirir.

Biçim, bu hedefi ulaşılması zor bir ideal olmaktan çıkarıp makul kılar. Çapraz başvuru tablosu nesne başına bir uzaklık dizini olduğundan, bir oluşturucu kaynak Spec: ISO 32000-2, §7.5.4 uyarınca bunu yapabilir: nesneleri tamamladıkça yazar ve yalnızca uzaklıklarını tutar. Sınırlı bellek, dosya biçimine karşı bir mücadele değil, onunla tutarlıdır.

Sınırlı bellek, ayarlanan bir bayrak değil, oluşturma biçiminin bir özelliğidir. Her belgeyi tamamlayıp serbest bırakan bir toplu işleme döngüsü, çalışma boyunca yığını sabit tutar:

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;
use NextPDF\Core\PdfFactory;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Typography\FontRegistry;
// Process-lifetime, shared once.
$factory = PdfFactory::new()
->withCompress(true)
->withDocumentFactory(new DocumentFactory(
new FontRegistry(),
new ImageRegistry(maxCacheBytes: 50 * 1024 * 1024),
));
// Per-document, created and released each iteration.
foreach ($invoiceBatch as $invoice) {
$doc = $factory->create();
$doc->addPage();
$doc->writeHtml($invoice->toHtml());
$doc->save($invoice->outputPath());
unset($doc); // the document model is not carried into the next iteration
}

Kayıt defterleri paylaşılır; çünkü yazı tiplerini ve görüntüleri bir kez ayrıştırmak, işçi sürecinin amacıdır. Belge ise paylaşılmaz; her geçişte serbest bırakılır — bu da toplu işleme belleğinin toplu işlemle değil, tek bir belgeyle sınırlı kalmasını sağlar.

En yaygın yanlış anlama, “sınırlı bellek”i bir kıyaslama iddiası olarak ele almaktır — alıntılanacak bir megabayt rakamı beklemektir. Bu, söylenmek istenenin tam tersidir. Buradaki garanti yapısaldır: yazıcı, bir belgeyi tutmanın maliyeti sayfalar eklendikçe artmayacak şekilde tasarlanmıştır. Belirli bir tepe değeri sayfa içeriğine, yazı tiplerine ve görüntülere bağlıdır ve yalnızca ölçüm yöntemiyle birlikte verildiğinde anlamlıdır; bu nedenle bu cümleye değil, bir kıyaslamaya aittir.

İkinci bir tuzak: php://temp akışının sizi zaten koruduğunu varsaymak. Korur — ancak yalnızca varsayılan bellek içi penceresi dolduktan sonra. Diske aktarmayı anında başlatan, maxmemory:0 seçeneğidir. Ayrıntı, mekanizmanın kendisidir. Bu seçenek olmadan bu özellik, tam da var olma nedeni olan büyük belgeler altında geçerli olmazdı.

Bu sayfa, akış mekanizmasını ve bir performans bütçesinin anlamını açıklar. Ölçülmüş tepe bellek veya iş hacmi rakamlarını belirtmez. Bunlar, bildirilen bir yöntem altında kıyaslama disipliniyle üretilir ve ADR-001, deneysel sayıları açıkça o ölçüme havale eder. “Belge başına” sınırlı olması, tek bir belgenin içeriğinden bağımsız olarak sabit olduğu anlamına gelmez: gömülü çok sayıda büyük görüntü içeren bir sayfa, yine de o görüntülerin maliyeti kadar yük getirir. Büyümeyen şey, sayfa başına kayıt tutma işlemi ve tutulan sayfa grafiğidir. Her oluşturma yolu akış yazıcısını kullanmaz. Hangi yolların akış yaptığı ve hangilerinin arabelleğe aldığı, bu genel bakışla değil, kod ve ilgili yürütme yolunun biçimiyle belirlenir. Açıklanan mekanizma, bu sayfanın gözden geçirilme tarihi itibarıyla doğrudur. Yetkili kaynaklar, çekirdek deposundaki src/Writer/Streaming/ ve ADR-001’dir.

Akış ve sınırlı bellek tasarımı, bir Core özelliğidir. Sürümler bunu değiştirmez:

Bounded-memory streaming writer — edition availability
Edition Availability
Core Core, akışa dayalı, diske aktaran yazıcı tasarımını sağlar.
Pro Pro, aynı sınırlı bellekli yazıcıyı devralır; farklı bir bellek modeli değil, özellikler ekler.
Enterprise Enterprise, aynı sınırlı bellekli yazıcıyı devralır; farklı bir bellek modeli değil, özellikler ekler.
  • Sınırlı bellek — belgeyi tutma maliyetinin, sayfalar eklendikçe daha fazla yığın tüketmediği bir tasarım özelliği (sayfa başına O(1) hedefi).
  • Akış yazıcısı — belgenin tamamını tutmak yerine her sayfayı çıktıya yayan ve arabelleğini serbest bırakan yazıcı.
  • php://temp/maxmemory:0 — hemen diske aktarmaya zorlanan, büyüyen nesne başına kayıt tutma işlemi için kullanılan bir PHP geçici akışı.
  • Performans bütçesi — yapılandırılmış bir belgeleme sözleşmesi: belirtilmiş ve denetlenebilir bir duvar saati üst sınırı ve bir tepe bellek üst sınırı.
  • Canlı sinyal — metne gömülü sabit bir sayı yerine, belirtilen koşullar altında yöntemiyle birlikte raporlanan ölçülmüş bir değer.