Pular para o conteúdo

Performance: analisador de fragmentação da memória

O módulo Performance tem escopo restrito. Ele oferece uma única ferramenta apenas de observação, MemoryFragmentationAnalyzer, que mede a memória de pico e a memória retida em janelas marcadas de trabalho do motor. Sua superfície pública também inclui o snapshot imutável produzido pela ferramenta. Ele não impõe orçamentos, não limita o trabalho nem altera o comportamento do motor.

Escopo e estabilidade. A superfície real deste módulo são duas classes (MemoryFragmentationAnalyzer, MemoryFragmentationSnapshot). Ele não é um framework de imposição de orçamento por operação. O valor performance_budget no frontmatter de cada módulo é uma convenção de documentação, não um valor que este módulo impõe. A superfície é experimental: trata-se de um diagnóstico introduzido em @since 3.2.0. Seu formato de snapshot pode evoluir.

Terminal window
composer require nextpdf/core:^3

A utilização de recursos é uma preocupação central de qualidade para um motor Portable Document Format (PDF). O observável mínimo é a separação entre a memória de pico (o valor máximo durante uma janela) e a memória retida (o que ainda permanece alocado depois da janela). Este módulo mede apenas isso.

MemoryFragmentationAnalyzer apenas observa; ele não altera o estado do writer nem do documento. reset() executa um ciclo de coleta de lixo (GC) e zera o contador de pico do PHP, de modo que as medições subsequentes se refiram à janela desde o reset. mark(string $label) captura um MemoryFragmentationSnapshot em um ponto rotulado. snapshots() retorna a série capturada. peakDelta() e retainedDelta() informam a variação de pico e a variação de memória retida ao longo da execução.

MemoryFragmentationSnapshot é um objeto de valor final readonly: um ponto rotulado com transientBytes() (pico menos retida, ou seja, a memória usada e liberada), retentionRatio() (retida sobre pico) e toArray() para exportação. Um volume alto de bytes transientes com uma razão de retenção baixa indica rotatividade que uma estratégia de reutilização de buffer poderia eliminar. Ambas as classes são @since 3.2.0.

ClasseMembros principaisPapel
MemoryFragmentationAnalyzerreset(), mark(string $label), snapshots(), peakDelta(), retainedDelta()Analisador de memória somente de observação (@since 3.2.0)
MemoryFragmentationSnapshottransientBytes(), retentionRatio(), toArray()Medição rotulada imutável (@since 3.2.0)

Execute composer docs:generate-api-php -- --module=Performance para gerar a tabela completa de PHPDoc.

Instrumente um caminho crítico e, em seguida, leia os 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());

Envolva uma renderização e emita o snapshot de fragmentação para um coletor de métricas. Trate uma razão de retenção baixa com bytes transientes elevados como sinal de rotatividade.

<?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() chama gc_collect_cycles() e memory_reset_peak_usage(). Ele tem efeito global no processo sobre o contador de pico do PHP. Não o intercale com outro componente que leia o mesmo contador na mesma requisição.
  • As medições pertencem à janela desde o último reset(). Um mark() sem um reset() anterior mede a partir do início do processo, o que geralmente não é o que você quer.
  • Isto é um diagnóstico, não um controle. Ele nunca limita nem aborta o trabalho. Não construa back pressure sobre ele.
  • O perfil de reprodutibilidade é structural: os valores em bytes dependem do runtime, do alocador e do estado do GC. Duas execuções podem apresentar diferenças numéricas mesmo para o mesmo trabalho lógico.

O overhead do próprio analisador é um ciclo de coleta de lixo em reset() e uma leitura de hrtime() / memory_get_* por mark(), o que é desprezível em relação ao trabalho observado. Ele aloca um pequeno snapshot por mark(). O valor performance_budget neste frontmatter é o valor de referência válido para toda a documentação; este módulo não o impõe.

Os valores de memória são dados de diagnóstico. Eles não contêm conteúdo do documento, mas um perfil de memória detalhado pode revelar o tamanho e o formato da entrada. Trate as exportações de snapshot como telemetria interna e aplique a obrigação de higienização de logs do projeto antes de compartilhá-las externamente. O módulo não realiza nenhuma operação de input/output (I/O) e não incorpora dados externos. Consulte o modelo de ameaças do motor em /modules/core/security/.

Este módulo não faz nenhuma afirmação normativa sobre a especificação PDF. Ele é um diagnóstico de memória e não implementa nenhum protocolo padronizado com cláusulas a citar. Seu fundamento arquitetural faz referência à visão de qualidade de utilização de recursos do framework de descrição de arquitetura ISO/IEC/IEEE 42010. Essa referência é um alinhamento de prática arquitetural, não uma citação do PDF. A conformidade do motor é validada pelas suítes oracle e golden descritas em /modules/core/conformance/.