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 с ошибкой