Ir al contenido

Rendimiento: analizador de fragmentación de memoria

El módulo Performance es intencionadamente pequeño. Consta de una única herramienta de solo observación — MemoryFragmentationAnalyzer — que mide la memoria máxima y retenida durante ventanas marcadas de trabajo del motor. La superficie también incluye la instantánea inmutable que produce la herramienta. No impone presupuestos, no regula ni modifica el comportamiento del motor.

Alcance y estabilidad. La superficie real de este módulo son dos clases (MemoryFragmentationAnalyzer, MemoryFragmentationSnapshot). Esto no es un arnés para imponer presupuestos por operación. El valor performance_budget en el front-matter de cada módulo es una convención de documentación, no algo que este módulo imponga. La superficie es experimental: es un diagnóstico introducido en @since 3.2.0. La forma de su instantánea puede evolucionar.

Ventana de terminal
composer require nextpdf/core:^3

La utilización de recursos es una preocupación de calidad de primer orden para un motor PDF. El observable mínimo que la sustenta es distinguir la memoria máxima (la marca de nivel máximo durante una ventana) de la memoria retenida (lo que sigue conservándose tras la ventana). Este módulo mide exactamente eso y nada más.

MemoryFragmentationAnalyzer es de solo observación: no muta el estado del escritor ni del documento. reset() ejecuta un ciclo de GC y reinicia el contador de máximo de PHP, de modo que las mediciones posteriores sean atribuibles a la ventana transcurrida desde ese reinicio. mark(string $label) captura una MemoryFragmentationSnapshot en un punto etiquetado. snapshots() devuelve la serie capturada. peakDelta() y retainedDelta() informan del cambio de memoria máxima y retenida a lo largo de la ejecución.

MemoryFragmentationSnapshot es un objeto de valor final readonly: un punto etiquetado con transientBytes() (máximo menos retenido: la memoria que se usó y se liberó), retentionRatio() (retenido sobre máximo) y toArray() para exportación. Una cifra alta de bytes transitorios con una razón de retención baja indica una rotación que una estrategia de reutilización de búferes podría eliminar. Ambas clases son @since 3.2.0.

ClaseMiembros claveRol
MemoryFragmentationAnalyzerreset(), mark(string $label), snapshots(), peakDelta(), retainedDelta()Analizador de memoria de solo observación (@since 3.2.0)
MemoryFragmentationSnapshottransientBytes(), retentionRatio(), toArray()Medición etiquetada inmutable (@since 3.2.0)

Ejecutar composer docs:generate-api-php -- --module=Performance para obtener la tabla PHPDoc completa.

Acotar una ruta crítica y leer los deltas.

<?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());

Envolver una representación y emitir la instantánea de fragmentación a un sumidero de métricas, tratando una razón de retención baja con bytes transitorios altos como señal de rotación.

<?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() llama a gc_collect_cycles() y memory_reset_peak_usage(). Tiene un efecto global de proceso sobre el contador de máximo de PHP. No intercalarlo con otro componente que también lea el contador de máximo en la misma petición.
  • Las mediciones son atribuibles a la ventana transcurrida desde el último reset(). Un mark() sin un reset() previo mide desde el inicio del proceso, lo que normalmente no es lo deseado.
  • Esto es un diagnóstico, no un control. Nunca regula ni aborta el trabajo; no construir contrapresión sobre ello.
  • El perfil de reproducibilidad es structural: las cifras de bytes dependen del entorno de ejecución, del asignador y del estado del GC. Dos ejecuciones pueden diferir numéricamente incluso para el mismo trabajo lógico.

La sobrecarga propia del analizador es un ciclo de GC en reset() y una lectura de hrtime() / memory_get_* por cada mark(): insignificante en relación con el trabajo que observa. Asigna una pequeña instantánea por cada mark(). El performance_budget de este front-matter es la cifra de referencia de toda la documentación; este módulo no la impone.

Las cifras de memoria son datos de diagnóstico. No contienen el contenido del documento, pero un perfil de memoria detallado puede filtrar información sobre el tamaño y la forma de la entrada. Tratar las exportaciones de instantáneas como telemetría interna y aplicar la obligación de depuración de registros del proyecto antes de compartirlas externamente. El módulo no realiza ninguna E/S ni incrusta datos externos. Consultar el modelo de amenazas del motor en /modules/core/security/.

Este módulo no formula ninguna declaración normativa sobre la especificación PDF. Es un diagnóstico de memoria y no implementa ningún protocolo estandarizado cuyas cláusulas deban citarse. Su justificación arquitectónica hace referencia a la vista de calidad de utilización de recursos del marco de descripción de arquitectura ISO/IEC/IEEE 42010, lo que constituye una alineación con la práctica de arquitectura, no una cita de PDF. La conformidad del motor se valida mediante las suites de oráculo y golden descritas en /modules/core/conformance/.