Быстрая проверка в подсети строк ip-адресов (как для ipv4, так и для ipv6)

Я хотел бы выполнить быструю проверку строк IP-адресов в подсети. Преобразование строки в целое число было бы дорогостоящей операцией, и желательно получить код как можно быстрее. Библиотека SubnetUtils не предлагает проверку IPv6 в подсети, а версия IPv4 работает довольно медленно.

Рассмотрим следующий ввод: IP: aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa Subnet: aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa/128

Это должно привести к true, поскольку IP-адрес включен в подсеть.

Каковы самые быстрые способы выполнения этого вычисления в Java как для IPv4, так и для IPv6?

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

=========================================================================================
Benchmark results - IP comparison (/24)
-----------------------------------------------------------------------------------------
Input: IP, IP range (using CIDR) with a /24 subnet (50% is in-range, 50% is not in range)
Desired output: whether the IP address is in range.
-----------------------------------------------------------------------------------------
Method 0: CIDRUtils.
Method 1: An optimized character-wise comparison using CIDRUtils and using Ineter for IPv6.
Method 2: SubnetUtils (IPv4 only, runs on half of the test cases).
Method 3: Ineter.
=========================================================================================
Number of runs: 1000000
Method 0 (on IPv4): 1228.0 ms
Method 1 (on IPv4): 459.0 ms
Method 2 (on IPv4): 844.0 ms
Method 3 (on IPv4): 963.0 ms
-----------------------------------------------------------------------------------------
Method 0 (on IPv6): 2203.0 ms
Method 1 (on IPv6): 845.0 ms
Method 2 (on IPv6): 0.0 ms
Method 3 (on IPv6): 543.0 ms

=========================================================================================
Benchmark results - Exact IP comparison (/128 and /32)
-----------------------------------------------------------------------------------------
Input: IP, IP range (using CIDR) which is a /128 subnet for IPv6 and an /32 subnet for IPv4 (50% is in-range, 50% is not in range)
Desired output: whether the IP address is in range.
-----------------------------------------------------------------------------------------
Method 0: CIDRUtils.
Method 1: An optimized character-wise comparison using CIDRUtils and using Ineter for IPv6.
Method 2: SubnetUtils (IPv4 only, runs on half of the test cases).
Method 3: Ineter.
=========================================================================================
Number of runs: 1000000
Method 0 (on IPv4): 602.0 ms
Method 1 (on IPv4): 336.0 ms
Method 2 (on IPv4): 690.0 ms
Method 3 (on IPv4): 626.0 ms
-----------------------------------------------------------------------------------------
Method 0 (on IPv6): 1607.0 ms
Method 1 (on IPv6): 499.0 ms
Method 2 (on IPv6): 0.0 ms
Method 3 (on IPv6): 530.0 ms

Попробуй Инетер: github.com/maltalex/Ineter

Malt 11.04.2018 14:10

Привет, Солод, спасибо за предложение. В моем тесте IPv6 он работал достаточно хорошо. Однако на IPv4-адресах он работал довольно медленно по сравнению с другими методами.

www.data-blogger.com 11.04.2018 14:23

Можете поделиться своим тестом?

Malt 11.04.2018 14:24

Его добавлено сейчас :)

www.data-blogger.com 11.04.2018 14:31

Результаты хорошие, но код будет лучше :)

Malt 11.04.2018 14:38

Конечно: gist.github.com/kevin91nl/a131150599967b94c1ee95487a0dc074, я опубликую «метод 1», когда будут добавлены новые предложения. CIDRUtils и SubnetUtils доступны на GitHub.

www.data-blogger.com 11.04.2018 14:41

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

Ron Maupin 11.04.2018 16:40

@ www.data-blogger.com Два пункта - 1. Вы не должны проводить такие тесты. JVM JIT может исказить ваши результаты. Вместо этого используйте тестовую программу, такую ​​как JMH. 2. Для Ineter вместо создания подсети из String попробуйте использовать только что созданный экземпляр IPv4Address и длину маски 32 или 24. Это может быть немного быстрее, чем анализ строки подсети.

Malt 11.04.2018 19:39
0
8
613
1

Ответы 1

Я пробовал ваш тест с библиотека Java с открытым исходным кодом IPAddress для сравнения с другими (CIDRUtils, SubnetUtils и Ineter), упомянутыми выше. Отказ от ответственности: я руководитель проекта библиотеки IPAddress. Вы также ссылаетесь на «Оптимизированное посимвольное сравнение с использованием CIDRUtils и Ineter для IPv6», но у меня нет доступа к этому коду.

Код для IPAddress проще, чем для остальных. В отличие от других, код одинаков как для IPv4, так и для IPv6 и представляет собой всего одну строку:

 boolean result = new IPAddressString(ipRange).prefixEquals(new IPAddressString(ip));

Не стесняйтесь попробовать сами.

Ваши тесты:

  • Тест 1 и 2: диапазон IPv4 и IPV6 (с использованием CIDR) с подсетью / 24 (50% находится в диапазоне, 50% - вне диапазона)
  • Тест 3 и 4: диапазон IPv4 и IPv6 (с использованием CIDR), который представляет собой подсеть / 128 для IPv6 и подсеть / 32 для IPv4 (50% находится в диапазоне, 50% - вне диапазона)

Согласно 10 отдельным запускам теста на моем ноутбуке macbook pro, мои результаты показывают:

  • IPAddress быстрее, чем CIDRUtils и SubnetUtils во всех тестах (от 25% до 70% быстрее). IPAddress и Ineter сопоставимы по производительности
  • Сравнение IPAddress и Ineter:
    • Тест 1: IP-адрес на 3% быстрее, около 470 мс
    • Тест 2: Ineter на 7% быстрее, около 520 мс
    • Тест 3: IPAddress на 32% быстрее, около 278 мс
    • Тест 4: Ineter на 3% быстрее, около 453 мс

Конечно, на других машинах пробег может отличаться. Однако, согласно моим измерениям, кажется, что Ineter немного быстрее работает с IPv6, но немного медленнее в подсети IPv4 / 24 и намного медленнее в подсетях с полным / 32 IPv4. Однако IPAddress немного отличается тем, что код идентичен для IPv4 и IPv6, в отличие от Ineter, который требует кода, специфичного для IPv4 или IPv6.

Другие вопросы по теме