Перейти к содержимому

Производительность: анализатор фрагментации памяти

У модуля Performance узкая область применения. Он предоставляет один инструмент, предназначенный только для наблюдения, — MemoryFragmentationAnalyzer, который измеряет пиковое и удержанное использование памяти в размеченных интервалах работы движка. В публичный интерфейс также входит неизменяемый снимок, который создаёт этот инструмент. Модуль не применяет бюджеты, не ограничивает выполнение и не изменяет поведение движка.

Область применения и стабильность. Фактический интерфейс этого модуля — два класса (MemoryFragmentationAnalyzer, MemoryFragmentationSnapshot). Это не механизм применения бюджетов на уровне отдельных операций. Значение performance_budget во frontmatter каждого модуля — соглашение документации, а не значение, которое этот модуль применяет. Интерфейс помечен как experimental: это диагностический инструмент, появившийся в @since 3.2.0. Структура его снимка может измениться.

Окно терминала
composer require nextpdf/core:^3

Использование ресурсов — важнейшая характеристика качества движка Portable Document Format (PDF). Минимальная наблюдаемая метрика — разница между пиковой памятью (максимумом за интервал) и удержанной памятью (тем, что остаётся занятым после интервала). Этот модуль измеряет только её.

MemoryFragmentationAnalyzer только наблюдает: он не изменяет состояние writer или документа. reset() запускает цикл сборки мусора (GC) и сбрасывает счётчик пикового значения PHP, чтобы последующие измерения относились к интервалу с момента сброса. mark(string $label) фиксирует MemoryFragmentationSnapshot в отмеченной точке. snapshots() возвращает зафиксированную последовательность. peakDelta() и retainedDelta() сообщают, как изменились пиковая и удержанная память за время выполнения.

MemoryFragmentationSnapshot — объект-значение final readonly: отмеченная точка с transientBytes() (пик минус удержанное, то есть память, которая была использована и освобождена), retentionRatio() (удержанное относительно пика) и toArray() для экспорта. Высокое значение transient-bytes при низком коэффициенте удержания указывает на отток, который можно устранить за счёт повторного использования буферов. Оба класса — @since 3.2.0.

КлассОсновные членыРоль
MemoryFragmentationAnalyzerreset(), mark(string $label), snapshots(), peakDelta(), retainedDelta()Анализатор памяти только для наблюдения (@since 3.2.0)
MemoryFragmentationSnapshottransientBytes(), retentionRatio(), toArray()Неизменяемое помеченное измерение (@since 3.2.0)

Выполните composer docs:generate-api-php -- --module=Performance, чтобы сгенерировать полную таблицу PHPDoc.

Расставьте метки измерения на горячем участке кода, затем считайте дельты.

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

Оберните отрисовку наблюдением и отправьте снимок фрагментации в приёмник метрик. Низкий коэффициент удержания при большом количестве transient bytes рассматривайте как сигнал об оттоке.

<?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() вызывает gc_collect_cycles() и memory_reset_peak_usage(). Он влияет на счётчик пикового значения PHP на уровне процесса. Не используйте его вперемежку с другим компонентом, который читает тот же счётчик в рамках одного запроса.
  • Измерения относятся к интервалу с момента последнего reset(). mark() без предшествующего reset() измеряет от запуска процесса, и обычно это не то, что вам нужно.
  • Это диагностический инструмент, а не средство управления. Он никогда не ограничивает и не прерывает работу. Не выстраивайте на его основе механизм обратного давления.
  • Профиль воспроизводимости — structural: значения в байтах зависят от среды выполнения, аллокатора и состояния GC. Два запуска могут различаться численно даже для одной и той же логической работы.

Собственные накладные расходы анализатора сводятся к циклу сборки мусора при reset() и одному чтению hrtime() / memory_get_* на каждый mark(), что пренебрежимо мало по сравнению с наблюдаемой работой. Он выделяет один небольшой снимок на каждый mark(). Значение performance_budget в этом frontmatter — справочное значение для документации в целом; этот модуль его не применяет.

Показатели памяти — это диагностические данные. Они не содержат содержимого документа, но детальный профиль памяти может раскрыть размер и структуру входных данных. Рассматривайте экспортированные снимки как внутреннюю телеметрию и выполняйте обязательную очистку логов, предусмотренную проектом, прежде чем передавать их вовне. Модуль не выполняет операций input/output (I/O) и не встраивает внешние данные. См. модель угроз движка в /modules/core/security/.

Этот модуль не содержит нормативных заявлений относительно спецификации PDF. Это диагностический инструмент для памяти; он не реализует стандартизированный протокол, на пункты которого можно было бы сослаться. Его архитектурное обоснование ссылается на представление качества по использованию ресурсов из системы описания архитектуры ISO/IEC/IEEE 42010. Эта ссылка согласует модуль с архитектурной практикой, но не является цитатой из спецификации PDF. Соответствие движка проверяется оракулом и golden-наборами, описанными в /modules/core/conformance/.