Существуют ли практические ограничения на размер битовых масок?

Существует распространенный способ хранения нескольких значений в одной переменной с использованием битовой маски. Например, если у пользователя есть права на чтение, запись и выполнение элемента, это можно преобразовать в одно число, произнеся read = 4 (2^2), write = 2 (2^1), execute = 1 (2^0), а затем сложив их вместе, получить 7.

Я использую эту технику в нескольких веб-приложениях, где я обычно сохраняю переменную в поле и присваиваю ей тип MEDIUMINT или что-то еще, в зависимости от количества различных значений.

Что меня интересует, так это то, существует ли практический предел количества значений, которые вы можете хранить таким образом? Например, если число было больше 64, вы больше не могли использовать (64-битные) целые числа. Если бы это было так, что бы вы использовали? Как это повлияет на логику вашей программы (т.е. сможете ли вы по-прежнему использовать побитовые сравнения)?

Я знаю, что как только вы начнете получать действительно большие наборы значений, оптимальным решением будет другой метод, но меня интересуют границы этого метода.


person nickf    schedule 07.10.2008    source источник


Ответы (7)


Внезапно я бы написал функцию set_bit и get_bit, которая могла бы принимать массив байтов и битовое смещение в массиве, а также использовать некоторую битовую перестановку для установки/получения соответствующего бита в массиве. Что-то вроде этого (на C, но, надеюсь, вы поняли):

// sets the n-th bit in |bytes|. num_bytes is the number of bytes in the array
// result is 0 on success, non-zero on failure (offset out-of-bounds)
int set_bit(char* bytes, unsigned long num_bytes, unsigned long offset)
{
  // make sure offset is valid
  if(offset < 0 || offset > (num_bytes<<3)-1) { return -1; }

  //set the right bit
  bytes[offset >> 3] |= (1 << (offset & 0x7));

  return 0; //success 
}

//gets the n-th bit in |bytes|. num_bytes is the number of bytes in the array
// returns (-1) on error, 0 if bit is "off", positive number if "on"
int get_bit(char* bytes, unsigned long num_bytes, unsigned long offset)
{
  // make sure offset is valid
  if(offset < 0 || offset > (num_bytes<<3)-1) { return -1; }

  //get the right bit
  return (bytes[offset >> 3] & (1 << (offset & 0x7));
}
person Mike Spross    schedule 07.10.2008

Я использовал битовые маски в коде файловой системы, где битовая маска во много раз больше, чем машинное слово. думайте об этом как о «массиве логических значений»;

(журналирование масок во флэш-памяти, если хотите знать)

многие компиляторы знают, как сделать это за вас. Добавьте немного объектно-ориентированного кода, чтобы иметь типы, которые работают разумно, и тогда ваш код начнет выглядеть так, как будто это намерение, а не какой-то битовый удар.

Мои 2 цента.

person Tim Williscroft    schedule 07.10.2008
comment
так вы предлагаете, возможно, сохранить его в базе данных как двоичное поле переменной длины (BLOB?), а затем при его обработке преобразовать в массив логических значений? это может сработать - какой тип данных вы должны использовать в БД? - person nickf; 07.10.2008

С 64-битным целым числом вы можете хранить значения до 2 ^ 64-1, 64 — это только 2 ^ 6. Так что да, есть предел, но если вам нужно больше, чем 64 флага, мне было бы очень интересно узнать, что они все делали :)

О скольких состояниях вам нужно потенциально подумать? Если у вас есть 64 потенциальных состояния, количество комбинаций, в которых они могут существовать, равно полному размеру 64-битного целого числа.

Если вам нужно беспокоиться о 128 флагах, то будет достаточно пары битовых векторов (2 ^ 64 * 2).

Дополнение: в Programming Pearls есть расширенное обсуждение использования битового массива длиной 10^7, реализованного в целых числах (для хранения используемых 800 чисел) — это очень быстро и очень подходит для задачи. описано в той главе.

person warren    schedule 07.10.2008
comment
ага, я имел в виду 64 флага (2^64), а не 64 комбинации (2^6). - person nickf; 07.10.2008
comment
Я понял, что вы имели в виду, но хотел уточнить в своем ответе :) - person warren; 07.10.2008

Некоторые языки (я полагаю, что это делает perl, но не уверен) разрешают побитовую арифметику для строк. Дает вам гораздо больший эффективный диапазон. ((strlen * 8bit chars) комбинации)

Однако я бы не стал использовать одно значение для наложения более одного /типа/ данных. Базовый триплет r/w/x из 3-битных целых чисел, вероятно, будет верхним «практическим» пределом не из соображений экономии места, а из соображений практической разработки.

( Php использует эту систему для управления своими сообщениями об ошибках, и я уже обнаружил, что это немного чрезмерно, когда вам нужно определить значения, где константы php не являются резидентными, и вам нужно сгенерировать целое число вручную, и чтобы если честно, если бы chmod не поддерживал синтаксис стиля 'ugo+rwx', я бы никогда не захотел его использовать, потому что я никогда не смогу вспомнить магические числа)

В тот момент, когда вам нужно взломать таблицу констант для отладки кода, вы понимаете, что зашли слишком далеко.

person Kent Fredric    schedule 07.10.2008

Старый поток, но стоит упомянуть, что есть случаи, требующие раздутых битовых масок, например, молекулярные отпечатки пальцев, которые часто генерируются как 1024-битные массивы, которые мы упаковали в 32 поля bigint (SQL Server не поддерживает UInt32). Побитовые операции работают нормально - пока ваша таблица не начнет расти и вы не осознаете медлительность отдельных вызовов функций. Двоичный тип данных мог бы работать, если бы не запрет T-SQL на побитовые операторы, имеющие два бинарных операнда.

person Ian Russell    schedule 22.09.2013

Например, .NET использует массив целых чисел в качестве внутреннего хранилища для своего класса BitArray. Практически нет другого выхода.

При этом в SQL вам понадобится более одного столбца (или используйте BLOBS) для хранения всех состояний.

person arul    schedule 07.10.2008

Вы отметили этот вопрос SQL, поэтому я думаю, вам нужно проконсультироваться с документацией по вашей базе данных, чтобы найти размер целого числа. Затем вычтите один бит для знака, просто на всякий случай.

Изменить: в вашем комментарии говорится, что вы используете MySQL. В документации для числовых типов MySQL 5.0 указано, что максимальный размер NUMERIC составляет 64 или 65 цифр. Это 212 бит для 64 цифр.

Помните, что выбранный вами язык должен уметь работать с этими цифрами, поэтому в любом случае вы можете быть ограничены 64-битным целым числом.

person Mark Ransom    schedule 07.10.2008
comment
да, тип данных mysql BIGINT 64-битный. Мне было интересно, какой тип поля использовать, если вам нужно более 64 флагов. - person nickf; 07.10.2008
comment
Microsoft SQL Server имеет интересную оптимизацию, благодаря которой он упаковывает до 8-битных столбцов в один байт в строке. В документации нет упоминания о верхнем пределе количества битовых столбцов, которые может иметь таблица. Эта оптимизация позволяет вам рассматривать каждый бит как отдельный объект и позволить движку заботиться о его сохранении, извлечении и обновлении. - person David A. Gray; 01.08.2015