У меня есть большой блок данных, где некоторые операции были бы самыми быстрыми, если бы блок рассматривался как массив из 64-битных целых без знака, а другие были бы самыми быстрыми, если бы они рассматривались как массив из 32 бит целые числа без знака. «Самый быстрый», я имею в виду самый быстрый в среднем для машин, которые будут запускать код. Моя цель - быть почти оптимальной во всех средах, на которых работает код, и я думаю, что это возможно, если я использую указатель void, отведя его к одному из двух типов для разыменования. Это приводит меня к моим вопросам:Стоимость доступа к объединению vs с использованием фундаментальных типов
1) Если я использую указатель на void, будет ли его перетаскивание на один из двух типов для разыменования будет медленнее, чем непосредственно с помощью указателя нужного типа?
2) Правильно ли я в своем понимании стандарта, что это не будет нарушать правила сглаживания и что оно не приведет к каким-либо неопределенным или неуказанным поведением? 32 и 64-битные типы, которые я использую, существуют и не имеют дополнения (это статическое утверждение).
3) Правильно ли я понимаю правила сглаживания, чтобы в основном выполнять две цели: обеспечить безопасность и гарантировать компилятор для оптимизации? Если это так, если все ситуации, когда код, который я обсуждаю, будут выполняться, таковы, что не происходит другого разыменования, могу ли я потерять значительную оптимизацию компилятора?
Я отметил это с помощью 'c11', потому что мне нужно доказать из стандарта c11, что поведение четко определено. Любые ссылки на стандарт будут оценены.
Наконец, я хотел бы затронуть вопрос, который, вероятно, будет затронут в ответах относительно «преждевременной оптимизации». Во-первых, этот код запускается в разнообразном вычислительном кластере, поскольку производительность имеет решающее значение, и я знаю, что даже снижение одной инструкции при разыменовании будет значительным. Во-вторых, тестирование этого на всех аппаратных средствах потребует времени, когда мне не нужно заканчивать проект. Существует множество различных типов аппаратного обеспечения, и у меня есть ограниченное количество времени на месте, чтобы фактически работать с оборудованием. Тем не менее, я уверен, что ответ на этот вопрос позволит мне в любом случае сделать правильный выбор дизайна.
EDIT: ответ и комментарии отметили, что с этим подходом существует проблема сглаживания, которую я проверил непосредственно в стандарте c11. Массив союзов потребует двух вычислений адресов и реферирования в 32-битном случае, поэтому я предпочел бы объединение массивов. Затем возникают вопросы:
1) Есть ли проблема с производительностью при использовании члена объединения в качестве массива, а не указателя на память? I.e., есть ли стоимость доступа члена профсоюза? Обратите внимание, что объявление двух указателей на массивы нарушает правила сглаживания, поэтому доступ должен производиться непосредственно через объединение.
2) Является ли содержимое массива гарантированным инвариантом при доступе через один массив, а затем через другой?
И ваша проверка времени выполнения (if()) не влияет на производительность? Если вы хотите, чтобы это действительно невероятно оптимально, сделайте 2 сборки и проверьте время установки, которое из 2 для установки. – BitTickler
Извините, я был не очень ясен в этом вопросе. Некоторые операции лучше с 64-битными типами, а другие - с 32-разрядными типами, поэтому я бы использовал оба члена объединения во время выполнения. – jack
IIRC, тип-punning через объединение явно разрешен в C99. Что касается части производительности, да, может быть штраф за производительность, если вы записываете в память с использованием одного размера слова и сразу читаете его с использованием другого размера слова или другого выравнивания. Когда это происходит, это зависит от процессора. Если вы хотите остаться в стороне от этого штрафа, я бы рекомендовал сохранить не менее 100 циклов между записью и чтением.(100 циклов, вероятно, слишком много, но я никогда не сравнивал его, чтобы получить более точный номер.) – Mysticial