хэш-функция для src dest ip + порт

Итак, я рассматриваю различные хэш-функции для использования для хэширования ip-адреса 4 кортежей и порта для идентификации потоков.

один я наткнулся на

((size_t)(key.src.s_addr) * 59) ^
((size_t)(key.dst.s_addr)) ^
((size_t)(key.sport) << 16) ^
((size_t)(key.dport)) ^
((size_t)(key.proto));

теперь, для жизни меня, я не могу объяснить используемое простое число (59). почему бы не 31,а затем почему бы не испортить его, умножив Спорт на 2. Есть ли лучшая хэш-функция, которая будет использоваться для ip-адресов ?

4 ответов


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

сдвиг на 16 может быть вызван тем, что порты ограничены диапазоном 2^16. Функция, по-видимому, перемещает исходный порт в верхнюю часть битового поля, оставляя порт назначения в нижней части. Думаю, это можно объяснить в следующем абзаце.

еще одна причина, по которой происходит умножение (и это верно и для операции сдвига), заключается в том, что она разрушает ассоциативную природу хеш-функции. Помните, что XOR ассоциативен, поэтому IPs src=192.168.1.1 и dst=192.168.1.254 будут хэшироваться одинаково значение как src=192.168.1.254 и dst=192.168.1.1 (заменено), если умножения не было.


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


лично я думаю, что вам было бы лучше прочитать четыре байта IP как беззнаковый длинный, который дал бы вам число примерно в диапазоне 0 - 2^32-1. Затем вы вычисляете, сколько потоков вы хотите иметь активными в любой момент времени, и это будет ваш размер таблицы индекса.

возьмите 2000, например. Это означает, что вы хотите отобразить 2^32 числа примерно на 2^11 indeces (для потока информации). Это не сработает, потому что хеширование почти никогда не работает, если заполнены на 100% и даже 90% может быть трудный. Использование индексной таблицы, которую вы заполняете только до 50% (4000 indeces) или даже 25% (8000), не имеет большого значения для сегодняшних воспоминаний.

точный размер индексной таблицы должен быть неравномерным числом местоположений и предпочтительно простым числом. Это связано с тем, что вам, скорее всего, понадобится некоторая обработка переполнения для обработки конфликтов (два или более ip - номеров, которые после хэширования указывают на одно и то же место в таблице индексов), которые вы получите. Обработка переполнения должна быть другой простое число меньше размера индексной таблицы. Все эти простые числа! Да что с ними?

я проиллюстрирую примером (в C):

idxsiz = prime(2000 * 2);    // 50% loading
ovfjmp = prime(idxsiz/3);

...

первоначально заполните таблицу позиций idxjmp неиспользуемой маркировкой (-1). Иметь готовую удаленную маркировку (-2).

ваш ip-номер входит в систему, и вы ищете его запись потока (может или не может существовать):

stoppos = ip % idxsiz;    /* modulo (division) just this once */
i = stoppos;
do
{
  if (index[i] == UNUSED) return NULL;
  if (index[i] != DELETED)
  {
    flowrecptr = &flow_record[index[i]];
    if (!flowrecptr->in_use) {/* hash table is broken */}
    if (flowrecptr->ip == ip) return flowrecptr;
  }
  i += ovfjmp;
  if (i >= idxsiz) i -= idxsiz;
}
while (i != stoppos);
return NULL; 

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

Это было, когда вы пытались сделать вам. Вы получили NULL назад от get, поэтому вам нужно сделать put, который вы начинаете с поиска первой позиции индекса, содержащей неиспользуемые или удаленные. Замените это значение индексом на первую / следующую свободную строку таблицы flow_record. Пометить строку как in_use. Поместите исходный ip-номер в IP-член строки flow_record.

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

использование простых чисел гарантирует, что в худшем случае, когда все места заняты индекс - механизм будет проверить каждое место. Чтобы проиллюстрировать это: предположим idxsiz делится на ovfjmp: вам не придется много обработки переполнения в помине. 35 и 7 приведут к тому, что места 0,7,14,21 и 28 будут протестированы до того, как индекс подскочит до 0, где тест while приведет к остановке поиска.

----------------------упс!

Я пропустила, что вы хотели номер порта, а также. Предполагая, что ip V4 означает 6 байт адреса. Прочитайте это как 64-разрядное целое число без знака и очистите верхние 16 бит/2 байта. Затем вы делаете по модулю расчет.


Брайан Гидеон в значительной степени суммирует его; умножение и сдвиг предназначены для нарушения симметрии. Так что это бросается в гипотетическом случае машина для работы с telnet машина B и наоборот, и так получилось, что они выбрали ту же эфемерность номер_порта. Не очень распространенное, но и не невозможное. Большая часть 5-кортежа довольно постоянна: протокол поступает из очень маленького домена, а также половина {address,portnum}.

предполагая, что премьер-размера хеш-таблицы, магической константы 59 может быть заменен любым простым, ИМО. (Port

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