Memoria e streaming
Spec: ISO 32000-2, §7.5.4 ISO 32000-2 §7.5.4 Evidence: Mixed evidence
In sintesi
Sezione intitolata “In sintesi”Un PDF di grandi dimensioni non dovrebbe richiedere grandi quantità di memoria. Questa pagina spiega come NextPDF mantiene limitato l’heap del processo mentre un documento cresce, dove esegue lo streaming su disco anziché accumulare dati e cosa significa qui un «budget prestazionale» — un contratto verificato, non un numero da vetrina.
Perché è importante
Sezione intitolata “Perché è importante”Il formato PDF non costringe un generatore a usare molta memoria. La sua tabella di riferimenti incrociati registra un offset in byte per ogni oggetto indiretto, perciò un lettore ha bisogno solo dell’accesso casuale al file, non dell’intero file in memoria. Un generatore può rispecchiare questa struttura: può emettere gli oggetti man mano che vengono completati e ricordare soltanto dove sono stati scritti. Se invece l’intero documento rimane nell’heap fino alla scrittura finale, il numero di pagine fa crescere la memoria in modo lineare e un report che funziona a cento pagine può far fallire il processo a cinquantamila.
Per i carichi di lavoro batch e dei worker, questa è la differenza tra un servizio stabile e uno che fallisce in modo imprevedibile sotto carico. La memoria limitata è una proprietà da ottenere per progettazione, non un numero in cui sperare.
La versione breve
Sezione intitolata “La versione breve”- Il writer in streaming è costruito in modo che la memoria resti limitata per documento. Ogni pagina viene scritta sull’output non appena è finalizzata; quindi il suo buffer viene rilasciato.
- La contabilità che altrimenti crescerebbe con il numero di oggetti — gli offset dei riferimenti incrociati e i riferimenti
Kidsdell’albero delle pagine — viene scritta su flussi temporanei aperti conphp://temp/maxmemory:0, che li riversano subito su disco invece di riempire l’heap di PHP. - L’obiettivo di progettazione è heap O(1) per pagina: mantenere il documento non costa di più man mano che vengono aggiunte pagine. È questo l’obiettivo ingegneristico attorno al quale è modellato il writer.
- Un budget prestazionale è un concetto reale e strutturato nel sistema di documentazione: un limite di tempo reale e un limite di memoria di picco, espressi come contratto verificato. Dichiara un obbligo. Non è il risultato di un benchmark.
- I numeri concreti sono trattati come un segnale vivo, misurato secondo un metodo dichiarato, non cristallizzati nella prosa, dove potrebbero diventare obsoleti senza che nessuno se ne accorga.
Come lo affronta NextPDF
Sezione intitolata “Come lo affronta NextPDF”L’intera struttura del writer in streaming deriva da una sola decisione: non trattenere mai ciò che si può emettere.
- Start page A single active cursor; no document-wide page graph in memory.
- Finalise page Page content + page object written straight to the output stream.
- Release buffer The finalised page buffer is dropped; the heap returns to baseline.
- Record offset to disk Xref and Kids entries go to php://temp/maxmemory:0 — immediate disk spill.
- Close Pages-tree root, Catalog, and trailer written once at the end.
Il dettaglio decisivo è il riversamento su disco. php://temp di PHP mantiene una piccola quantità di dati in memoria e riversa su disco solo dopo il superamento di una soglia. Il writer apre quei flussi temporanei con l’opzione maxmemory:0, che li costringe a riversarsi su disco immediatamente — la soglia in memoria è zero. L’effetto pratico è che la contabilità per oggetto, che per definizione cresce con il documento, non si accumula mai nell’heap. Si accumula su disco, dove la dimensione non è il vincolo principale. Senza quell’opzione, la finestra in memoria predefinita dovrebbe riempirsi prima di riversare su disco, vanificando l’obiettivo della memoria limitata proprio quando conta di più.
Il budget prestazionale è l’altra metà della storia: un contratto del sistema di documentazione, non un’affermazione di marketing. Lo schema definisce un budget come due interi con limiti: un limite di tempo reale in millisecondi e un limite di memoria residente di picco in mebibyte. Una recipe che dichiara un budget dichiara un obbligo verificabile, allo stesso modo in cui una firma tipizzata dichiara un obbligo che un compilatore può verificare. Il valore di un budget sta nel fatto che è dichiarato e applicato, non nel suo essere piccolo.
Cosa dicono le prove
Sezione intitolata “Cosa dicono le prove”Questa pagina è contrassegnata come Evidence: Mixed evidence e la combinazione è deliberata, perché le prove sono effettivamente di tre tipi.
- Meccanismo supportato dal codice. Il writer in streaming in
src/Writer/Streaming/StreamingPdfWriter.phpdocumenta e implementa il ciclo per pagina di emissione e successivo rilascio e apre i suoi flussi xref e Kids conphp://temp/maxmemory:0per forzare il riversamento immediato su disco, così «la memoria di PHP resta limitata indipendentemente dal numero di oggetti». La progettazione in streaming, a cursore singolo e senza mantenere l’albero in memoria, è anche la decisione architetturale registrata in ADR-001 (la pipeline di rendering mantiene al massimo uno stato O(depth), non O(n) nodi). - Budget come principio di progettazione. Il campo
performance_budgetè una parte reale e opzionale dello schema di documentazione, definita come{ wall_ms, peak_mb }con limiti superiori espliciti. È un contratto applicabile per progettazione. - Benchmark come segnale vivo. ADR-001 chiarisce esplicitamente che le misure controllate di memoria di picco e tempo reale su documenti di grandi dimensioni sono un obiettivo empirico da raccogliere e registrare secondo un metodo dichiarato — non un numero da asserire nella prosa. Questa pagina enuncia quindi il meccanismo e il contratto e rimanda i dati concreti alla sede che li misura.
Il formato rende l’obiettivo concreto, non velleitario. Poiché la tabella di riferimenti incrociati è un indice di offset per oggetto secondo Spec: ISO 32000-2, §7.5.4 ISO 32000-2 §7.5.4 , un generatore è in grado di scrivere gli oggetti man mano che li completa e conservare soltanto i loro offset. La memoria limitata è coerente con il formato del file, non una lotta contro di esso.
Esempio pratico
Sezione intitolata “Esempio pratico”La memoria limitata è una proprietà del modo in cui si genera, non un flag da impostare. Un ciclo batch che finalizza e rilascia ogni documento mantiene l’heap stabile per tutta l’esecuzione:
<?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}I registri sono condivisi perché analizzare font e immagini una sola volta rientra nello scopo di un worker. Il documento non è condiviso e viene rilasciato a ogni iterazione — il che mantiene la memoria del batch limitata da un solo documento, non dal batch.
Idea sbagliata comune
Sezione intitolata “Idea sbagliata comune”L’idea sbagliata più comune è trattare la «memoria limitata» come un’affermazione da benchmark, aspettandosi un numero in megabyte da citare. Questo capovolge il senso della garanzia. La garanzia qui è strutturale: il writer è costruito in modo che mantenere un documento non costi di più man mano che vengono aggiunte pagine. Un dato di picco specifico dipende dal contenuto delle pagine, dai font e dalle immagini ed è significativo solo se accompagnato dal proprio metodo di misurazione, motivo per cui appartiene a un benchmark, non a questa frase.
Una seconda trappola: presumere che php://temp protegga già da solo. Lo fa, ma solo dopo che la sua finestra in memoria predefinita si è riempita. È l’opzione maxmemory:0 a rendere immediato il riversamento su disco. Il dettaglio è il meccanismo. Senza di essa la proprietà non reggerebbe proprio sui documenti di grandi dimensioni per cui esiste.
Limiti e confini
Sezione intitolata “Limiti e confini”Questa pagina spiega il meccanismo di streaming e il significato di un budget prestazionale. Non dichiara dati misurati di memoria di picco o di throughput. Quei dati derivano dalla disciplina di benchmarking secondo un metodo dichiarato e ADR-001 rimanda esplicitamente i numeri empirici a quella misurazione. «Limitata per documento» non significa costante a prescindere dal contenuto di un singolo documento: una pagina con molte immagini incorporate di grandi dimensioni richiede comunque la memoria necessaria per quelle immagini. Ciò che non cresce è la contabilità per pagina e il grafo delle pagine trattenuto. Non ogni percorso di generazione passa per il writer in streaming. Quali percorsi eseguono lo streaming e quali bufferizzano è determinato dal codice e dalla forma della pipeline, non da questa panoramica. Il meccanismo descritto è accurato alla data di revisione di questa pagina. Le fonti autorevoli sono src/Writer/Streaming/ e ADR-001 nel repository core.
La progettazione in streaming e a memoria limitata è una proprietà di Core. Le edizioni non la cambiano:
| Edition | Availability |
|---|---|
| Core | Core fornisce la progettazione del writer in streaming con riversamento su disco. |
| Pro | Pro eredita lo stesso writer a memoria limitata; aggiunge funzionalità, non un modello di memoria diverso. |
| Enterprise | Enterprise eredita lo stesso writer a memoria limitata; aggiunge funzionalità, non un modello di memoria diverso. |
Documentazione correlata
Sezione intitolata “Documentazione correlata”- Il modello della pipeline — dove si colloca lo stadio del writer nel flusso del documento.
- Benchmarking onesto — come NextPDF riporta i numeri che questa pagina deliberatamente non asserisce.
- Generazione di documenti ad alto volume — lo scenario batch per cui è costruito questo meccanismo.
Glossario
Sezione intitolata “Glossario”- Memoria limitata — una proprietà di progettazione in cui mantenere il documento non consuma più heap man mano che vengono aggiunte pagine (l’obiettivo O(1) per pagina).
- Writer in streaming — il writer che emette ogni pagina sull’output e rilascia il suo buffer anziché trattenere l’intero documento.
php://temp/maxmemory:0— un flusso temporaneo di PHP configurato per riversarsi su disco immediatamente, usato per la contabilità per oggetto in crescita.- Budget prestazionale — un contratto strutturato di documentazione: un limite di tempo reale e un limite di memoria di picco, dichiarati e verificabili.
- Segnale vivo — un valore misurato riportato con il proprio metodo in condizioni dichiarate, anziché un numero fisso incorporato nella prosa.