Сравнение производительности

Стоимость создания экземпляров форматтеров

Основная нагрузка в Intl API возникает не на этапе форматирования, а при создании экземпляров объектов. Конструкторы Intl.DateTimeFormat, Intl.NumberFormat, Intl.Collator выполняют значительную работу при инициализации:

  • загрузка и разбор локализационных данных ICU
  • построение внутренних таблиц правил форматирования
  • выбор оптимального набора алгоритмов под локаль и опции
  • подготовка кешируемых структур

Именно этот этап делает создание форматтера сравнительно дорогой операцией.

const formatter = new Intl.NumberFormat("ru-RU", {
  style: "currency",
  currency: "RUB"
});

Вызов конструктора включает подготовку всей локали, и при массовом создании объектов затраты становятся заметными.


Цена вызова format()

После создания экземпляра основная операция — вызов методов форматирования — значительно дешевле:

  • format() у Intl.NumberFormat работает за близкое к O(1) время
  • format() у Intl.DateTimeFormat оптимизирован под повторное использование внутренних структур
  • compare() у Intl.Collator использует предвычисленные правила сравнения
formatter.format(123456.78);

Здесь отсутствует повторная инициализация локали, поэтому вызовы масштабируются линейно по количеству операций, но с минимальным коэффициентом.


Разница между однократным и многократным созданием

Критическое различие в производительности проявляется при следующих подходах:

Плохой сценарий

function formatPrice(value) {
  return new Intl.NumberFormat("ru-RU", {
    style: "currency",
    currency: "RUB"
  }).format(value);
}

Здесь при каждом вызове функции создаётся новый экземпляр, что приводит к:

  • повторной инициализации ICU-структур
  • росту времени выполнения
  • увеличению давления на GC

Оптимизированный сценарий

const formatter = new Intl.NumberFormat("ru-RU", {
  style: "currency",
  currency: "RUB"
});

function formatPrice(value) {
  return formatter.format(value);
}

Повторное использование экземпляра исключает стоимость повторной инициализации.


Intl.DateTimeFormat и стоимость календарных вычислений

Intl.DateTimeFormat обладает более высокой внутренней сложностью по сравнению с числовым форматированием:

  • преобразование timestamp в локальное представление даты
  • учёт часовых поясов
  • применение правил календаря (григорианский, альтернативные календари)
  • обработка летнего времени
const dtf = new Intl.DateTimeFormat("ru-RU", {
  year: "numeric",
  month: "long",
  day: "numeric"
});

dtf.format(Date.now());

Форматирование даты включает больше вычислений, чем чисел, особенно при частой смене временных зон.


Intl.Collator и стоимость сравнения строк

Intl.Collator используется для локализованного сравнения строк и сортировки:

const collator = new Intl.Collator("ru-RU");

collator.compare("яблоко", "язык");

Сравнение строк зависит от:

  • правил языка
  • нормализации Unicode
  • обработки диакритических знаков
  • параметров sensitivity

Хотя compare() быстрый, его стоимость выше обычного localeCompare без параметров только при некорректном использовании (создание нового коллатора на каждое сравнение).


Кэширование как основной фактор ускорения

Intl API рассчитан на повторное использование экземпляров. Кэширование снижает стоимость практически до нуля относительно инициализации.

Типовые стратегии:

  • создание форматтеров на уровне модуля
  • использование singleton-объектов
  • ленивое создание с последующим переиспользованием

Влияние локали на производительность

Разные локали могут иметь различную сложность обработки:

  • простые локали (например, en-US) обрабатываются быстрее
  • локали с более сложными правилами сортировки или чисел увеличивают нагрузку
  • комбинированные локали с несколькими fallback-цепочками требуют дополнительной обработки

Особенно заметно это при первом создании экземпляра, когда происходит загрузка данных ICU.


Node.js и браузерные реализации

Производительность Intl зависит от движка:

  • V8 (Node.js, Chrome) — использует ICU с оптимизациями кеширования
  • SpiderMonkey (Firefox) — отдельная реализация ICU-интеграции
  • JavaScriptCore (Safari) — собственная интеграция ICU

Различия проявляются в:

  • скорости инициализации форматтеров
  • размере памяти под локали
  • поведении кеширования

Аллокации памяти и garbage collection

Основные источники аллокаций:

  • создание объектов Intl.*
  • внутренние структуры ICU
  • промежуточные строки при форматировании

Частое создание форматтеров приводит к:

  • увеличению нагрузки на GC
  • фрагментации памяти
  • падению стабильности latency в высоконагруженных системах

Микробенчмарки и искажение результатов

Измерение производительности Intl требует учёта:

  • JIT-компиляции
  • прогрева движка
  • кеширования ICU
  • различий между первым и повторным вызовом

Типичная ошибка — измерение только format() без учёта конструктора, что даёт искажённую картину.


Стоимость параметров форматирования

Некоторые опции увеличивают нагрузку:

  • style: "unit" и сложные единицы измерения
  • notation: "compact" с локализацией сокращений
  • timeZone в DateTimeFormat
  • sensitivity в Collator

Чем сложнее конфигурация, тем выше цена инициализации.


Оптимизационные паттерны

На уровне архитектуры производительность зависит от следующих факторов:

  • разделение форматтеров по типам данных
  • минимизация количества уникальных конфигураций
  • отказ от динамического создания Intl-объектов в горячих путях выполнения
  • предварительное создание наборов форматтеров для разных локалей

Сравнение с альтернативными подходами

Примитивные решения без Intl:

  • быстрее на этапе инициализации
  • хуже масштабируются по локалям
  • требуют ручной реализации правил форматирования

Intl API:

  • дороже в инициализации
  • стабильнее при большом количестве локалей
  • эффективнее при повторном использовании

Поведение в горячих циклах

В циклах с миллионами итераций ключевым фактором становится не форматирование, а наличие или отсутствие повторного создания объектов:

for (let i = 0; i < 1e6; i++) {
  formatter.format(i);
}

Такой подход остаётся быстрым при условии заранее созданного formatter.

Создание внутри цикла приводит к экспоненциальному росту затрат времени.


Итоговая структура затрат

Производительность Intl API можно разложить на три уровня:

  • инициализация — самая дорогая операция
  • форматирование — дешёвая и стабильная
  • сравнение/локализация — средняя по стоимости, зависит от сложности правил локали