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

Isolates и параллелизм

Асинхронный код конкурентен, но не параллелен — он целиком выполняется в одном потоке. Настоящий параллелизм в Dart — это isolates: отдельная память, отдельный event loop, сообщения вместо общего состояния. Модель, в которой гонки данных невозможны по построению, ровно поэтому делает обмен данными неудобным.

Почему это важно. Любая тяжёлая для процессора работа на главном isolate подвешивает интерфейс, а зависший интерфейс пользователь читает как падение приложения. Достаточно разбора крупного ответа, изменения размера картинки или одной криптографической операции.

Что нужно понимать

  • Медленное место ждёт или считает — isolate нужен только во втором случае
  • Сколько стоит пересечь границу isolate и оправдывает ли работа эту цену
  • Что можно передать в сообщении, а что нельзя
  • Кому принадлежит состояние, когда ничего не разделяется
  • Нужен ли ещё результат к моменту, когда он приходит

Основные темы

Модель

  • Isolate как память плюс event loop, без разделяемого изменяемого состояния
  • Передача сообщений: копирование против передачи владения
  • Почему это избавляет от блокировок и чем приходится расплачиваться

Как это делают

  • Isolate.run для разовой работы — обычный случай, и как правило этого хватает
  • Долгоживущие isolates и порты для повторяющейся работы
  • compute во Flutter и что под ним
  • Что можно отправить и какие ошибки прилетают, когда нельзя

Как решают

  • Сначала измерить: работа действительно упирается в процессор?
  • Цена запуска isolate против объёма работы
  • Пакетирование множества мелких задач вместо оплаты запуска для каждой
  • Где платформа уже сделала это за вас

Рядом

  • Параллелизм на сервере, где ограничения другие
  • Web, где isolates ложатся на workers и правила меняются

Уровни

УровеньКак это выглядит
JuniorЗнает, что «асинхронно» не значит «параллельно». Использует compute, когда так сказали.
MiddleНаходит профилировщиком работу, упирающуюся в процессор, уносит её с главного isolate и обрабатывает пути отказа.
SeniorПроектирует распределение работы — пул, пакетирование, отмену — и знает, когда isolate делает только хуже.

Практика

Для начала

  • Найти подтормаживание Снять профиль экрана, который дёргается, и понять, уходит время на ожидание или на вычисления.

  • Унести одну задачу Взять тяжёлый разбор или преобразование, запустить его через Isolate.run и измерить разницу.

  • Намеренно сломать границу Попробовать отправить то, что отправить нельзя, и внимательно прочитать ошибку.

Глубже

  • Оставить isolate жить Заменить повторяющиеся запуски долгоживущим isolate с портом и измерить.

  • Отменить работу на ходу Сделать фоновую задачу такой, чтобы её можно было бросить, когда пользователь уходит с экрана.

  • Решить по числам Найти случай, где isolate медленнее, чем то же самое на месте, и объяснить почему.

Проверьте себя

  • Как вы отличаете в своём приложении работу, упирающуюся в процессор, от работы, упирающейся в ввод-вывод?
  • Какая самая долгая синхронная операция выполняется сегодня на вашем главном isolate?
  • Во что обходится отправка крупного объекта в другой isolate?
  • Когда запуск isolate дороже самой работы?
  • Как фоновая задача узнаёт, что она больше не нужна?
  • Что из всего этого меняется в web?

Материалы

  • Isolates — официальное руководство: Isolate.run, порты и точный перечень того, что может пересечь границу.
  • Concurrency in Dart — модель, стоящая за isolates, и почему Dart выбрал сообщения вместо общей памяти. Читать до справочника по API, а не после.
  • Flutter performance profiling — как вообще убедиться, что проблема в процессоре; именно этот шаг обычно пропускают по пути к isolate.
  • compute — обёртка от Flutter; стоит прочитать, чтобы увидеть, насколько она тонкая.
  • Introducing isolate groups — от команды Dart, о том почему запуск стал достаточно дешёвым, чтобы применять его не задумываясь.