96-битное длинное битовое поле с выровненными по октетам подполями - PullRequest
3 голосов
/ 15 ноября 2011

Мне нужна 96-битная структура, в которую я могу поместить пользовательские битовые поля.Длина полей повсюду: 8, 3, 26, 56.Важно, чтобы они оставались такими точными длинами (с одним исключением, см. Ниже).

Я вижу множество способов объединения данных в одно компактное поле: std::bitset, struct s (удерживая полясмежно), и, конечно, просто используя int с.Однако:

  • Подход bitset проблематичен, поскольку операции должны выполняться очень быстро: набор битов не обеспечивает метод для мгновенной установки диапазона (x..y) из всего диапазона (0..96), с одной атомарной операцией.Будь я проклят, если я собираюсь выполнить цикл для установки отдельных битов.

  • Подход struct проблематичен из-за этого ограничения на длину.

  • Подход int проблематичен, потому что int64_t недостаточно длинный.Конечно, я могу использовать int32_t наряду с этим, однако см. Ниже.

Одно из очевидных решений - поместить поля 56 + 8 в int64_t, а остальныев int32_t.Проблема здесь в том, что 56 -длинное поле - единственное поле, которое на самом деле может быть уменьшено в дальнейшем при разработке, и это будет означать, что у меня будет несколько запасных битов в int64_t и некоторые 32 - (26 + 3) = 3 запасных битовв int32_t.

Есть ли способы сохранить их как можно более компактно (с точки зрения кода), сохраняя при этом возможность доступа к широким областям путем маскировки (в отличие от std::bitset)?

Ответы [ 3 ]

3 голосов
/ 15 ноября 2011

Хорошо, у вас классическая ситуация по сравнению со скоростью.Я собираюсь спросить, это ситуация, когда каждый бит имеет значение?Разве это так важно, если пара битов не совсем используется?Кодеру C во мне нравится либо массив из 3 32-битных значений, либо 64-битный 32-битный подход.Оптимизатору во мне не нравится тот факт, что 96-битные структуры данных не являются полностью дружественными к кешу и скорее будут заполнены 128-битными или, по крайней мере, не будут доступны через 4-байтовые границы в максимально возможной степени.

Использование 64-битного значения, в зависимости от вашей целевой платформы, позволяет маскировать всю 56-битную запись в 1 инструкции, тогда как 32-битная версия потребует как минимум 2 операции.Но если бы вы могли уменьшить это значение до 32-битных (или до 64-битных), тогда никакое маскирование и полная скорость не будут достигнуты, если вы сохраните это 64-битное значение на границах 64-битных адресов.Некоторые цели позволят вам получить доступ к несогласованным данным со штрафом, тогда как другие на самом деле будут генерировать исключения.

Самый безопасный способ - это массив из 3 32-битных значений.Ваше выравнивание гарантировано, математика может быть простой, если вы не перекрываете 32-битные границы своими битовыми полями, и она будет наиболее переносимой.Если вы должны преодолеть границу, вы получите быстрый удар с дополнительной маскировкой и смещением.Но, и это главный вопрос, действительно ли вы действительно уверены, что доступ к этим данным является проблемой скорости?У вас есть профиль, показывающий, что это узкое место?Если нет, я бы просто выбрал решение C ++ для битовых полей и назвал его хорошим.Безопаснее и проще в использовании - это всегда выигрыш.

2 голосов
/ 15 ноября 2011

Как я уже сказал в своем комментарии, используйте bitset, в нем есть все необходимые бинарные операторы, например:

std::bitset<96> foo(0xFF1); // set bit 1 & bits 5-12

// to test
std::bitset<96> mask(0xFF0); // are bits 5-12 set?
// test, yes the global & operator will create a temporary
if ((mask & foo) == mask)
{
  // do stuff...
}
2 голосов
/ 15 ноября 2011
uint32_t bits[3];

Там, 96 бит, в виде POD, которые вы можете копать столько, сколько хотите.

Конечно, если вам нужна скорость, очень вероятно, что отсутствие единственного битового поля ускорит процесс. Но если вы do хотите упаковать данные на этом уровне, а интерфейс std::bitset слишком ограничен, я бы использовал простой массив.

...