Чи можливо реалізувати добре розподілену хеш-таблицю без використання оператора%?


11

Я хочу впровадити швидку, добре розподілену хеш-таблицю в C #. У мене виникають проблеми з вибором функції обмеження хешу, яка приймає довільний хеш-код і "обмежує" його, щоб його можна було використовувати для індексації відра. Дотепер я бачу два варіанти:

  • З одного боку, ви можете переконатися, що у ваших відрах завжди є проста кількість елементів, а щоб обмежити хеш, ви просто змодулюєте його на кількість відра. Це, власне, те, що робить словник .NET . Проблема такого підходу полягає в тому, що використання% є надзвичайно повільним порівняно з іншими операціями; якщо ви подивитеся на таблиці інструкцій Agner Fog , idiv(який є кодом складання, який генерується на%), затримка інструкцій становить ~ 25 циклів для новіших процесорів Intel. Порівняйте це близько 3 для mul, або 1 для бітового опса , як and, orабо xor.

  • З іншого боку, ви можете мати кількість відра завжди потужністю 2. Вам все одно доведеться обчислювати модуль хешу, щоб не намагатися індексувати поза масивом, але цього разу це буде дешевше . Оскільки для потужностей 2 % Nпросто & (N - 1), обмеження зводиться до операції маскування, яка займає лише 1-2 цикли. Це робиться з-за рідкості Google . Мінус цього полягає в тому, що ми розраховуємо на те, щоб користувачі надавали хороші хеши; маскування хеша по суті відрізає частину хешу, тому ми більше не беремо до уваги всі біти хешу. Якщо хеш користувача розподілений нерівномірно, наприклад, заповнюються лише більш високі біти або послідовно нижчі біти, то такий підхід має набагато більшу швидкість зіткнень.

Я шукаю алгоритм, з яким я можу використовувати найкращі з обох світів: він враховує всі біти хешу, а також швидший, ніж використання%. Це не обов'язково має бути модулем, просто те, що гарантовано знаходиться в діапазоні 0..N-1(де N - довжина відра) і має рівномірний розподіл для всіх слотів. Чи існує такий алгоритм?

Дякуємо за допомогу.


1
Знайдіть ефект лавини , а також пояснення в murmurhash3 (smhasher) . Однак принциповий момент у вашому питанні не вирішується шляхом прийняття кращої хеш-функції. Натомість, це питання про те, чому користувачі в першу чергу не приймають таку саму кращу хеш-функцію, і клопотання про контрзаходи (наче користувачі злісно ліниві).
rwong


Для швидкого по модулю (2^N +/- 1)см stackoverflow.com/questions/763137 / ...
rwong

@rwong Вибачте, але я не зовсім впевнений, що стосується вашого коментаря до моєї публікації. Я не контролюю хеш, наданий користувачем, тому не шукаю кращої хеш-функції. Я також не розумію, що ви маєте на увазі під "зловмисно ледачими користувачами".
Джеймс Ко

4
Якщо хеш-функція погана, реалізатор таблиці хешів нічого не може зробити, щоб "виправити" поганий розподіл. Modulo просто число не відновлює поганий хеш. Розглянемо хеш-функцію, що виробляє як вихід, кратні простому числу. Я бачив таку проблему в реальному виробничому коді.
Френк Хілеман

Відповіді:


9

Сучасні реалізації хеш-таблиць не використовують модульну функцію. Вони часто використовують потужність таблиць двох розмірів і відсікають непотрібні біти. Ідеальна хеш-функція дозволила б це зробити. Використання модуля в поєднанні з розмірами простих таблиць з числами виникло в дні, коли хеш-функції були загалом поганими, оскільки вони часто знаходяться в розробці .net. Я рекомендую почитати про SipHash , сучасну хеш-функцію, а потім прочитати про деякі інші сучасні функції, наприклад xxHash .

Я повинен пояснити, чому .net хеш-функції часто погані. У .net, програмісти часто змушені реалізовувати хеш-функції, замінюючи GetHashcode. Але .net не надає інструментів, необхідних для забезпечення високоякісних створених програмістом функцій, а саме:

  • інкапсуляція стану хешу в структурі або класі
  • хеш-функції "додати", які додають нові дані до стану хешу (додайте байтовий масив або подвійний, наприклад)
  • хеш-функція "доопрацювати", щоб створити лавину
  • інкапсуляція хеш-результату - в .net ви отримуєте один вибір, 32-бітове ціле число, підписане.

Для отримання додаткової інформації про використання результату хеш-функції в якості індексу хеш-таблиці, будь ласка, ознайомтеся з визначеннями універсальних форм хешування в цій статті: Швидше 64-бітове універсальне хешування з використанням переносу без переносу


3

Щоб використовувати AND, зберігаючи всі біти, використовуйте і XOR.

Для прикладу, temp = (hash & 0xFFFF) ^ ( hash >> 16); index = (temp & 0xFF) ^ (temp >> 8);.

У цьому прикладі немає модуля і всі 32 біти hashефекту є 8-бітним index. Однак, швидше це чи ні, ніж ДІВ - це те, що залежить від занадто багатьох факторів, і в деяких випадках це може бути повільніше, ніж ДІВ (наприклад, великий хеш і крихітний індекс).


Це завжди буде швидше, ніж DIV / IDIV, однак я не думаю, що це відповідає на моє запитання - indexбуде в діапазоні [0..255]. Мені потрібно щось в асортименті [0..n-1], де nкількість відра.
Джеймс Ко

@JamesKo Але якщо ви реалізуєте словник, ви також контролюєте кількість відра (певною мірою). Отже, замість простих чисел ви могли вибрати потужність двох. (Чи дійсно це було б насправді гарною ідеєю, я не можу вам сказати.)
svick

@svick Для потужностей 2 ми могли б зробити просту операцію з маскою. Як уже згадувалося в запитанні, я шукаю дешевий спосіб зробити це з простими числами, тому навіть у погано розподілених хешах розміщено.
Джеймс Ко

1

Ви можете скористатися тим, що багато простих цілих чисел мають модульну мультиплікативну обернену. Дивіться цю статтю . Ви задовольнили одне із обмежень, зробивши свій індекс ковша простим і модулем 2 ^ n, який по суті є відносно простим.

У статті описано алгоритм пошуку числа такого, що множення на це число та ігнорування переливу дасть такий самий результат, як якщо б ви розділили розмір індексу відра.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.