4.19. Пули пам’яті

Камера, що зберігає три кадри повної роздільної здатності у пулі кадрових буферів, паралельно підтримує окремий буфер попереднього перегляду і ще залишає місце для скрипта Python та його об’єктів, — така камера жонглює більшим обсягом пам’яті, ніж міг би забезпечити єдиний блок RAM на MCU. MicroPython вміщує все це, розподіляючи дані між кількома різними видами пам’яті, які надає MCU, і спрямовуючи кожен вид виділення до тієї пам’яті, яка йому справді потрібна.

4.19.1. Види пам’яті

Сучасний MCU камери OpenMV Cam надає чотири різні види пам’яті. Перший недоступний для застосунку; три інших — це пули, з яких здійснюються виділення.

  • Кеш даних CPU — невелика, дуже швидка область пам’яті між CPU і рештою RAM. Коли CPU зчитує або записує значення з основної пам’яті, кеш автоматично зберігає копію, тому повторні звернення до одних і тих самих даних залишаються в кеші й не потребують звернення до повільнішої пам’яті. Кеш не є пулом, з якого виділяється пам’ять. Він прозорий для застосунку — він просто робить решту RAM на практиці швидшою, ніж її сира затримка, доки робочий набір вміщується в ньому.

  • Тісно зв’язана пам’ять процесора — невеликий блок RAM, підключений безпосередньо до CPU без жодної шини. Однотактний доступ, ніколи не промахується, ніколи не чекає. Виділення, яким справді потрібна найшвидша можлива пам’ять — де кожен такт затримки має значення, — надходять з цього пулу.

  • Швидка вбудована пам’ять — кілька сотень кілобайт до приблизно мегабайта RAM, вбудованої в корпус MCU. Низька затримка, висока пропускна здатність, але обмежений розмір. Тут розміщується купа MicroPython, щоб доступ до об’єктів Python залишався швидким; менші робочі буфери, до яких CPU часто звертається, розділяють цей пул.

  • Повільна зовнішня пам’ять — на платах, де MCU поєднано із зовнішньою мікросхемою пам’яті, десятки мегабайт позачипової RAM, доступні через зовнішню шину. Набагато більший об’єм, але кожне звернення займає більше часу, ніж до вбудованої пам’яті; кеш даних приховує більшу частину цієї вартості для робочих наборів, що вміщуються в ньому, а різниця відчутна в операціях, що обходять дані, надто великі для кешування. Використовується для виділень, які мають бути великими і які CPU може терпимо опрацьовувати на нижчій швидкості — насамперед для пулу кадрових буферів.

Плати цього сімейства займають різні місця в спектрі: одні мають лише вбудовану RAM, інші поєднують вбудовану RAM зі значно більшим зовнішнім блоком. Кожен з трьох видів пам’яті, що виділяється, розглядається як пул пам’яті — ділянка, з якої надходять виділення, — і позначається так, що кожен запит може вказати потрібний вид пам’яті.

4.19.2. Первинний кадровий буфер

Кадровий буфер, що лежить в основі snapshot(), не вимагає швидкої пам’яті. Він вимагає достатнього обсягу пам’яті — нічого більше. Це розміщує його в найбільшому доступному пулі, тому на платі з вбудованою та зовнішньою пам’яттю кадровий буфер потрапляє до зовнішнього блоку.

Кадровий буфер повної роздільної здатності з потрійною буферизацією надто великий, щоб вміститися у швидкому вбудованому пулі на більшості компонентів; більший пул — єдиний, що взагалі може його вмістити. Кеш даних CPU приховує більшу частину вартості кожного звернення під час обробки зображення застосунком, а DMA-рушій, що заповнює кадровий буфер з датчика, встигає за швидкістю передачі даних датчика в будь-якому разі.

Точний розмір, який займає кадровий буфер, визначається поточними значеннями pixformat(), framesize() і кількістю framebuffers(); він збільшується або зменшується кожного разу, коли будь-яке з них змінюється.

4.19.3. Вторинні кадрові буфери датчика

Другий екземпляр CSI отримує власний кадровий буфер, виділений з того самого пулу, що й первинний. Пул спільний; буфери незалежні. Слід другого кадрового буфера зазвичай набагато менший за слід первинного, оскільки вторинні датчики працюють з нижчою роздільною здатністю, тому додаткова пам’ять другого кадрового буфера становить незначну частку від первинного.

4.19.4. Кадровий буфер потоку

Буфер попереднього перегляду зображення є винятком. Він не виділяється з жодного пулу під час виконання; це фіксована область, зарезервована під час збірки з відомою адресою і відомим розміром. Це не дає шляху попереднього перегляду заважати іншим виділенням — область існує з моменту завантаження і ніколи не переміщується.

4.19.5. Купа MicroPython

Об’єкти Python — змінні, списки, словники, екземпляри класів, обгортка Image, яку повертає виклик snapshot(), кожен рядок і кортеж, створений застосунком, — розміщуються на купі MicroPython зі збором сміття, яка відділена від пулів пам’яті камери. Купа зі збором сміття (GC) — це область пам’яті, якою керує сам MicroPython: Python-код виділяє з неї пам’ять неявно кожного разу, коли створюється об’єкт, а MicroPython час від часу сканує купу та звільняє простір, зайнятий об’єктами, на які застосунок більше не посилається, тому застосунку ніколи не потрібно звільняти пам’ять вручну.

Для купи GC під час завантаження виділяється окрема область, зазвичай розміщена у швидкій вбудованій пам’яті для швидкого доступу до Python, з можливим переповненням у більший зовнішній блок на платах, яким потрібно більше простору для великих структур даних.

Об’єкт Image, що повертається snapshot(), є невеликим об’єктом-обгорткою на купі GC; основні дані пікселів зберігаються у кадровому буфері в одному з пулів камери. Вони ніколи не конкурують за одну й ту саму пам’ять.

4.19.6. Підсумок

Спрямування кожного виду виділення до правильного пулу — великих буферів до більшого пулу, де вони вміщуються, даних, чутливих до затримки, до швидших пулів, купи Python до власної області, попереднього перегляду до зарезервованого слоту — дає змогу запускати повний конвеєр захоплення у повній роздільній здатності, канал попереднього перегляду та нетривіальний скрипт Python поряд один з одним на компонентах, що мають лише кілька мегабайт швидкої пам’яті загалом.