Jump to content
    

упаковка данных в структуре

Добрый день,

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

 

Во внешней памяти данные хранятся в виде последовательности структур, сами структуры при этом могут иметь не кратное 4 количество байт, например {int32_t x;chat y;}. Я начитался, что работа с такими данными медленная, а может и приводить к ошибкам, поэтому готов делать memcpy в выровненный блок данных и с ним уже работать. Сами структуры при этом объявляются packed.

Но возникает проблема - я не знаю как объявить область памяти так, чтобы в ней разместить эти же структуры, но выравненными. Получается, что для каждой выравненной структуры я должен делать отдельное выделение памяти что делает код запутанным и нечитаемым. Как быть? Как вообще правильно работать с такими данными?

 

Отдельный вопрос про double. Компилятор не ругается на выравнивание структуры типа {double x[2][3];int16_t y;}. Непонятно почему и как такую структуру следует выравнивать - по 2, 4 или 8 байтам?

 

Share this post


Link to post
Share on other sites

10 minutes ago, natsu said:

Я начитался...

(C) "Не читайте советских газет по утрам". 

Очень сильно нужно постараться, чтобы эта "медленная работа" сказывалась на работе проекта. А если она уже сказывается, то проект можно смело закрывать :)

По поводу ошибок, так они если и возникают, то вовсе не от того упакованная структура или нет.

 

  

10 minutes ago, natsu said:

Как быть?

Для начала следует указать о каком "железе" идет речь и по возможности о применяемом софте для программирования.

Share this post


Link to post
Share on other sites

keil 5.33 для stm32f7 - но это пока. Потом микроконтроллер будет подобран из соображений достаточности. И не хотелось бы переделок программы.  

А ничего, если адрес время от времени будет оказываться нечетным?

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

Share this post


Link to post
Share on other sites

31 минуту назад, natsu сказал:

Но возникает проблема - я не знаю как объявить область памяти так, чтобы в ней разместить эти же структуры, но выравненными

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

31 минуту назад, natsu сказал:

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

Медленная - да, а все ошибки 99.999% ваши и находятся в вашем исходнике.

Share this post


Link to post
Share on other sites

1) Определить тип структуры.
2) Определить упакованный тип этой структуры.
3) Объекты, которые в памяти не выровнены - типа 2.
4) Нужный объект копируется в объект типа 1, дальнейшая работа ведется с ним.

Можно и напрямую обращаться к невыровненным объектам, но нужно пользоваться соотв. квалификаторами __unaligned.

А можно и подумать насчет принудительного выравнивания, если память позволяет.

Share this post


Link to post
Share on other sites

30 minutes ago, natsu said:

Во внешней памяти данные хранятся в виде . . . .

так что медленно, внутренняя работа STM32 или работа с этим "внешним". Что пишется, архив, лог, параметры итд ?

Share this post


Link to post
Share on other sites

7 минут назад, Сергей Борщ сказал:

Это-то как раз просто - создайте union вашей структуры с uintX_t.

Но структуры встречаются не маленькими. Придется высчитывать сколько интов надо чтоб покрыть размер. А если придется что-то изменить? 

 

7 минут назад, Arlleex сказал:

1) Определить тип структуры.
2) Определить упакованный тип этой структуры.

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

 

8 минут назад, k155la3 сказал:

ак что медленно, внутренняя работа STM32 или работа с этим "внешним". Что пишется, архив, лог, параметры итд ?

Внешняя память по шине 40МГц, не сказать чтоб медленно - (на прерываниях получается дольше чем с опросом!). Но памяти всегда мало, приходится экономить. Пишутся логи событий и несколько разных профилей с измеренными данными, с разной периодичностью и сроком хранения. Для отдачи данных приходится их перечитывать и обрабатывать.

 

На самом деле я спрашивал как работать с такими данными. Т.е. насколько это безопасно и насколько необходимо все таки их выравнивать для обработки. Можно подробнее про __unaligned?  Или где почитать, а то толи я неправильно ищу толи не очень понимаю что искать.

Share this post


Link to post
Share on other sites

1 минуту назад, natsu сказал:

Можно подробнее про __unaligned?  Или где почитать, а то толи я неправильно ищу толи не очень понимаю что искать.

В описании на ARM Compiler 6.

Share this post


Link to post
Share on other sites

7 минут назад, natsu сказал:

Но структуры встречаются не маленькими. Придется высчитывать сколько интов надо чтоб покрыть размер.

Выравнивается начало структуры. Для этого достаточно union с одним-единственным uintX_t. Все остальные члены структуры автоматически подразумеваются выровненными. Для неупакованных структур это выполняется автоматически - компилятор сам добавляет в структуру пропуски для выравнивания каждого члена исходя из его размера. Для упакованных структур это надо делать вручную, сортируя члены при объявлении структуры. memcpy никак не сможет сделать это за вас.

Share this post


Link to post
Share on other sites

31 минуту назад, Сергей Борщ сказал:

Это-то как раз просто - создайте union вашей структуры с uintX_t.

Самые критичные места у меня в момент записи, там как раз я записываю подготовленные и естественно выравненные данные в поток, внутри структуры все данные расположены по убыванию размера. Но однажды определив __packed структуру компилятор будет ВСЕГДА считать ее невыравненной - в документации на компилятор сказано, что отказаться от невыравненности нельзя. И получится, что вся затея с выравниванием не имеет смысла. (Хотя в описании на __unaligned сказано, что он необходим для упакованных структур, что противоречит описанию __packed)

 

Получается проще делать структуры выравненными. Но тогда истинный (упакованный) размер придется считать руками для каждой структуры.

И так и непонятно, нужно ли выравнивание вообще, насколько код медленнее и объемнее с неупакованными данными?

Share this post


Link to post
Share on other sites

1 hour ago, natsu said:

Во внешней памяти данные хранятся в виде . . . .

по поводу упоминания memcpy()

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

Share this post


Link to post
Share on other sites

18 минут назад, natsu сказал:

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

Тогда без разницы - упакованной её считает компилятор или неупакованной. Это никак не скажется на скорости доступа к её членам: скорость будет максимальной.

Вы смешиваете понятия упакованности и выравнивания. Это 2 разных понятия. Скорость может падать на обращении к невыровненным данным. А в данном случае все данные получаются выравненными.

Если конечно базовый адрес самой структуры выравнен на размер её максимального члена.

Цитата

Но однажды определив __packed структуру компилятор будет ВСЕГДА считать ее невыравненной - в документации на компилятор сказано, что отказаться от невыравненности нельзя. И получится, что вся затея с выравниванием не имеет смысла.

Имеет. См. выше.

42 минуты назад, natsu сказал:

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

Например - с помощью макросов:

#define TMPL_ZData { \
  u32 data0;         \
  u8 data1;          \
  u32 data2;         \
  u8 data3;          \
}

struct ZData TMPL_ZData zData;
__packed struct ZDataP TMPL_ZData zDataP;

Это для IAR, но и для других компиляторов, не поддерживающих __packed, можно сделать аналогично.

Share this post


Link to post
Share on other sites

1 hour ago, Forger said:

Очень сильно нужно постараться, чтобы эта "медленная работа" сказывалась на работе проекта. А если она уже сказывается, то проект можно смело закрывать :)

у людей разные задачи бывают. У меня почти всегда - это затык и все по несколько раз приходится перетрясывать в памяти, но у меня обычно - это линейная алгебра, то есть приходит сильно упакованное данное по DMA SPI, и если его далее так и использовать, то все садится по производительности из-за кривого доступа по памяти или кеша.

1 hour ago, Forger said:

По поводу ошибок, так они если и возникают, то вовсе не от того упакованная структура или нет.

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

 

2ТС: правильно заметили, что саму структуру надо в памяти выравнять, но и не забыть выравнять все внутри, чтобы 4-байтовые с адреса кратного 4, 2-байтовые с адреса кратного 2 лежали, ну и про 8 байтовые не забыть. Если есть битовые поля, их засунуть так, чтобы было минимальное число случаев, когда одно поле находится в нескольких байтах, и чтобы никогда не было, чтобы поле было в двух 4-байтовых словах. Все остальное - обычно не помогает, так как сама струрктура кешируется и все более-менее быстро доступно. В некоторых процессорах длина кэшевой строки может быть больше 4 байт, и тогда все надо выравнивать под эту длину, если хочется выжать максимум производительности, но не забывать, что кеш - это всегда очень мало, около 16Кбайт.

Share this post


Link to post
Share on other sites

6 минут назад, iiv сказал:

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

Какие-то чудеса рассказываете. На Cortex-M невыровненность доступа добавит наверное 1 такт к длительности команды чтения. И всё. Вместо 2 тактов станет 3. А с учётом того, что как правило - не бывает кода состоящего из одних только операций обращения к памяти, а они перемешаны с другими командами, то суммарный % замедления должен быть совсем невелик.

 

PS: Может у вас там (на ESP32) тупо разрешена генерация fault-ов при невыровненном доступе? :biggrin: И вызовы их обработчика и замедляют работу программы.

Share this post


Link to post
Share on other sites

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

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

 

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...