README.md

IP-Addresses-Counter

PicoCLI Maintainability Test Coverage Maintainability Rating

Реализация тестового задания из Списка тестовых заданий для прокачки.
а именно,
Посчитать количество уникальных IP-адресов в простом текстовом файле (Kotlin/Java).

Реализация выполнена на Java с применением массива битов.
Для учёта уникальных IP адресов заведены 4 битовых массива, собранные в список. Каждый битовый массив будет хранить информацию о своём диапазоне IP адресов: 0.0.0.0 - 63.255.255.255, 64.0.0.0 - 127.255.255.255, 128.0.0.0 - 191.255.255.255 и 192.0.0.0 - 255.255.255.255.

Алгоритм работы

Из файла считывается строка с IP адресом и преобразуется в число типа long, это возможно, поскольку в IPv4 адрес представляет собой 32-битное число.
После чего вычисляется индекс массива в списке - целочисленным делением на $2^{30}$ и позиция бита в массиве - остатком от деления на $2^{30}$. Этот бит устанавливается в значение 1.
После достижения конца файла подсчитывается и суммируется количество битов в значении 1 во всех массивах и возвращается как ответ.

Программа принимает в качестве параметра путь к файлу со списком IP адресов и несколько ключей
“-d”, включающий отображение информации о номере считываемой строки и IP адресе в этой строке.
“-m”, для больших файлов, отображает информацию о каждой миллионной строке

./app/build/install/app/bin/app -d ./app/src/test/resources/small

P.S. При решении задачи был написан класс IPSet, позволяющий хранить информацию о всех адресах в одной структуре данных, но такое решение сразу занимает 512 MB памяти, в отличие от BitSet, который увеличивается динамически, и не даёт прироста производительности, поэтому не используется.

В процессе решения были испробованы и отвергнуты следующие подходы:

Префиксные деревья

Огромный расход памяти - 160 миллионов адресов заняло 12 ГБ ОЗУ, после чего выполнение программы практически остановилось из-за нехватки памяти.

Использование регулярных выражений для проверки корректности IP адреса

Значительно замедление скорости работы - порядка 5 часов на тестовый файл.

Например, для метода isIPv4Address() скорость выполнения ≈ 700,000 операций в секунду. И 2,700,000, если вынести вычисление regexPattern из метода

    public static boolean isIPv4Address(String ipAddress) {
        String pattern = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
        Pattern regexPattern = Pattern.compile(pattern);
        return regexPattern.matcher(ipAddress).matches();
    }
Benchmark Mode Cnt Score Error Units
MatchBench.isIPv4AddressInnerRegex thrpt 25 715006,184 ± 5881,340 ops/s
MatchBench.isIPv4AddressOuterRegex thrpt 25 2679271,358 ± 92405,723 ops/s


Разбиение IP адреса на октеты и преобразование строки в число

Сравнение метода ipToLong() и текущего метода для преобразования строки с IP адресом в число

    public static long ipToLong(String ipAddress) {
        String[] octetStrings = ipAddress.split("\\.");
        long result = 0;

        for (int i = 0; i < 4; i++) {
            long octetValue = Long.parseLong(octetStrings[i]);
            result += octetValue << (8 * (3 - i));
        }
        return result;
    }
Benchmark Mode Cnt Score Error Units
IPtoLongBench.testStringSplitToIP thrpt 25 6480849,205 ± 177321,560 ops/s
IPtoLongBench.testStringCharByCharToIP thrpt 25 60621167,508 ± 437508,189 ops/s

В итоге, сейчас используется метод посимвольного преобразования строки в IP адрес, подсмотренный в OpenJDK. Помимо преобразования в методе одновременно осуществляется проверка на корректность адреса. Скорость выполнения зависит от количества знаков.

Benchmark Mode Cnt Score Error Units
IPLengthBench.test4Digit thrpt 25 123210472,781 ± 272082,957 ops/s
IPLengthBench.test8Digit thrpt 25 82186672,406 ± 545401,768 ops/s
IPLengthBench.test12Digit thrpt 25 59494004,545 ± 2726267,979 ops/s


Запуск для тестового файла в 106 ГБ, 8 миллиардов строк, показал следующие результаты:

Для SSD - 00:12:03.019

Для HDD - 00:19:34.167


Для запуска приложения необходимо склонировать репозиторий

git clone https://github.com/sergeloie/IP-Addresses-Counter.git

Перейти в каталог с приложением и скомпилировать его

cd IP-Addresses-Counter && make run-dist

Запустить, указав параметры и путь к файлу

./app/build/install/app/bin/app -d ./app/src/test/resources/small
Описание

Счётчик количества уникальных IP адресов в файле

Конвейеры
0 успешных
0 с ошибкой