Битовые поля C/С++ по сравнению с побитовыми операторами для выделения битов, которые быстрее, лучше и более переносимы?
Мне нужно собрать несколько бит в байте следующим образом:
struct
{
char bit0: 1;
char bit1: 1;
} a;
if( a.bit1 ) /* etc */
или
if( a & 0x2 ) /* etc */
Из ясности исходного кода для меня довольно очевидно, что битполы более аккуратные. Но какой вариант быстрее? Я знаю, что разница в скорости будет не слишком большой, но если я могу использовать любой из них, если один быстрее, лучше.
С другой стороны, я читал, что битовые поля не гарантируют, что биты будут расположены в одном и том же порядке на разных платформах, и я хочу, чтобы мой код был переносимым.
Примечания. Если вы планируете отвечать на "профиль", я буду, но, поскольку я ленив, если у кого-то уже есть ответ, гораздо лучше.
Код может быть неправильным, вы можете исправить меня, если хотите, но помните, какой смысл в этом вопросе, и попробуйте ответить и на него.
Ответы
Ответ 1
Я предпочел бы использовать второй пример для максимальной переносимости. Как отметил Нил Баттерворт, использование битполей - это только для собственного процессора. Хорошо, подумайте об этом, что произойдет, если Intel x86 завтра выйдет из бизнеса, код застрянет, а это значит, что нужно повторно реализовать битподы для другого процессора, скажем, RISC-чипа.
Вы должны посмотреть на картинку большего размера и спросить, как OpenBSD удалось перенести свои BSD-системы на множество платформ, используя одну кодовую базу? Хорошо, я признаю, что это немного выше, и дискуссионный и субъективный, но реалистично, если вы хотите перенести код на другую платформу, его способ сделать это, используя второй пример, который вы использовали в своем вопросе.
Не только, что компиляторы для разных платформ будут иметь свой собственный способ заполнения, выравнивая битовые поля для процессора, в котором включен компилятор. И, кроме того, как насчет энтузиазма процессора?
Никогда не полагайтесь на битполы как волшебную пулю. Если вам нужна скорость для процессора и будет зафиксирована на нем, то есть нет намерения портировать, тогда не стесняйтесь использовать битовые поля. У вас не может быть обоих!
Ответ 2
Битвые поля делают код более понятным, если они используются соответствующим образом. Я бы использовал битовые поля только как компактное устройство. Одно из распространенных мест, которое я видел, они используются в компиляторах: часто информация о типе или символе состоит из группы флагов true/false. Битвые поля идеальны здесь, так как у типичной программы будет много тысяч этих узлов, созданных при компиляции.
Я бы не использовал битовые поля для выполнения обычного встроенного программирования: чтение и запись регистров устройства. Я предпочитаю использовать сдвиги и маски здесь, потому что вы получаете именно те биты, о которых вам говорит документация, и вам не нужно беспокоиться о различиях в реализации битовых полей в разных компиляторах.
Что касается скорости, хороший компилятор даст тот же код для бит-полей, которые будут маскироваться.
Ответ 3
С битовыми полями были мертворожденные с момента их изобретения - по неизвестной причине. Люди не любили их и вместо этого использовали побитовые операторы. Вы должны ожидать, что другие разработчики не поймут код битового поля C.
В отношении чего происходит быстрее: нерелевантно. Любой оптимизирующий компилятор (что означает практически все) заставит код делать то же самое в любой нотации. Это распространенное заблуждение программистов C, что компилятор будет просто искать и заменять ключевые слова в сборке. Современные компиляторы используют исходный код в качестве плана для того, что должно быть достигнуто, а затем испускать код, который часто выглядит совсем другим, но достигает намеченного результата.
Ответ 4
Первый явный и независимо от скорости второе выражение подвержено ошибкам, потому что любое изменение в вашей структуре может сделать неправильное второе выражение.
Итак, используйте первый.
Ответ 5
Если вы хотите переносимость, избегайте битполей. И если вы заинтересованы в производительности конкретного кода, нет альтернативы написанию собственных тестов. Помните, битполы будут использовать побитовые команды процессора под капотом.
Ответ 6
Я думаю, что программист С будет стремиться ко второму варианту использования бит-масок и логических операций, чтобы вывести значение каждого бита. Вместо того, чтобы иметь код, замусоренный шестнадцатеричными значениями, будут перечислены перечисления или, как правило, при использовании более сложных операций, макросы для получения/установки определенных бит. Я слышал на виноградной лозе, что эта структура реализовала битподы медленнее.
Ответ 7
Не читайте много в "не переносимых битовых полях". Существуют два аспекта битовых полей, которые определены реализацией: подпись и макет, а также один неуказанный: выравнивание единицы распределения, в которой они упакованы. Если вам не нужно что-либо еще, что эффект упаковки, используя их, является переносимым (при условии, что вы явно указываете ключевое слово signed
, где это необходимо), как вызовы функций, которые также имеют неопределенные свойства.
Относительно производительности, профиль - лучший ответ, который вы можете получить. В идеальном мире между этими двумя статьями не было бы разницы. На практике могут быть некоторые, но я могу думать о многих причинах в одном направлении, как о другом. И он может быть очень чувствительным к контексту (логически бессмысленная разница между неподписанными и подписанными для примера), поэтому измерять в контексте...
Подводя итог, различие, таким образом, является главным образом различием стиля в тех случаях, когда у вас действительно выбор (т.е. если важна точная компоновка). В этом случае это оптимизация (по размеру, а не по скорости), и поэтому я бы предпочел сначала написать код без него и добавить его, если необходимо. Таким образом, бит-поля являются очевидным выбором (модификации, которые нужно выполнить, являются наименьшими для достижения результата и содержатся в уникальном месте определения вместо того, чтобы распространяться на все места использования).