Remark и Rehype построены вокруг концепции потоковой обработки данных, называемой pipeline. Pipeline — это последовательность шагов, которые последовательно трансформируют AST (Abstract Syntax Tree) Markdown или HTML-документа. Каждый шаг реализуется в виде плагина, который получает дерево, выполняет преобразование и передает результат следующему плагину.
Основные принципы работы pipeline:
vfile.data, что удобно
для измерения времени и ресурсов каждого шага.Профилирование pipeline делится на три уровня: время выполнения, использование памяти, анализ узлов AST.
Измерение времени выполнения плагинов
Основной способ измерить производительность — обернуть каждый плагин в таймер:
import { performance } from 'perf_hooks';
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
const pipeline = unified()
.use(remarkParse)
.use((tree, file, next) => {
const start = performance.now();
// выполняем плагин
next();
const end = performance.now();
console.log(`remarkParse занял: ${end - start} мс`);
})
.use(remarkStringify);
Для асинхронных плагинов нужно использовать async/await
и performance.now() вокруг
await next().
Анализ использования памяти
JavaScript предоставляет средства для оценки потребления памяти с
помощью process.memoryUsage(). Для больших документов важно
отслеживать рост heap:
console.log(`Heap до обработки: ${process.memoryUsage().heapUsed} байт`);
// вызов плагина
console.log(`Heap после обработки: ${process.memoryUsage().heapUsed} байт`);
Для долгоживущих pipeline с большим количеством плагинов рекомендуется использовать пошаговый профилинг после каждого плагина, чтобы выявить узкие места.
Сбор статистики AST
AST Markdown/HTML позволяет собирать метрики о дереве: количество узлов, глубина дерева, число текстовых узлов и ссылок. Например:
function analyzeTree(node) {
let count = 0;
function visit(n) {
count++;
if (n.children) n.children.forEach(visit);
}
visit(node);
return count;
}
const nodeCount = analyzeTree(tree);
console.log(`Количество узлов AST: ${nodeCount}`);
Такая статистика помогает понять, какие участки документа создают сложное дерево и замедляют обработку.
Для крупных проектов важно иметь наглядное представление о pipeline. Стандартные методы:
chart.js или d3, где по оси X — плагины, по
оси Y — время.Пример структуры профилируемого pipeline:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import { performance } from 'perf_hooks';
const plugins = [
remarkParse,
remarkRehype,
rehypeStringify
];
async function runPipeline(input) {
let tree = input;
for (const plugin of plugins) {
const start = performance.now();
tree = await unified().use(plugin).process(tree);
const end = performance.now();
console.log(`${plugin.name} занял: ${end - start} мс`);
}
}
runPipeline('# Заголовок\nТекстовый контент');
Такой подход позволяет точно измерять производительность каждого шага и выявлять потенциальные оптимизации без глобальной перестройки pipeline.
Профилирование pipeline — ключ к поддержке масштабируемых и быстрых процессов обработки Markdown и HTML в Remark и Rehype.