Набор битов Java: эффективный поиск всех истинных битов?

#java #performance #memory #bitset

Вопрос:

Предположим BitSet java.util.BitSet; , используется значение from. Цель состоит в том, чтобы быстро найти все битовые значения, для которых установлено значение true . Эти значения не упорядочены и не имеют определенного шаблона. Максимальный индекс BitSet будет равен 2^31 - 48 . Общее количество битов, для которых будет установлено true (2^31 - 48)/2 значение, равно . Другими словами, существует два миллиарда битов, которые могут быть true / false , как я могу эффективно найти все true биты?

Каждый раз , когда установлен бит true , требуется выполнить запуск, чтобы посетить все true биты в BitSet . Вы можете понять, почему каждый раз перебирать все 2^31 - 48 биты не так эффективно, когда дело доходит до производительности.

Вот решение , которое не соответствует моим потребностям: создайте int[] indices размер (2^31 - 48)/2 и каждый раз i , когда установлен бит true , сохраняйте значение i в следующем доступном слоте indices . Хотя это помогает в выполнении запроса, это добавит около 32 * (2^31 - 48)/2 бит в память, которая составляет около 4,3 Гб.

Основное внимание уделяется производительности и повторяющимся вычислениям. Использование файлов ввода/вывода или чего-то другого, кроме BitSet нежелательного.

Каков самый быстрый подход к достижению желаемого поведения? Или… что такое достаточно быстрый подход, который также использует значительно меньше памяти?

Комментарии:

1. Подождите… так вы говорите, что вероятность установки значения true/false для «Коллекции» равна 50/50, И вы хотите, чтобы это было сделано БОЛЕЕ ЭФФЕКТИВНО, чем подсчет или хранение положительных индексов? Возможно, используйте сжатие, такое как LZW, а затем разберите биты в словаре и их расположение, но это действительно далеко… и вам нужны ошибки 0/0, поэтому о любом другом статистическом или аналитическом трюке не может быть и речи… «Каждый раз, когда бит установлен в значение true, требуется выполнить запуск, чтобы посетить все истинные биты»… почему? Я думаю, что здесь кроется центр проблемы…

2. Расширение вашего решения ( int[] indices ): вместо использования абсолютных адресов вы можете сохранить дельту индекса до следующего индекса в виде байта или, в зависимости от возможности, в еще меньшем количестве битов. И если так случится, что дельта больше, чем вы можете сохранить, имейте значение индикатора, которое указывает на больший скачок и требует большего количества считываемых данных, в основном, как UTF-8 преобразует байты в символы.

3. Набор бит java 1.8 stream , который создает цель — вы смотрели на его реализацию? docs.oracle.com/javase/8/docs/api/java/util/…

Ответ №1:

Каков самый быстрый подход к достижению желаемого поведения?

Если вы ограничиваете себя API BitSet, то я думаю, что вам нужен цикл, который многократно вызывает BitSet.nextSetBit . Да, это повлечет за собой 2^30 звонков. Но я думаю, что это так же хорошо, как вы собираетесь использовать BitSet API.

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

Наблюдение: изучение 2^30 бит каждый раз, когда меняется один бит, будет очень дорогостоящим с точки зрения вычислений, независимо от того, как вы это делаете.

Если бы это была моя проблема, я бы сначала поискал более разумное решение, которое вообще не требовало бы этого. Если бы не было разумного решения, я бы, вероятно, использовал массив int вместо a BitSet и нашел способ распараллелить сканирование по 8 / 16 / 32 ядра 1. (Но это также зависит от того, что вам нужно сделать для каждого бита, который есть true .)


1 — Это предполагает, что у вас есть незанятые ядра / питание / охлаждение, чтобы решить эту проблему.


Или… что такое достаточно быстрый подход, который также использует значительно меньше памяти?

AFAIK, вы не можете представлять 2^N случайные истинные / ложные значения в лучших O(2^N) битах. Ваша единственная надежда была бы, если бы битовый шаблон был неслучайным и легко сжимаемым. И даже в этом случае у вас возникают проблемы с затратами процессора на сжатие / распаковку, а также проблема эффективного обновления бита в сжатой последовательности битов. Осуществимо ли это, будет зависеть от характера вашего битового потока.

Комментарии:

1. Почему вы думаете, что распараллеливание обработки массива int лучше, чем просто распараллеливание обработки набора битов?

2. Хмм… теперь, когда вы упомянули об этом, у меня нет хорошего ответа на этот вопрос.