July 16, 2020

FB_FileLoad и память роутера

Три в одном FB_FileLoad умеет загружать файл целиком в память. Сам по себе, другие ФБ для работы с файлами не нужны. Внутри него уже есть FB_FileOpen, FB_FileRead и FB_FileClose. Два основных вопроса (всего четыре):
  • асинхронность? Иначе говоря, долгая загрузка должна быть разбита на несколько циклов.
  • Как быстро загрузит большой файл?
  • Что с потреблением памяти?
  • Вдруг свой будет работать лучше?


Используется библиотека Tc2_System версия 3.4.22.0
Базовое время цикла: 1мс
Время цикла ПЛК задачи: 1мс
ПЛК: CX5010-1110


Увеличение памяти. Бесплатно


Память ПЛК = 512 Мб. Подсмотреть ее объем можно в панели управления:



Я пытался загрузить файл размером около 32.7Мб, а получил ошибку 0x070A (1802) ADSERR_DEVICE_NOMEMORY — не хватает памяти. Причем на старте нехватка памяти в FB_FileLoad не проверяется, а выясняется только по окончании марлезонского балету. Получается, что ФБ что-то там грузит до последнего, а потом внезапно память у него заканчивается. И судя по времени загрузки, загрузить получается, но что-то в конце не складывается, идет не так как было задумано и каменный цветок в итоге не выходит. Что с памятью?

Во первых, код захватывает память статически: ему памяти либо хватает, либо он просто не соберется. Вот что пишет компилятор:
Size of generated code: 58432 bytes
Size of global data: 35024176 bytes

Total allocated memory size for code and data: 64445200 bytes
Здесь все нормально. Возможно что-то выделяется динамически из памяти роутера? Начинаю увеличивать память роутера: при 100 Мб контроллер все еще работал, а при 300Мб вообще ничего не запустилось. Остановился на 50 Мб, должно хватить. И хватило.

Со времен статьи про New, Delete и память роутера, то есть про динамическую память произошли небольшие изменения. Объем памяти настраивается все там же: System → Real-Time → Router Memory → Configured Size [MB] - мегабайты!
...но теперь для активации необходимо не только активировать конфигурацию, но и перезагрузить контроллер. Подробнее см. как загружаются параметры конфигурации. Ну и теперь нам показывают чуть больше информации:



Когда памяти стало хватать, а файл стал загружаться без ошибок, я взял FB_GetRouterStatusInfo и стал пристально следить за памятью роутера:
Доступно всего, maxMem = 52428800

До старта копирования, maxMemAvail = 52395456
После копирования, maxMemAvail = 52395456
Тоже все нормально, но внезапно я подключил Scope и все встало на свои места. По вертикальной оси отмечены байты, отмасштабированные в тысячах, хотя мегабайты должны быть кратны 1024, поэтому есть небольшое расхождение в итоговых цифрах объема:


Вот эта просадка — в виде красной ямы, происходит в конце загрузки. Система куда-то грузит файл, затем выделяет под него память роутера, что-то делает, затем данные появляются в моем статическом массиве, и тут же освобождается память роутера. Зачем?

Загрузка длится долго, около 30 секунд (см. ниже), буфер заполняется постепенно, а итоговый массив с данными доступен весь и на всем протяжении загрузки. Возможно, именно по этой причине, от нас временно пытаются скрыть недозагруженные куски данных. Я попробовал выделить буфер динамически — через команду __NEW, ну и получил очередную нехватку памяти, так как от 50 мб осталось только 17 Мб и буфер под копирование выделять уже было не из чего.


Timeout


Если ФБ загружает долго (а большие файлы он грузит долго), то при таймауте ФБ выдает ошибку 0x0745 (1861) ADSERR_CLIENT_SYNCTIMEOUT. Но почему таймаут срабатывает через в два раза больший промежуток времени: задаешь 2сек — срабатывает через 4, задаешь 5 сек — срабатывает через 10; 10 сек → 20 сек. Причем код ошибки nErrId формируется через заданное время, а флаг ошибки bError устанавливается спустя еще один промежуток таймаута (начиная от установки кода ошибки). Итого, получаем удвоенное время. Это баг или фича?

Я поставил таймаут в один час T#1H и этого должно хватить на загрузку. И хватило.


Тестируем оригинал


Под рукой был большой файл бинарного содержания /Hard Disk/NK.BIN размером около ~32.7Mb. Загружаем его несколько раз:
= 26,078 сек.
= 26,075 сек.
= 26,095 сек.
  
Исходный код чтобы обратить внимание на таймаут в T#1h и напомнить про тест памяти:



Пишем свой FileLoad c буферами и чартами

  1 Кб = 92,368 сек
  4 Кб = 42,063 сек
  8 Кб = 33,781 сек
 16 Кб = 29,631 сек
 32 Кб = 28,251 сек
 64 Кб = 26,625 сек <<<<
128 Кб = 26,613 сек
  1 Мб = 26,179 сек
  4 Мб = 26,115 сек
По результатам строим самый настоящий график из Экселя. Ось Y конечно же не от нуля: там секунды, а они в 1000 раз больше миллисекунд, поэтому — норм. По оси абсцисс отложен размер временного буфера или размер блока байт, которые за раз подгружаются из файла (чтение идет блоками):



Исходный код не зависящий от памяти роутера

No comments

Post a Comment

Note: Only a member of this blog may post a comment.