Каковы эффективные способы дедупликации набора> 1 миллиона строк? - PullRequest
2 голосов
/ 18 февраля 2020

Для моего проекта мне нужно очень эффективно дедуплицировать очень большие наборы строк. Т.е., учитывая список строк, которые могут содержать дубликаты, я хочу создать список всех строк в этом списке, но без дубликатов.

Вот упрощенный псевдокод:

set = # empty set
deduped = []
for string in strings:
    if !set.contains(string):
        set.add(string)
        deduped.add(string)

Вот упрощенный C ++ для него (примерно):

std::unordered_set <const char *>set;
for (auto &string : strings) {
  // do some non-trivial work here that is difficult to parallelize
  auto result = set.try_emplace(string);
}
// afterwards, iterate over set and dump strings into vector

Однако, это не достаточно быстро для моих нужд (я тщательно его протестировал). Вот некоторые идеи, чтобы сделать это быстрее:

  • Использовать другую реализацию набора C ++ (например, abseil)
  • Вставить в набор одновременно (однако, согласно комментарию в реализации C ++ , это сложно. Кроме того, при распараллеливании будет происходить снижение производительности)
  • Поскольку набор строк очень мало меняется при разных запусках, возможно, кешируйте, генерирует ли функция ha sh коллизии или нет. Если он не генерирует ничего (при учете изменений), тогда строки можно сравнивать по их ha sh во время поиска, а не по фактическому равенству строк (strcmp).
  • Сохранение данных строк в файле при каждом запуске (однако, хотя это может показаться простым, здесь много сложностей)

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

Ответы [ 2 ]

1 голос
/ 18 февраля 2020

Вы можете использовать различные алгоритмы и структуры данных для решения вашей проблемы:

  1. Попробуйте использовать дерево префиксов (tr ie), суффиксную машину, таблицу ha sh. Таблица ha sh - один из самых быстрых способов поиска дубликатов. Попробуйте разные таблицы ha sh.
  2. Используйте различные атрибуты данных, чтобы уменьшить ненужные вычисления. Например, вы можете обрабатывать только подмножества строк одинаковой длины.
  3. Попробуйте реализовать подход "разделяй и властвуй" для распараллеливания вычислений. Например, разделите набор строк на количество подмножеств, равное аппаратным потокам. Затем объедините эти подмножества в одно. Поскольку подмножества в процессе будут уменьшены в размере (если количество дубликатов достаточно велико), объединение этих подмножеств не должно быть слишком дорогим.

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

0 голосов
/ 21 февраля 2020

Вы можете значительно распараллелить вашу задачу, внедрив упрощенную версию std::unordered_set вручную:

  1. Создать произвольное количество сегментов (вероятно, должно быть пропорционально или равно количеству потоков в пуле потоков).
  2. Используя пул потоков, вычисляйте хэши ваших строк параллельно и разделяйте строки по их хэшам, между прочим. Возможно, вам понадобится заблокировать отдельные сегменты при добавлении туда ваших строк, но операция должна быть короткой, и / или вы можете использовать свободную от блокировки структуру.
  3. Обрабатывать каждый блок индивидуально, используя ваш пул потоков - сравнивать хэши и, если они равны, сравнивать строку самих себя.

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

Кстати, из вашего описания звучит, что вы загружаете все строки в память, а затем удаляете дубликаты. Вы можете попытаться прочитать ваши данные напрямую в std::unordered_set, тогда вы сэкономите память и увеличите скорость.

...