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