|
FatFs на STM32 - кеширование FAT32?, чтобы ускорить f_open() |
|
|
|
Jul 5 2010, 09:55
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Всем доброго времени суток.
На девайсе под управлением STM32 есть процедура, которая создаёт сортированный по алфавиту список файлов FAT32 диска на флеш карточке. Используется FatFs версии 0.08. Количество файлов в списке может быть максимум 500.
На днях обратил внимание, что львиная доля времени (около 90%), затрачиваемого на сортировку, потребляется функцией файловой системы f_open(). Поковырявшись в её дебрях, обнаружил, что в свою очередь это вызвано подфункцией move_window(), которая подгружает по одному сектору из, вероятно, каталога фат.
То есть чтобы открыть файл, файловая система продирается через структуру фат до того, как найдёт запись с информацией по нужному файлу. Считывание ведётся по одному сектору. И так для каждого файла.
Вот и подумалось тут, а почему бы не ввести кеширование для секторов, запрашиваемых функцией move_window()?
Главный вопрос здесь - хватит ли для этого 10-20 килобайт оперативки? К сожалению, приходится довольствоваться только внутренним ОЗУ контроллера.
Наверное, для FAT32 с большим количеством файлов кеширование 20 килобайт (40 секторов) не будет эффективным?
|
|
|
|
|
Jul 5 2010, 10:35
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(aaarrr @ Jul 5 2010, 14:04)  Ну почему же? Любой кэш лучше, чем его отсутствие. Особенно если использовать чтение группы блоков для заполнения строки. Хм, спасибо, тогда попробую, когда будет время. move_window() запрашивает по одному сектору, как тут организуешь мультисекторное чтение? Если только запросы будут идти последовательно по порядку. В любом случае, надо сначала посмотреть, как именно идёт чтение - организовать лог и писать туда номера запрашиваемых секторов. Тогда будет видна общая картина
|
|
|
|
|
Jul 5 2010, 12:12
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(sonycman @ Jul 5 2010, 14:52)  Конечно интересно, опишите пожалуйста  Хорошо. Используются две структуры: Код struct SD_CACHE_ENTRY { struct SD_CACHE_ENTRY *next; unsigned int sec; unsigned char *data; };
struct SD_WRITE_BUFFER_ENTRY { unsigned int sec; unsigned char *data; }; Первая описывает строку кэша, из этих записей формируется односвязный список. Вторая - для буфера записи, сложены в виде массива. Отдельно вводим переменную, указывающую текущее заполнение буфера записи - sd_wbuff_level. Данные отделены от списка для более эффективной работы кэша самого процессора, для STM32 можно буферы внести в структуру. При инициализации диска формируется список из SD_CACHE_ENTRY, поля sec заполняются значением SD_CACHE_INVALID_SEC (0xffffffff); переменную sd_wbuff_level устанавливаем в 0. Число секторов в строке у меня выбрано равным 8. Чтение: 1. Последовательно проходим по списку, если запрашиваемый сектор находится внутри имеющейся в кэш строки: 1.1. Копируем данные из кэш 1.2. Переставляем строку в начало списка (это нужно для увеличения производительности - редко использующиеся строки будут постепенно падать в конец списка) 2. Если данные в кэш отсутствуют, то: 2.1. Сбрасываем на диск буфер записи (некоторое упрощение, чтобы не искать данные в буфере) 2.2. Читаем с карты строку, заполняя при этом последнюю из записей, по которым ходили в п.1 2.3. Переставляем заполненную строку в начало списка Запись: 1. Проходим по списку. Если нужная строка найдена, заменяем в ней данные и запоминаем адрес 2. Проходим по буферу записи до уровня sd_wbuff_level, добавляем запись и увеличиваем sd_wbuff_level, если целевой сектор отсутствует 3. При заполнении структуры буфера записи заменяем адрес в SD_WRITE_BUFFER_ENTRY на адрес в кэше, если в п.1 строка была найдена (исключаем лишнее копирование) Очистка буфера записи: 1. Сортируем записи в буфере по возрастанию номеров блоков 2. Пишем данные с использованием мультиблочной записи Вот как-то так. P.S. У меня, правда, файловая система написана в расчете на кэшируемый диск, поэтому не стесняется что-то прочитать или записать, когда надо. Если над этой системой будет надстроена еще одна, то производительность несколько снизится.
|
|
|
|
|
Jul 5 2010, 19:26
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(sonycman @ Jul 5 2010, 19:36)  Вероятно, move_window() читает/пишет только такие сектора, поэтому попробую привязать кеш именно к ней... Нет, move_window() универсальная, вызывается во всех случаях. Цитата(sonycman @ Jul 6 2010, 00:49)  Логичнее было бы закешировать только корневой каталог и FAT (при интенсивной работе с большим кол-вом файлов). А может и нет  Логичнее сделать кеширование как написал aaarrr, и тогда в кеше автоматически будут храниться наиболее востребованные данные. Чаще идёт обращение к FAT - значит в кеше FAT. К данным (многократный парсинг одного файла) - в кеше данные. И не надо будет гадать ничего
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jul 5 2010, 19:43
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(AHTOXA @ Jul 5 2010, 23:26)  Нет, move_window() универсальная, вызывается во всех случаях. А во всех - это в каких? Я пока глубоко не вникал, но показалось, что эта функция с чтением/записью единственного сектора годится только для работы с каталогом. Цитата А может и нет  Логичнее сделать кеширование как написал aaarrr, и тогда в кеше автоматически будут храниться наиболее востребованные данные. Чаще идёт обращение к FAT - значит в кеше FAT. К данным (многократный парсинг одного файла) - в кеше данные. И не надо будет гадать ничего  Это смотря какие условия. К примеру - открыли мы файл (при этом кеш заполнился секторами корневого каталога), затем считали большой кусок этого файла. В кеше будет содержимое считанного файла, которое и без него читается быстро. Затем открываем другой файл - так как в кеше уже нет секторов каталога - снова идёт обращение к диску. Зачем мне это надо? Мне бы просто ускорить работу с каталогом
|
|
|
|
|
Jul 5 2010, 20:56
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(sonycman @ Jul 6 2010, 01:43)  А во всех - это в каких? Ну вообще во всех. Эта функция грузит сектор в единственный буфер объекта-файловой системы. Нужен сектор FAT - грузит его. Читаем данные - грузит их (и FAT конечно выгружен) Цитата Это смотря какие условия. К примеру - открыли мы файл (при этом кеш заполнился секторами корневого каталога), затем считали большой кусок этого файла. В кеше будет содержимое считанного файла, которое и без него читается быстро. Если файл больше чем кэш, то да, придётся предпринять дополнительные шаги. Первый из возможных - кешировать только сектора из FAT. Кстати, в другой известной имплементации FAT - чтение данных и чтение FAT изначально разделены. Хотя можно и с кешированием похимичить. Например, оставлять в кэше сектор лишь на второй раз.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jul 5 2010, 21:09
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(aaarrr @ Jul 6 2010, 00:21)  Увы, в условиях дефицита оперативки, придется так или иначе выкручиваться - добавлять в функцию чтения флаг запрета кэширования, например. Или встроить в FatFS отдельный кэш для каталогов и FAT. Просто прикрутить что-то снаружи не выйдет. Хотя даже с ограниченным объемом памяти приведенный мной вариант может несколько облегчить жизнь за счет наличия своего рода упреждающего чтения. Оптимальный способ для меня - использовать флаг разрешения кеширования. Тогда в исходники FatFs достаточно встроить пару строк (к примеру, в функцию f_open() или в move_window()), остальным займётся драйвер. Однако, это имеет смысл только в случае, когда все требуемые сектора каталога смогут поместиться в кеш. Иначе пользы будет ноль  Цитата(AHTOXA @ Jul 6 2010, 00:56)  Ну вообще во всех. Эта функция грузит сектор в единственный буфер объекта-файловой системы. Нужен сектор FAT - грузит его. Читаем данные - грузит их (и FAT конечно выгружен) А как же мультисекторные чтение/запись? Нет, конечно-же, не пугайте так  Эта функция используется только в конфигурации _FS_TINY. В остальных случаях идёт прямое обращение к disk_read() - к драйверу. Привыкли, наверное, к AVR? Цитата Если файл больше чем кэш, то да, придётся предпринять дополнительные шаги. Первый из возможных - кешировать только сектора из FAT. Да, именно это мне и требуется. Хотя, даже в случае наличия 20 килобайт памяти их для этого не хватает  Нужны "метры"!
|
|
|
|
|
Jul 5 2010, 21:12
|
Гуру
     
Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448

|
Цитата(sonycman @ Jul 6 2010, 00:59)  Однако, это имеет смысл только в случае, когда все требуемые сектора каталога смогут поместиться в кеш. Иначе пользы будет ноль  Если используется именно FAT32 (или не корневой каталог FAT16), то смысл будет иметь любой кэш объемом более одного кластера - так по крайней мере активный сектор FAT'а будет всегда "на плаву". Словом, некоторый выигрыш получите практически в любом случае. Конечно, чем больше объем - тем лучше. Можно сделать "как у больших", отдав всю не выделенную приложениям память под нужды дискового кэша. Правда, в случае STM32 без внешней ОЗУ это уже излишества.
|
|
|
|
|
Jul 5 2010, 21:35
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(aaarrr @ Jul 6 2010, 01:12)  Конечно, чем больше объем - тем лучше. Можно сделать "как у больших", отдав всю не выделенную приложениям память под нужды дискового кэша. Правда, в случае STM32 без внешней ОЗУ это уже излишества. Вот именно. Мне просто стало интересно, почему основное время процедура сортировки теряет не на собственно сортировке, а на банальном открытии файла  , вот и полез разбираться. Особых проблем не будет и без кеша, конечно. ЗЫ: Правильно ли я рассуждаю: к примеру, выделяем на кеш 40 секторов. Вызываем f_open() - она начинает двигаться по корневому каталогу в поисках целевого файла, последовательно считывая сектора. Скажем, находит его после считывания пятидесяти секторов, то есть в кеше находятся сектора 10 - 50. Вызываем f_open() снова - другой файл - чтение корневого каталога повторяется опять с первого сектора... которого в кеше нет. Соответственно, снова последовательно считываются все сектора заново, так как первый сектор замещает 10, второй - 11 и т.д. В этой ситуации пользы от кеша - ровно ноль. Но это теория. Надо проверить на практике.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|