memory_alias

h4tred
h4tred

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

Проблемы: как-то нужно решать вопрос с невыровненными данными.

Ссылка в тему: http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

Примеры, когда может пригодиться:

1) сеть, позволит вычитывать данные из ядерных буфферов максимально большими блоками, разбирать уже по ходу

2) USB: на многих устройствах есть ограничения на выравнивание и кратность размера блока для чтения из железа, поэтому, обычно, заводят один буффера с правильными __attribute__((align(x))), куда читают данные из DATA фазы Setup запроса. Но сами данные могут быть определены на основании другой информации из запроса: Target/Index/Value и так далее.

Насколько я понимаю, в современных реалиях нет официального способа так делать, хотя вариант c union работает на всех (? - не уверен) компиляторах - их тоже люди делают.

-2
рейтинг
10 комментариев
yndx-antoshkka
Я не понял, что именно вы предлагаете. Опишите решение, как вы его видите.
yndx-antoshkka
h4tred
yndx-antoshkka, это: cpp.sh/7lfyt, но так поступать, насколько я знаю/понимаю, не корректно. Хотя и работает на шланге/gcc/msvc/icc.
h4tred
yndx-antoshkka
h4tred, замените uint8_t на std::byte, а memory_alias<Foo>(buf) на reinterpret_cast<Foo*>(buf)

Никаких проблем с алиасингом std::byte* и Foo* не возникнет, по Стандарту С++ компилятор должен полагать что std::byte* (как и unsigned char*, как и char*) могут алиаситься со всеми типами данных.
yndx-antoshkka
h4tred
yndx-antoshkka, стоп, в char* да - могут, но обратно же нет: stackoverflow.com/questions/23848188/strict-aliasing-rule-and-char-pointers
h4tred
yndx-antoshkka
Вы правы, и правда не может. Судя по networking TS и стандарту ожидается что вы будете поступать наоборот. Будете создавать объект нужного вам типа, и в него читать байты cpp.sh/53hin

Это может быть неудобно в ряде случаев. Можно попробовать разрешить кастовать в обратную сторону. Но у меня нет информации о том, как это повлияет на производительность кода.

Хотите попробовать написать подобное предложение, чтобы разрешить делать reinterpret_cast<Foo*>(buf) на уровне языка?
yndx-antoshkka
h4tred
yndx-antoshkka,
> Судя по networking TS

при этом в самих Беркли-сокетах такое широко используется, когда структура, sockaddr может быть sockaddr_in/sockaddr_in6/in_addr/in6_addr. Тут приходится ещё и кастить даже не между Foo и char*, но и между Foo/Bar. Причём (где-то видел, но не могу найти подтверждения сейчас) для такого случая есть лазейка: типа если первые поля несовместимых структур совпадают, то обращаться к ним можно

> Хотите попробовать написать подобное предложение, чтобы разрешить делать reinterpret_cast<Foo*>(buf) на уровне языка?

Тут бы решить что лучше. Вариант с Union работает на всех компиляторах. Возможно просто его узаконить (type-punning), но мне бы пришлась по душе отдельная команда, вроде той, что я предложил: да бы было явно видно намерения программиста и он осознаёт последствия своего выбора (в том числе о возможных накладных расходах в случае отсутствия выравнивания). Есть ещё в GCC атрибут __may_alias__, который вроде бы частично призван решать проблему (но, судя по всему, работает только для C-компилятора). Плюс поголовно предлагают использовать memcpy, типа оно хорошо оптимизируется. Но проблема в том, что при оптимизации по тактам/командам, у нас возникают две копии данных. Когда у тебя 128кб памяти всего - каждый байт ценен, часто даже в ущерб быстродействию.

Оставлю тут парочку ссылок:
* blog.regehr.org/archives/959
* blog.regehr.org/archives/1307

Предложение написать можно. Но я в формальном языке не силён (да и вообще не айс :)). Если есть какие-то "рыбы", набор правил и рекомендаций, было бы неплохо их получить. В любом случае - попытка не пытка.
h4tred
mrgordonfreman
Тут имеется в виду проблема, когда sizeof(Foo) > sizeof(id) + sizeof(a) + sizeof(b) и при кастовании указателя на данные к Foo можем не в те байты писать?
mrgordonfreman
h4tred
mrgordonfreman, может. потому и явная операция приведения, сродни reiterpret_cast: в противовес old C-cast - искать проще по коду и для разбираться - почему так . Плюс есть всякие #pragma pack/__attribute__((packed)), которые убирают падинги в структурах. Во всякой эмбедщины, когда мало ресурсов, так удобно делать.
h4tred
mrgordonfreman
h4tred, а как должна работать эта операция, если на структуру явно задано ненулевое выравнивание?
mrgordonfreman
h4tred
mrgordonfreman, не единичное, вы хотели сказать :) потому я за явную операцию, что бы было ясно видно намерения разработчика. Но обычная ситуация такая: адрес буффера выровнен по границе, которая больше и/или кратна структурам (у меня это 16 байт /требования железа/, тогда как для структур используется 4). Структура пакованная, что вносит дополнительный оверхед при доступе к полям, но я готов на эту плату. По косвенным данным я понимаю какие данные в буффере и привожу к нужному типу. По выравниванию (стартового адреса структуры) тут нарушений нет.

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

Ещё временами такой трансфер осуществляется внутри какого-то железа, где точно известны параметры выравнивания и просто не делается паковка на струтуры и преобразование в буффер и обратно происходит вообще без каких-то последствий в плане производительности.
h4tred
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).