Zum Inhalt springen

Performance: Analyzer für Speicherfragmentierung

Das Performance-Modul ist bewusst schlank gehalten. Es stellt genau ein rein beobachtendes Werkzeug bereit — MemoryFragmentationAnalyzer —, das Spitzen- und gehaltenen Speicher über markierte Fenster der Engine-Arbeit hinweg misst. Zur Schnittstelle gehört außerdem der unveränderliche Snapshot, den dieses Werkzeug erzeugt. Es erzwingt keine Budgets, drosselt nichts und verändert das Verhalten der Engine nicht.

Geltungsbereich und Stabilität. Die tatsächliche Schnittstelle dieses Moduls besteht aus zwei Klassen (MemoryFragmentationAnalyzer, MemoryFragmentationSnapshot). Es ist kein Harness, das Budgets pro Operation erzwingt. Der Wert performance_budget im Front-Matter jedes Moduls ist eine Dokumentationskonvention und nichts, was dieses Modul erzwingt. Die Schnittstelle ist experimental; das Modul ist ein Diagnosewerkzeug, das in @since 3.2.0 eingeführt wurde. Die Form seines Snapshots kann sich weiterentwickeln.

Terminal-Fenster
composer require nextpdf/core:^3

Die Ressourcenauslastung ist für eine PDF-Engine ein zentrales Qualitätsmerkmal. Die dafür grundlegende Messung trennt Spitzenspeicher (den Höchststand während eines Fensters) von gehaltenem Speicher (dem, was nach dem Fenster noch belegt ist). Genau das misst dieses Modul, und nicht mehr.

MemoryFragmentationAnalyzer arbeitet rein beobachtend — er verändert weder den Zustand des Writers noch den des Dokuments. reset() führt einen GC-Zyklus aus und setzt den Spitzenzähler von PHP zurück, sodass nachfolgende Messungen dem Fenster seit dem Zurücksetzen zugeordnet werden können. mark(string $label) erfasst an einem benannten Punkt einen MemoryFragmentationSnapshot. snapshots() gibt die erfasste Reihe zurück. peakDelta() und retainedDelta() melden die Veränderung von Spitzen- und gehaltenem Speicher über den gesamten Lauf hinweg.

MemoryFragmentationSnapshot ist ein final readonly Value Object: ein benannter Messpunkt mit transientBytes() (Spitze minus gehalten — Speicher, der genutzt und wieder freigegeben wurde), retentionRatio() (gehalten geteilt durch Spitze) und toArray() für den Export. Ein hoher Wert an transienten Bytes bei niedrigem Halteverhältnis deutet auf Churn hin, den eine Strategie zur Pufferwiederverwendung beseitigen könnte. Beide Klassen sind @since 3.2.0.

KlasseWichtige MemberRolle
MemoryFragmentationAnalyzerreset(), mark(string $label), snapshots(), peakDelta(), retainedDelta()Rein beobachtender Speicher-Analyzer (@since 3.2.0)
MemoryFragmentationSnapshottransientBytes(), retentionRatio(), toArray()Unveränderliche benannte Messung (@since 3.2.0)

Führen Sie composer docs:generate-api-php -- --module=Performance aus, um die vollständige PHPDoc-Tabelle zu erhalten.

Umschließen Sie einen Hot Path und lesen Sie die Deltas aus.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Performance\MemoryFragmentationAnalyzer;
$analyzer = new MemoryFragmentationAnalyzer();
$analyzer->reset();
$analyzer->mark('before-write');
// ... engine work under observation ...
$analyzer->mark('after-write');
printf("Peak delta: %d B, retained delta: %d B\n", $analyzer->peakDelta(), $analyzer->retainedDelta());

Umschließen Sie ein Rendering und leiten Sie den Fragmentierungs-Snapshot an eine Metrik-Senke weiter; ein niedriges Halteverhältnis bei hohen transienten Bytes ist dabei als Churn-Signal zu werten.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Performance\MemoryFragmentationAnalyzer;
use Psr\Log\LoggerInterface;
final readonly class RenderMemoryProbe
{
public function __construct(private LoggerInterface $logger) {}
/** @param callable():void $render The render closure to observe. */
public function observe(callable $render): void
{
$analyzer = new MemoryFragmentationAnalyzer();
$analyzer->reset();
$analyzer->mark('start');
$render();
$analyzer->mark('end');
foreach ($analyzer->snapshots() as $snapshot) {
$this->logger->info('mem-frag', $snapshot->toArray());
}
}
}
  • reset() ruft gc_collect_cycles() und memory_reset_peak_usage() auf. Dies wirkt sich prozessweit auf den Spitzenzähler von PHP aus. Verschachteln Sie es nicht mit einer anderen Komponente, die im selben Request ebenfalls den Spitzenzähler liest.
  • Messungen lassen sich dem Fenster seit dem letzten reset() zuordnen. Ein mark() ohne vorausgehendes reset() misst ab dem Prozessstart, was in der Regel nicht erwünscht ist.
  • Dies ist ein Diagnoseinstrument, kein Steuerungsmechanismus. Es drosselt oder bricht Arbeit niemals ab; leiten Sie daraus keinen Gegendruck ab.
  • Das Reproduzierbarkeitsprofil ist structural: Die Byte-Werte hängen von der Laufzeitumgebung, dem Allocator und dem GC-Zustand ab. Zwei Läufe unterscheiden sich numerisch, selbst bei derselben logischen Arbeit.

Der Overhead des Analyzers selbst besteht aus einem GC-Zyklus bei reset() und einem hrtime() / memory_get_*-Lesevorgang je mark() — vernachlässigbar im Verhältnis zur beobachteten Arbeit. Der Analyzer allokiert je mark() einen kleinen Snapshot. Der Wert performance_budget in diesem Front-Matter ist der dokumentationsweite Referenzwert; dieses Modul erzwingt ihn nicht.

Speicherwerte sind Diagnosedaten. Sie enthalten keine Dokumentinhalte, doch ein feingranulares Speicherprofil kann Informationen über Größe und Form der Eingabe preisgeben. Behandeln Sie Snapshot-Exporte als interne Telemetrie und halten Sie die Log-Bereinigungspflicht des Projekts ein, bevor Sie sie extern weitergeben. Das Modul führt keine I/O-Vorgänge durch und bindet keine externen Daten ein. Siehe das Bedrohungsmodell der Engine in /modules/core/security/.

Dieses Modul leitet keinen normativen Anspruch aus der PDF-Spezifikation ab. Es handelt sich um eine Speicherdiagnose und implementiert kein standardisiertes Protokoll, dessen Abschnitte zitiert werden müssten. Seine architektonische Begründung verweist auf die Qualitätssicht der Ressourcenauslastung aus dem Architekturbeschreibungs-Framework ISO/IEC/IEEE 42010; das ist eine Ausrichtung an der Architekturpraxis und keine PDF-Zitierung. Die Konformität der Engine wird durch die Oracle- und Golden-Suites validiert, die in /modules/core/conformance/ beschrieben sind.