← Назад к вопросам

Можно ли оценить производительность приложения с помощью Libuv?

3.0 Senior🔥 61 комментариев
#Node.js и JavaScript#Кэширование и производительность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Оценка производительности приложения с помощью Libuv

Что такое Libuv

Libuv — это C библиотека, которая обеспечивает асинхронный ввод-вывод и event loop в Node.js. Это ядро, которое делает Node.js асинхронным. Но может ли она использоваться для оценки производительности?

Краткий ответ

Нет, не прямо. Libuv — это не инструмент для профилирования или бенчмарков. Но можно использовать информацию о ней для понимания узких мест в приложении.

Как работает Libuv

Event Loop (цикл событий)

┌─────────────────────────────┐
│      Event Loop (Libuv)     │
├─────────────────────────────┤
│ 1. Проверить таймеры        │
│    (setTimeout, setInterval) │
│                             │
│ 2. Выполнить I/O callbacks  │
│    (файлы, сеть, БД)       │
│                             │
│ 3. Выполнить microtasks     │
│    (Promises, async/await)  │
│                             │
│ 4. Проверить setImmediate   │
│    (приоритетные задачи)    │
│                             │
│ 5. Закрыть дескрипторы      │
│    (cleanup)                │
└─────────────────────────────┘
// Пример работы event loop
console.log('1. Синхронный код'); // Выполнится первым

setTimeout(() => console.log('2. setTimeout'), 0); // После event loop

Promise.resolve().then(() => console.log('3. Promise')); // Microtask

console.log('4. Еще синхронный'); // Выполнится вторым

// Вывод:
// 1. Синхронный код
// 4. Еще синхронный
// 3. Promise
// 2. setTimeout

Как профилировать с информацией о Libuv

1. Anaylizing Event Loop Lag

// Узнаем, как часто event loop блокируется

class EventLoopMonitor {
  private lastCheck = Date.now();
  private checks: number[] = [];

  start() {
    setInterval(() => {
      const now = Date.now();
      const lag = now - this.lastCheck - 100; // Ожидали 100ms, но
      
      if (lag > 10) {
        console.log(`Event loop lag: ${lag}ms`);
        this.checks.push(lag);
      }
      
      this.lastCheck = now;
    }, 100);
  }

  getStats() {
    const avg = this.checks.reduce((a, b) => a + b) / this.checks.length;
    const max = Math.max(...this.checks);
    return { avg, max };
  }
}

const monitor = new EventLoopMonitor();
monitor.start();

// После часа работы
console.log(monitor.getStats());
// { avg: 2.5, max: 150 }
// Event loop был заблокирован максимум на 150ms

2. Использование performance API

// Встроенный инструмент для профилирования

import { performance } from 'perf_hooks';

// Помечаем точки выполнения
performance.mark('db-query-start');
const results = await db.query('SELECT * FROM users');
performance.mark('db-query-end');

// Измеряем разницу
performance.measure('db-query', 'db-query-start', 'db-query-end');

// Получаем результаты
const measure = performance.getEntriesByName('db-query')[0];
console.log(`DB query took ${measure.duration}ms`);

// Это работает потому что использует Libuv timing

3. Анализ delays в обработке

// Libuv может отложить callback если event loop занят

setImmediate(() => {
  const { performance } = require('perf_hooks');
  const start = performance.now();
  
  // Это выполнится когда?
  console.log('setImmediate executed');
  console.log(`Delayed by: ${performance.now() - start}ms`);
});

// Если выводит "Delayed by: 150ms" — event loop был заблокирован

Реальные инструменты профилирования

Хотя Libuv нельзя использовать прямо, есть инструменты, которые анализируют его:

1. Node.js Built-in Profiler

# Запустить profiler
node --prof app.js
# Создаст isolate-*.log файл

# Анализировать результаты
node --prof-process isolate-*.log > processed.txt
cat processed.txt
# Результат покажет где приложение тратит время:
[Bottom-up (heavy) profile]
ticks  parent  name
 1234   12.3%  /lib/node_modules/express/index.js:1:100
  567    5.7%  JSON.stringify
  456    4.5%  Buffer.concat

2. clinic.js

# Установить
npm install -g clinic

# Запустить анализ
clinic doctor -- node app.js
clinic bubbleprof -- node app.js
clinic flame -- node app.js

# Откроется браузер с результатами

3. Node.js Inspector

# Запустить с инспектором
node --inspect app.js
# Откроется chrome://inspect

# Или через команду
node inspect app.js
// Внутри кода
const inspector = require('inspector');
const session = new inspector.Session();
session.connect();

session.post('Profiler.enable');
session.post('Profiler.start');

// Ваш код
await someAsyncOperation();

session.post('Profiler.stop', (err, { profile }) => {
  console.log(JSON.stringify(profile));
});

4. autocannon (бенчмарки)

npm install -g autocannon

# Запустить нагрузочное тестирование
autocannon http://localhost:3000

# Результат:
# Throughput: 50 MB/s
# Latency: avg 10ms, p99: 50ms
# Requests: 50000 successful, 0 errors

Практический пример: Профилирование Express API

import express from 'express';
import { performance } from 'perf_hooks';

const app = express();

// Middleware для логирования времени обработки
app.use((req, res, next) => {
  const start = performance.now();
  
  res.on('finish', () => {
    const duration = performance.now() - start;
    console.log(`${req.method} ${req.path} - ${duration.toFixed(2)}ms`);
    
    // Если > 100ms, это потенциальный узкое место
    if (duration > 100) {
      console.warn(`⚠️  SLOW REQUEST: ${req.path} took ${duration}ms`);
    }
  });
  
  next();
});

// Endpoint
app.get('/users/:id', async (req, res) => {
  // Профилируем DB запрос
  performance.mark('db-start');
  const user = await db.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
  performance.mark('db-end');
  performance.measure('db-query', 'db-start', 'db-end');
  
  // Профилируем JSON сериализацию
  performance.mark('json-start');
  const json = JSON.stringify(user);
  performance.mark('json-end');
  performance.measure('json-serialize', 'json-start', 'json-end');
  
  res.json(user);
});

// Логируем все меры
setInterval(() => {
  const measures = performance.getEntriesByType('measure');
  measures.forEach(m => {
    if (m.name === 'db-query') {
      console.log(`DB: ${m.duration.toFixed(2)}ms`);
    }
  });
}, 10000);

app.listen(3000);

Мониторинг Event Loop Health

import { performance } from 'perf_hooks';

class EventLoopHealthCheck {
  private readonly threshold = 100; // ms
  private readonly checkInterval = 1000; // ms
  
  start() {
    let lastCheck = performance.now();
    
    setInterval(() => {
      const now = performance.now();
      const lag = now - lastCheck - this.checkInterval;
      
      lastCheck = now;
      
      if (lag > this.threshold) {
        console.warn(`Event loop blocked for ${lag}ms`);
        // Отправить алерт мониторингу (Sentry, DataDog, etc)
      }
    }, this.checkInterval);
  }
}

const check = new EventLoopHealthCheck();
check.start();

Анализ с использованием Libuv Handle/Request счетчика

// Libuv отслеживает активные handles и requests
// Это можно анализировать для понимания утечек

import { createServer } from 'http';

const server = createServer((req, res) => {
  // Каждое соединение = Libuv handle
  res.end('OK');
});

server.listen(3000);

// Каждый открытый handle занимает память
setInterval(() => {
  const handles = process._getActiveHandles();
  const requests = process._getActiveRequests();
  
  console.log(`Active handles: ${handles.length}`);
  console.log(`Active requests: ${requests.length}`);
  
  // Если постоянно растет — есть утечка
  if (handles.length > 1000) {
    console.warn('Possible handle leak detected!');
  }
}, 5000);

Оптимизация на основе анализа Libuv

❌ Плохо: Блокирует event loop

// Синхронная операция блокирует Libuv event loop
const data = require('fs').readFileSync('/large-file.json');
const parsed = JSON.parse(data); // Если файл большой, lag может быть 500ms+

✅ Хорошо: Асинхронно

// Асинхронная операция позволяет Libuv обрабатывать другие события
const data = await fs.promises.readFile('/large-file.json');
const parsed = JSON.parse(data); // Event loop не блокируется

Вывод

Прямой ответ: NЕТ, Libuv — это не инструмент для оценки производительности.

Но это ядро, которое вы хотите монитировать. Используйте:

  1. clinic.js — специализированный инструмент для Node.js
  2. Node.js built-in profiler--prof флаг
  3. Performance API — встроенные измерения
  4. Inspector — отладка через DevTools
  5. Event loop monitoring — самодельные мониторы

Когда анализируете производительность, смотрите на:

  • Event loop lag (не должен быть > 50ms)
  • Активные handles (не должны расти бесконечно)
  • Время обработки запроса (должен быть < 100ms)
  • Использование памяти (проверяйте на утечки)

Libuv дает информацию о том, ЧТО происходит, но не о том, КАК это оптимизировать. Для оптимизации используйте специальные инструменты.