Кэш с обратной записью требует более интеллектуального контроллера кеша

Обновлено: 07.07.2024

В Windows Server 2016 добавлена поддержка устройств NVDIMM-N, которые обеспечивают высочайшую скорость операций ввода-вывода. Одним из привлекательных способов применения таких устройств является организация кэша обратной записи для обеспечения низкой задержки при записи. В этой статье описывается, как настроить зеркальное дисковое пространство с зеркальным кэшем обратной записи NVDIMM-N в качестве виртуального устройства для хранения журнала транзакций SQL Server. Если вы также хотите использовать его для хранения таблиц или иных данных, вы можете добавить в пул носителей больше дисков или создать несколько пулов для обеспечения изоляции.

Определение нужных дисков

Настраивать дисковые пространства в Windows Server 2016, особенно с расширенными функциями, такими как кэш обратной записи, проще всего с помощью PowerShell. Прежде всего следует определить, какие диски должны входить в пул дисковых пространств, на основе которого будет создаваться виртуальный диск. В устройствах NVDIMM-N применяется тип носителя и тип шины SCM (память класса хранилища), поддерживающий запросы посредством командлета PowerShell Get-PhysicalDisk .

Снимок экрана окна Windows PowerShell с выходными данными командлета Get-PhysicalDisk.

При использовании устройств NVDIMM-N больше не требуется выбирать целевые устройства для кэша обратной записи.

Для создания зеркального виртуального диска с зеркальным кэшем обратной записи требуется по крайней мере два устройства NVDIMM-N и еще два других диска. Присвоение нужных физических дисков переменной перед созданием пула позволяет упростить процесс.

На снимке экрана показана переменная $pd, а также присвоенные ей два диска SSD и два устройства NVDIMM-N, возвращенные с помощью следующего командлета PowerShell:

Снимок экрана окна Windows PowerShell с выходными данными командлета $pd.

Создание пула носителей

Используя переменную $pd, содержащую физические диски, можно легко создать пул носителей с помощью командлета PowerShell New-StoragePool .

Снимок экрана окна Windows PowerShell с выходными данными командлета New-StoragePool.

Создание виртуального диска и тома

После создания пула следует выделить виртуальный диск и отформатировать его. В этом случае будет создан только один виртуальный диск, и процесс можно упростить с помощью командлета PowerShell New-Volume :

Снимок экрана окна Windows PowerShell с выходными данными командлета New-Volume.

Виртуальный диск создан, инициализирован и отформатирован как NTFS. На снимке экрана ниже показано, что он имеет размер 300 ГБ и кэш обратной записи размером 1 ГБ, который будет размещаться на устройствах NVDIMM-N.

Снимок экрана окна Windows PowerShell с выходными данными командлета Get-VirtualDisk.

Теперь этот новый том можно увидеть на сервере. Этот диск можно использовать для хранения журнала транзакций SQL Server.

Most PCs are held back not by the speed of their main processor, but by the time it takes to move data in and out of memory. One of the most important techniques for getting around this bottleneck is the memory cache.

The idea is to use a small number of very fast memory chips as a buffer or cache between main memory and the processor. Whenever the processor needs to read data it looks in this cache area first. If it finds the data in the cache then this counts as a 'cache hit' and the processor need not go through the more laborious process of reading data from the main memory. Only if the data is not in the cache does it need to access main memory, but in the process it copies whatever it finds into the cache so that it is there ready for the next time it is needed. The whole process is controlled by a group of logic circuits called the cache controller.

One of the cache controller's main jobs is to look after cache coherency which means ensuring that any changes written to main memory are reflected within the cache and vice versa. There are several techniques for achieving this, the most obvious being for the processor to write directly to both the cache and main memory at the same time. This is known as a 'write-through' cache and is the safest solution, but also the slowest.

The main alternative is the 'write-back' cache which allows the processor to write changes only to the cache and not to main memory. Cache entries that have changed are flagged as 'dirty', telling the cache controller to write their contents back to main memory before using the space to cache new data. A write-back cache speeds up the write process, but does require a more intelligent cache controller.

Most cache controllers move a ‘line’ of data rather than just a single item each time they need to transfer data between main memory and the cache. This tends to improve the chance of a cache hit as most programs spend their time stepping through instructions stored sequentially in memory, rather than jumping about from one area to another. The amount of data transferred each time is known as the 'line size'.

Большинство компьютеров проводятся еще не скорость их основной процессор, а время, необходимое для перемещения данных в память и из нее. Одним из наиболее важных методов для получения вокруг этого узкого места является кэш-памяти.Идея заключается в том, чтобы использовать небольшое количество чипов памяти очень быстро как буфер или кэш между основной памятью и процессором. Всякий раз, когда процессор должен считывать данные он выглядит в этой области кэша сначала. Если он находит данные в кэше, то это засчитывается как кэш и процессор не нужно идти через более трудоемкий процесс считывания данных из основной памяти. Только если данные не в кэше ему нужно получить доступ к основной памяти, но в процессе он копирует то, что он находит в кэш так, чтобы он готов там следующий раз, когда это необходимо. Весь процесс контролируется группой логических схем, называется кэш-памяти контроллера.Один из основных работ кэша контроллера является ухаживать за кэш-памяти, что означает, что любые изменения записываются в основную память отражаются в кэше и наоборот. Существует несколько методов для достижения этого, наиболее очевидным является процессор для записи непосредственно в кэш и памяти одновременно. Это называется «write-through» cache и является самым безопасным решением, но самый медленный.Основной альтернативой является «обратной записи» кэш, который позволяет процессору записывать изменения только в кэш, а не к основной памяти. Записи кэша, которые были изменены, помеченных как «грязные», сообщая контроллер кэша для записи их содержимого обратно в основную память перед использованием пространства для кэширования новых данных. Кэш обратной записи ускоряет процесс записи, но требует более интеллектуальный контроллер кэша.Большинство контроллеров кэша переместить «линия» данных, а не только один элемент каждый раз, когда им нужно для передачи данных между основной памятью и кэш. Это, как правило, улучшить шансы на попадание в кэш, поскольку большинство программ тратят свое время, пошаговое выполнение инструкции последовательно хранятся в памяти, а не прыгать о из одной области в другую. Объем данных, передаваемых каждый раз, называется «размер линии».

Большинство компьютеров сдерживает не скорость их основного процессора, но к тому времени, которое требуется для перемещения данных в и из памяти. Одним из наиболее важных методов для обхода этой проблемы является кэш - память.

Идея заключается в том , чтобы использовать небольшое количество очень быстрых чипов памяти в качестве буфера или кэша между основной памятью и процессором. Всякий раз , когда процессор должен прочитать данные , он выглядит в этой области кэша в первую очередь. Если он находит данные в кэше , то это засчитывается как "хит кэш" и процессору не нужно проходить через более трудоемкий процесс считывания данных из основной памяти. Только если данные не в кэше это нужно , чтобы получить доступ к основной памяти, но в процессе того, что копирует все , что он находит в кэш , так что он там готов в следующий раз это необходимо. Весь процесс контролируется группой логических схем называется кэш - контроллер.

Одна из главных задач кэш - контроллер является смотреть после того, как кэш - когерентность что означает обеспечение , что любые изменения , записанные в основной памяти отражаются в кэше , и наоборот. Есть несколько методов для достижения этой цели, наиболее очевидные из которых для процессора писать непосредственно как кэш - памяти и оперативной памяти одновременно. Это известно как "сквозной записи" кэш и является самым безопасным решением, но и самый медленный.

Основной альтернативой является "обратной записи" кэш , который позволяет процессору записать изменения только в кэш , а не в основной памяти. Записей кэша , которые были изменены помечены как «грязные», говоря кэш - контроллер , чтобы написать их содержимое обратно в основную память , прежде чем использовать пространство для кэширования новых данных. Кэш обратной записи ускоряет процесс записи, но требует более интеллектуальный кэш - контроллер.

Большинство кэш - контроллеров перемещения "линию" данных , а не только одного элемента каждый раз , когда им необходимо для передачи данных между основной памятью и кэш. Это , как правило , чтобы улучшить вероятность попадания кэша поскольку большинство программ проводят свое время пошагового команды , хранящиеся в памяти последовательно, а не прыгая из одной области в другую. Объем передаваемых данных каждый раз , когда известен как "размер линии".


Cloud Performance 8.3.6 Файловые системы - кэширование со сквозной и обратной записью

  • @EricWang Я думаю, ты имеешь в виду write back имеет лучшую производительность?
  • @wlnirvana Да, вы правы, это моя канцелярская ошибка. Я бы удалил его и поставил здесь новый комментарий, чтобы не вводить в заблуждение в будущем.
  • 6 Проще говоря, write back имеет лучшую производительность, потому что запись в основную память происходит намного медленнее, чем запись в кеш процессора, а данные могут быть короткими во время (средства могут снова измениться раньше, и нет необходимости помещать старую версию в память). Это сложно, но более изощренно, большая часть памяти в современных процессорах использует эту политику.
  • Я вижу, что дан пояснительный ответ. Я советую вам взглянуть на теги Write-Allocate, Write-NoAllocate после изучения алгоритма обратной записи.
  • Ответ на ваш вопрос заключается в том, что при сквозном кэшировании при записи в одном блоке требуется только одна запись в основную память. Подробности смотрите в моем ответе.

Преимущество сквозной записи в основную память состоит в том, что она упрощает конструкцию компьютерной системы. При сквозной записи в основной памяти всегда есть актуальная копия строки. Поэтому, когда чтение выполнено, основная память всегда может ответить запрошенными данными.

Если используется обратная запись, иногда актуальные данные находятся в кэше процессора, а иногда - в основной памяти. Если данные находятся в кэше процессора, то этот процессор должен остановить ответ основной памяти на запрос чтения, поскольку в основной памяти может быть устаревшая копия данных. Это сложнее, чем сквозная запись.

Кроме того, сквозная запись может упростить протокол согласованности кэша, поскольку он не требует Изменить штат. В Изменить состояние записи, что кэш должен записать строку кэша, прежде чем он аннулирует или вытесняет строку. При сквозной записи строка кэша всегда может быть признана недействительной без обратной записи, так как в памяти уже есть обновленная копия строки.

Еще одна вещь - в программном обеспечении с архитектурой обратной записи, которое записывает в отображенные в память регистры ввода-вывода, необходимо предпринимать дополнительные шаги, чтобы гарантировать немедленную отправку записей из кеша. В противном случае записи не будут видны за пределами ядра до тех пор, пока строка не будет прочитана другим процессором или строка не будет исключена.

Давайте посмотрим на это на примере.Предположим, у нас есть кэш с прямым отображением и используется политика обратной записи. Итак, у нас есть действительный бит, грязный бит, тег и поле данных в строке кеша. Предположим, у нас есть операция: записать A (где A отображается в первую строку кеша).

Что происходит, так это то, что данные (A) от процессора записываются в первую строку кеша. Установлены действительные биты и биты тегов. Грязный бит установлен в 1.

Грязный бит просто указывает, была ли когда-либо записана строка кеша с момента последнего внесения в кеш!

Теперь предположим, что выполняется другая операция: чтение E (где E также отображается в первую строку кеша)

Таким образом, политика обратной записи не гарантирует, что блок будет таким же в памяти и связанной с ним строке кэша. Однако всякий раз, когда строка собирается заменить, сначала выполняется обратная запись.

Политика сквозной записи прямо противоположна. Согласно этому в памяти всегда будут актуальные данные. То есть, если записывается блок кеша, соответственно будет записана и память. (без грязных битов)

возможно, эта статья поможет вам разместить ссылку здесь

Сквозная запись: запись выполняется синхронно как в кэш, так и в резервное хранилище.

Обратная запись (или отложенная запись): запись выполняется только в кэш. Измененный блок кеша записывается обратно в хранилище непосредственно перед заменой.

Сквозная запись: при обновлении данных они записываются как в кэш, так и во внутреннее хранилище. Этот режим прост в эксплуатации, но он медленен при записи данных, поскольку данные должны записываться как в кэш, так и в хранилище.

Обратная запись: при обновлении данных они записываются только в кеш. Измененные данные записываются во внутреннее хранилище только при удалении данных из кеша. Этот режим имеет высокую скорость записи данных, но данные будут потеряны, если произойдет сбой питания до того, как обновленные данные будут записаны в хранилище.

Обратная и сквозная запись описывают политику, когда написать хит происходит, то есть когда в кеше есть запрошенная информация. В этих примерах мы предполагаем, что один процессор выполняет запись в основную память с помощью кеша.

Сквозная запись: Информация записывается в кэш и память, и запись завершается, когда оба завершаются. Это имеет то преимущество, что его проще реализовать, и основная память всегда согласована (синхронизирована) с кешем (для однопроцессорного случая - если какое-то другое устройство изменяет основную память, то этой политики недостаточно), а также промах при чтении никогда не приводит к записи в основную память. Очевидным недостатком является то, что каждое попадание записи должно производить две записи, одна из которых обращается к более медленной основной памяти.

Преимущество состоит в том, что записи могут происходить со скоростью кеш-памяти, и если запись выполняется в одном и том же блоке, требуется только одна запись в основную память (при замене предыдущего блока). Недостатки заключаются в том, что этот протокол сложнее реализовать, основная память может быть несовместима (не синхронизирована) с кешем, а операции чтения, приводящие к замене, могут вызвать запись грязных блоков в основную память.

Политика для написать мисс подробно описаны в моей первой ссылке.

Эти протоколы не заботятся о случаях с несколькими процессорами и несколькими кешами, как это обычно бывает в современных процессорах. Для этого посложнее согласованность кеша требуются механизмы. Кеши со сквозной записью имеют более простые протоколы, поскольку запись в кэш немедленно отражается в памяти.

Обратная запись является более сложной задачей и требует сложного протокола согласования кэш-памяти (MOESI), но оно того стоит, поскольку делает систему быстрой и эффективной.

Единственное преимущество сквозной записи заключается в том, что она делает реализацию чрезвычайно простой и не требует сложного протокола согласованности кеша.

image

Привет!

В предыдущей статье мы для ускорения графики на микроконтроллере в Embox применяли процессорный кэш. При этом мы использовали режим «write-through». Тогда мы писали о некоторых преимуществах и недостатках связанных с «write-through» режимом, но это был лишь беглый обзор. В этой статье я, как и обещал, хочу подробней рассмотреть типы кэшей в ARM микроконтроллерах, а также сравнить их. Конечно, все это будет рассмотрено с точки зрения программиста, и вдаваться в детали работы контроллера памяти в данной статье мы не планируем.

Начну с того на чем остановился в предыдущей статье, а именно, на разнице между «write-back» и «write-through» режимами, поскольку именно эти два режима чаще всего используются. Если кратко, то:

  • «Write-back». Данные по записи попадают только в кэш. Реальная запись в память откладывается до тех пор, пока кэш не переполнится и не потребуется место для новых данных.
  • «Write-through». Запись происходит “одновременно” и в кэш и в память.

Write-through

Преимуществами режима write-through считается простота использования, что потенциально уменьшает количество ошибок. Действительно, в этом режиме память всегда находится в корректном состоянии и не требует дополнительных процедур обновления.

Конечно кажется будто это должно сильно сказаться на производительности, но сам STM в этом документе говорит, что это не так:

Write-through: triggers a write to the memory as soon as the contents on the cache line are written to. This is safer for the data coherency, but it requires more bus accesses. In practice, the write to the memory is done in the background and has a little effect unless the same cache set is being accessed repeatedly and very quickly. It is always a tradeoff.

То есть, изначально мы предполагали, что раз запись происходит в память, то на операциях записи производительность будет примерно такой же как и совсем без кэша, а основной выигрыш происходит за счет повторных чтений. Однако, STM это опровергает, говорится что данные в в память попадают “в фоне”, поэтому производительность на записи практически такая же как и в режиме «write-back». Это, в частности, может зависеть от внутренних буферов контроллера памяти (FMC).

Минусы режима «write-through»:

  • При последовательном и быстром доступе в одну и ту же память производительность может снижаться. В режиме «write-back» последовательные частые доступы к одной памяти будут, наоборот, являться плюсом.
  • Как и в случае с «write-back» все равно нужно делать cache invalidate после окончания DMA операций.
  • Баг “Data corruption in a sequence of Write-Through stores and loads” в некоторых версиях Cortex-M7. Нам указал на него один из разработчиков LVGL.

Write-back

Как уже говорилось выше, в этом режиме (в отличие от «write-through») данные в общем случае не попадают в память по записи, а попадают только в кэш. Как и у «write-through», у этой стратегии имеются два “под варианта” — 1) «write allocate», 2) «no write allocate». Об этих вариантах мы поговорим дальше.

Write Allocate

Как правило, в кэшах всегда используется «read allocate» — то есть по промаху (cache miss) на чтение данные забираются из памяти и размещаются в кэше. Аналогично, при промахе на запись данные могут подгружаться в кэш («write allocate») или не подгружаться («no write allocate»).

Обычно на практике используются сочетания “write-back write allocate” или “write-through no write allocate”. Далее в тестах мы попробуем чуть более детально проверить в каких ситуациях использовать «write allocate», а в каких «no write allocate».

Прежде чем переходить к практической части нам необходимо разобраться как же задавать параметры региона памяти. Для выбора режима кэша (или его отключения) для определенного региона памяти в архитектуре ARMv7-M используется MPU (Memory Protection Unit).

В контроллере MPU поддерживается задание регионов памяти. Конкретно в архитектуре ARMV7-M может быть до 16 регионов. Для этих регионов можно независимо устанавливать: стартовый адрес, размер, права доступа (read/write/execute и т.д.), атрибуты — TEX, cacheable, bufferable, shareable, а так же и другие параметры. С помощью такого механизма, в частности, можно добиться любого типа кэширования для определенного региона. Например, мы можем избавиться от необходимости вызывать cache_clean/cache_invalidate просто выделив регион памяти под все операции DMA и пометив эту память как не кэшируемую.

Нужно отметить важный момент при работе с MPU:

The base address, size and attributes of a region are all configurable, with the general rule that all regions are naturally aligned. This can be stated as:
RegionBaseAddress[(N-1):0] = 0, where N is log2(SizeofRegion_in_bytes)

Иными словами, стартовый адрес региона памяти должен быть выровнен на его собственный размер. Если у вас, к примеру, регион 16 Кб, то выравнивать нужно на 16 Кб. Если регион памяти 64 Кб, то выравниваем на 64 Кб. И так далее. Если этого не сделать, то MPU может автоматически “обрезать” регион под размер соответствующий его стартовому адресу (проверено на практике).

Кстати, в STM32Cube есть несколько ошибок. Например:


Видно, что стартовый адрес выровнен на 64 Кб. А размер региона хотим 256 Кб. В этом случае придется создавать 3 региона: первый 64 Кб, второй — 128 Кб, и третий 64 Кб.

Задавать нужно только регионы с отличными от стандартных свойствами. Дело в том, что атрибуты всех памятей при включении кэша процессора описаны в архитектуре ARM. Есть стандартный набор свойств (к примеру, поэтому память SRAM STM32F7 имеет режим “write-back write-allocate” по умолчанию), Поэтому если вам понадобится не стандартный режим для какой-то из памятей, то нужно будет задать его свойства через MPU. При этом внутри региона можно задать подрегион со своими свойствами, Выделив внутри этого региона еще один с большим приоритетом с требуемыми свойствами.

Как следует из документации (раздел 2.3 Embedded SRAM), первые 64 Кб SRAM в STM32F7 некэшируемые. В самой архитектуре ARMv7-M по адресу 0x20000000 находится память SRAM. TCM тоже относится к SRAM, но находится на другой шине относительно остальных памятей (SRAM1 и SRAM2), и располагается “ближе” к процессору. Из-за этого данная память очень быстрая, по сути дела, имеет такую же скорость как и кэш. И из за этого кэширование не нужно, и этот регион не возможно сделать кэшируемым. По сути TCM это еще один такой вот кэш.

Instruction cache

Стоит отметить, что все рассмотренное выше относится к кэшу данных (D-Cache). Но кроме кэша данных в ARMv7-M предусмотрен и кэш инструкций — Instruction cache (I-Cache). I-Cache позволяет перенести часть исполняемых (и следующих) инструкций в кэш, что может значительно ускорить работу программы. Особенно, в тех случаях, когда код находится в более медленной памяти чем FLASH, к примеру, QSPI.

Чтобы уменьшить непредсказуемость в тестах с кэшем ниже, мы намеренно отключим I-Cache и будем думать исключительно о данных.

При этом хочу отметить, что включается I-Cache достаточно просто и не требует никаких дополнительных действий со стороны MPU в отличие от D-Cache.

Синтетические тесты

После обсуждения теоретической части, давайте перейдем к тестам, чтобы лучше понять разницу и сферы применимости той или иной модели. Как я и говорил выше, отключаем I-Cache и работаем только с D-Cache. Так же я намеренно компилирую с -O0, чтобы циклы в тестах не оптимизировались. Тестировать будем через внешнюю память SDRAM. С помощью MPU я разметил регион 64 Кб, и будем выставлять этому региону нужные нам атрибуты.

Так как тесты с кэшами очень капризные и находятся под влиянием всего и вся в системе — сделаем код линейным и непрерывным. Для этого отключаем прерывания. Так же, замерять время будем не таймерами, а DWT (Data Watchpoint and Trace unit), в котором есть 32 битный счетчик процессорных тактов. На его основе (на просторах интернета) люди делают микросекундные задержки в драйверах. Счетчик довольно быстро переполняется на системной частоте 216 МГц, но до 20 секунд померить можно. Просто будем об этом помнить, и сделаем тесты в этом временном интервале, предварительно обнуляя счетчик тактов перед стартом.

Посмотреть полные коды тестов можно тут. Все тесты были проведены на плате 32F769IDISCOVERY.

Non-cacheable memory VS. write-back

Итак, начнем с совсем простых тестов.

Просто последовательно пишем в память.


Так же последовательно пишем в память, но не по одному байту за раз, а немного развернем циклы.


Так же последовательно пишем в память, но теперь еще и чтение добавим.


Если запустить все эти три теста, то они дадут абсолютно одинаковый результат какой бы режим вы не выбрали:


И это резонно, SDRAM не такая уж и медленная, особенно если учесть внутренние буферы FMC, через который она подключена. Тем не менее, я ожидал небольшой вариации в цифрах, но оказалось что ее на этих тестах нет. Ну что же, будем думать дальше.

Давайте попробуем “подпортить” жизнь SDRAM смешивая чтения и записи. Для этого развернем циклы добавим такую распространенную на практике вещь как инкремент элемента массива:


Уже лучше — с кэшем оказалось на пол секунды быстрей. Давайте попробуем еще усложнить тест — добавим доступ по “разреженным” индексам. К примеру, с одним индексом:


Теперь разница с кэшем стала более чем заметна! И в довершение введем второй такой индекс:


Видим как время для не кэшируемой памяти подросло еще почти на секунду, в то время как для кэша осталось прежним.

Write allocate VS. no write allocate

Теперь давайте разберемся с режимом “write allocate”. Тут еще сложней увидеть разницу, т.к. если в ситуации между не кэшируемой памятью и “write-back” становятся хорошо видны уже начиная с 4-го теста, то различия между “write allocate” и “no write allocate” до сих пор тестами не вскрылись. Давайте подумаем — когда “write allocate” будет быстрей? Например, когда у вас есть много записей в последовательные ячейки памяти, а чтений из этих ячеек памяти мало. В этом случае в режиме “no write allocate” будем получать постоянные промахи, и подгружаться по чтению в кэш будут совсем не те элементы. Давайте смоделируем такую ситуацию:


Здесь в 15 из 16 записей выставляется константа VALUE, в то время как чтение осуществляется из разных (и не связанных с записью) элементов arr[i % 1024 + (j % 256) * 128]. Получается, что при стратегии no write allocate только эти элементы и будут загружаться в кэш. Причина по которой используется такая индексация (i % 1024 + (j % 256) * 128) — “ухудшение скорости” FMC/SDRAM. Так как обращения к памяти по существенно различным (не последовательным) адресам, могут существенно сказываться на скорости работы.


Наконец-то получили разницу, пусть и не настолько заметную, но уже видимую. То есть наша гипотеза подтвердилась.

И наконец, самый сложный, на мой взгляд, случай. Хотим понять когда “no write allocate” лучше чем “write allocate”. Первый лучше если мы “часто” обращаемся к адресам, с которыми в ближайшее время работать не будем. Такие данные, не нужно заносить в кэш.

В следующем тесте в случае “write allocate” данные будут заполняться по чтению и по записи. Я сделал массив “arr2” на 64 Кб, поэтому кэш будет сбрасываться, чтобы подкачать новые данные. В случае же с “no write allocate” я сделал массив “arr” на 4096 байт, и только он попадет в кэш, а значит данные кэша сбрасываться в память не будут. За счет этого и попробуем получить хотя бы небольшой выигрыш.


Видно, что “write-back” “write allocate” режим чуть-чуть быстрей. Но главное, что быстрей.

Практические примеры

Давайте перейдем от синтетических примеров к реальным.

Один из самых простых — это ping. Его легко запустить, а время можно смотреть прямо на хосте. Embox был собран с оптимизацией -O2. Сразу приведу результаты:

OpenCV

Еще одним примером реальной задачи на которой мы хотели попробовать работу подсистемы cache это OpenCV на STM32F7. В той статье было показано, что запустить вполне реально, но производительность была довольно низкая. Мы используем для демонстрации стандартный пример, который выделяет границы на основе фильтра Canny. Давайте измерим время работы с кешами (и D-cache и I-cache) и без.


То есть, 926ms и 134ms ускорение почти в 7 раз.

На самом деле у нас достаточно часто спрашивают про OpenCV на STM32, в частности какая производительность. Получается FPS конечно не высокий, но 5 кадров в секунду, вполне реально получить.

Не кэшируемая или кэшируемая память, но с cache invalidate?

В реальных устройствах повсеместно используется DMA, естественно с ним связаны трудности, ведь нужно синхронизировать память даже для режима “write-through”. Возникает естественное желание просто выделить кусок памяти который будет не кэшируемый и использовать его при работе с DMA. Немного отвлекусь. В Linux это делается функцию через dma_coherent_alloc(). И да, это очень эффективный метод, например, когда идет работа с сетевыми пакетами в ОС, пользовательские данные проходят большой этап обработки прежде чем дойти до драйвера, а в драйвере подготовленные данные со всем шапками копируются в буферы, которые используют не кэшируемую память.

А есть случаи когда в драйвере с DMA более предпочтителен clean/invalidate? Да, есть. К примеру, видеопамять, которая нас и побудила более подробно разобраться с работой cache (). В режиме двойной буферизации у системы есть два буфера, в которые она поочередно рисует, а потом отдает видеоконтроллеру. Если делать такую память не кэшируемой, то случится падение в производительности. Поэтому лучше сделать clean перед тем как отдать буфер в видеоконтроллер.

Заключение

Мы немного разобрались с разными вида кэшей в ARMv7m: write-back, write-through, а также настроек “write allocate” и “no write allocate”. Построили синтетические тесты, в которых попытались выяснить когда один режим лучше другого, а также рассмотрели практические примеры с ping и OpenCV. В Embox мы еще только занимаемся данной тематикой, поэтому соответствующая подсистема пока прорабатывается. Хотя достоинства в использовании кэшей определенно заметны.

Все примеры можно посмотреть и воспроизвести собрав Embox из открытого репозитория.

Если вам интересна тема системного программирования и OSDev, то уже завтра будет проходить конференция OS Day! В этом году она проходит в онлайне, так что желающие не пропустите! Embox выступает завтра в 12.00

Читайте также: