Обмеження пам'яті браузера під час обробки файлів
Мій файл важить лише 15 MB. Чому вкладці забракло пам'яті?
Файл на диску — це вже стиснута версія
Усе непорозуміння вміщується в одне речення: число, яке ви бачите у файловому менеджері, — це розмір стиснутих даних, а стиснуті дані обробити неможливо. Щоб змінити розмір, перестиснути, конвертувати чи проаналізувати зображення, його спершу треба декодувати в сирі пікселі, а сирі пікселі величезні.
Арифметика проста. Піксель у пам'яті зазвичай займає чотири байти — червоний, зелений, синій та альфа. Фотографія 4000 × 3000 — це дванадцять мільйонів пікселів, тож декодований растр важить близько 48 MB. JPEG, з якого він узявся, міг важити 4 MB. Файл виріс у дванадцять разів ще до того, як почалася хоч якась робота.
| Розміри | Пікселі | Декодований растр |
|---|---|---|
| 1920 × 1080 | 2,1 мільйона | близько 8 MB |
| 4000 × 3000 | 12 мільйонів | близько 48 MB |
| 6000 × 4000 | 24 мільйони | близько 96 MB |
| 10000 × 10000 | 100 мільйонів | близько 400 MB |
І однієї копії ніколи не досить
Реальний конвеєр тримає в пам'яті кілька копій одночасно: стиснуті вхідні байти, декодований растр оригіналу, растр результату в нових розмірах, внутрішні буфери кодера і стиснутий результат. Інструмент, який ще й показує попередній перегляд, тримає ще одну. Для фотографії 4000 × 3000 з прикладу вище пікове споживання у дві-три сотні мегабайтів — цілком звична річ, і це для файлу, який виглядав на 4 MB.
Растеризація PDF поводиться так само, сторінка за сторінкою: сторінка A4, відрендерена при 300 DPI, — це приблизно 2480 × 3508 пікселів, близько 35 MB у декодованому вигляді, а інструмент, який рендерить кілька сторінок одразу, множить цю цифру. З відео ще гірше, бо декодеру потрібно тримати в пам'яті кілька опорних кадрів водночас.
Де пролягають справжні стелі
- Бюджет пам'яті вкладки визначає браузер, а не сайт, і він менший за обсяг пам'яті, встановленої в машині. Ще й зменшується, коли інші вкладки зайняті роботою.
- Модулі WebAssembly, зібрані під поширену 32-бітну модель пам'яті, здатні адресувати щонайбільше чотири гібібайти, і браузери часто дозволяють на один екземпляр значно менше.
- Окремі буфери мають власну максимальну довжину, яка значно менша за всю пам'ять, доступну сторінці.
- Мобільні операційні системи просто завершують вкладку, що розрослася, без попередження і без помилки, яку сторінка могла б перехопити.
- Графічна пам'ять — це окремий, менший пул. Canvas, більший за максимальний розмір, який дозволяє платформа, просто не створюється, скільки б системної пам'яті не було вільно.
Жодне з цих обмежень не опубліковане як одне число, яке можна десь подивитися, бо все залежить від браузера, його версії, пристрою і того, що ще на ньому працює. У цьому й полягає справжня складність: інструмент не може запитати, скільки пам'яті йому дозволено використати.
Чому телефони — найсуворіший випадок
У телефонів менше фізичної пам'яті, немає свопу в тому розумінні, як на десктопі, і операційна система, яка агресивно забирає пам'ять у фонових процесів. А вкладка браузера стає фоновим процесом тієї миті, коли ви відповідаєте на повідомлення. Тому мобільні браузери тримають жорсткіші бюджети і швидше вивантажують вкладку, а виглядає це вивантаження зазвичай не як помилка, а як перезавантаження сторінки з втратою всієї вашої роботи.
Практичний наслідок такий: завдання, яке завершується на ноутбуці, може бути неможливим на телефоні з тим самим файлом. Це не дефект інструмента. Це апаратна межа, і чесна реакція на неї — сказати про це до початку, а не впасти на півдорозі.
Як поводиться акуратний інструмент
- Оцінює потрібний обсяг пам'яті за декодованими розмірами ще до того, як щось виділити, і відмовляється від завдання, яке явно не вміститься.
- На обмеженому пристрої обробляє по одному файлу за раз, а не запускає пакет паралельно.
- Передає буфери між сторінкою та її воркерами, а не копіює їх, тож великий файл не існує у двох примірниках.
- Звільняє кожен результат, щойно той записаний, і відкликає тимчасові URL, які інакше тримали б дані живими.
- Там, де формат це дозволяє, працює потоково — сторінка за сторінкою або кадр за кадром, замість завантажувати весь документ у пам'ять.
- Обмежує максимальну кількість пікселів, яку взагалі приймає, — саме це не дає маленькому файлу, що розгортається в гігантське полотно, покласти вкладку.
Що робити, коли завдання провалилося
- Закрийте інші вкладки. Вони конкурують за той самий бюджет, і браузери не ділять його порівну.
- Обробляйте менше файлів за раз. Черга з одного файлу повільніша, зате має набагато більше шансів дійти до кінця.
- Спершу зменште розміри. Якщо вдвічі скоротити найдовшу сторону, потрібна пам'ять зменшиться вчетверо, а часто саме цього ви й хотіли.
- Розділіть великий PDF і обробіть частини окремо.
- Найбільші завдання переносьте на десктоп. Це не поразка локальної обробки, а правильне використання наявного заліза.
- Перезапустіть браузер, якщо вкладка відкрита вже кілька днів. Довгоживучі вкладки накопичують пам'ять, яку звільняє перезавантаження.
Межа будь-якої оцінки
Браузери дають лише грубу підказку про доступну пам'ять, а деякі не дають нічого. Тому будь-яка цифра, яку показує локальний інструмент, — це оцінка, побудована з розмірів самого файлу та обережної моделі конвеєра, а не показник, зчитаний в операційної системи. Вона корисна, щоб вирішити, чи братися за завдання. Вона не обіцяє, що завдання завершиться, бо вирішальний чинник — чим решта пристрою займатиметься наступні тридцять секунд — вебсторінці не видно.
Інструменти для цього
Джерела
- W3C — WebAssembly Core Specification, memory model
- W3C — Device Memory API, and why the value is coarse
- WHATWG — HTML Standard, transferable objects