Спикер вернулся с обучения ElixirConf EU и поделился мыслями о том, как команда Erlang Ops расширяет классическую модель процессов. Вместо привычных мутных структур данных здесь работают примитивы многопоточности и неизменяемости. Для ситуаций, где нужна немного мутабельная общая память, все знают об ETS, но теперь появились инструменты для подсчёта — модули :atomics и :counters.
Атомарный массив :atomics работает вне гильбы процесса, то есть не в её стеке, а в специальном пространстве БИМа. Это общий мутабельный блок из N × 64-битных целых чисел. При обновлении меняются именно байты в памяти, а не создаются копии, как в Elixir из-за имммутабельности. Создать такую структуру и обратиться к ней можно в разных процессах: результат мгновенно виден всем. Данные собираются и уничтожаются, когда на них больше нет ссылок. Отсутствуют проверки переполнения для беззнаковых чисел — если попробовать присвоить значению больше 2^64, оно обернется вокруг. Главное преимущество — сверхскорость. Операции добавления и чтения, особенно атомарное добавление и получение сразу, не допускают гонки данных. Также доступна операция обмена значения и классический Compare-and-swap (CAS) для синхронизации. Структура гарантирует последовательную согласованность: читатель увидит изменения по порядку.
Братский модуль :counters похож на :atomics, но использует другую модель памяти. Здесь тоже массив целых чисел, но только со знаком и без атомарных операций типа обмена. Интересная деталь в архитектуре: массив состоит из одного целого на каждый планировщик (scheduler). На машине со 14 ядрами один-элементный массив фактически занимает 14 ячеек. Запись работает экстремально быстро, так как нет конкуренции за одно ядро — каждый планировщик работает со своими данными. Сложность перекладывается на чтение, которое суммирует значения всех планировщиков. Установка значения через put/3 тоже дорога, так как требует сброса остальных ячеек на ноль. Чтение не дает мгновенной консистентной картинки: результаты будут эквивалентны eventually-consistent.
Бенчмарки проводились на macOS с чипом Apple M4 Pro и 14 ядрами. С одним писателем все решения тянут поровну. Как только писателей становится больше ядра, ETS сильно теряет в производительности. :atomics показывает стабильный рост. :counters в режиме write_concurrency работает отлично, так как потоки не дерутся за ресурсы. Производительность растет при масштабировании, пока не упрётся в количество ядер, после чего выходит на плато, но не падает. Спикер заключил: :atomics берите для атомарных операций и синхронизации, например в библиотеке Broadway. :counters в режиме write_concurrency идеальны для счетчиков, где много записей и мало чтений, чтобы избежать конфликтов кэш-строки. Эти структуры — быстрые «шлюзы», позволяющие безопасно выйти за пределы изоляции процессов.