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, о том почему запуск стал достаточно дешёвым, чтобы применять его не задумываясь.