объединение больших двоичных файлов в C ++ с оптимизацией - PullRequest
0 голосов
/ 09 июня 2018

Справочная информация. Я написал измененную подпрограмму контрольной суммы adler32, которая генерирует 64-битные контрольные суммы.Я хочу проверить наличие коллизий в области прописных и строчных букв, цифр и _.Я написал программу-генератор, которая просматривает все комбинации строк в этом диапазоне до заданной максимальной длины.Он выводит отсортированные двоичные файлы размером до 4G с результатами контрольных сумм.Ограничение 4G связано с размером памяти, хотя большие файлы означают меньшее количество файлов, что ускоряет слияние и проверку значительно.Общий размер данных для строк длиной до 6 байтов составляет 64 * 64 * 64 * 64 * 64 * 63 = 67 645 734 912 целых или 541 ГБ при 8 байтах на целое число.Это мой предел, так как следующий итератон может привести к добавлению файлов размером до 64 * 541 ГБ или более 34 ТБ.

Проблема: у меня 122 двоичных файла uint64_t, большинство из которых имеют размер 4 ГБ.Каждый отдельный файл отсортирован.Мне нужно проверить, есть ли какие-либо дубликаты значений в этих файлах.

Кажется, что работает следующий код, но, по оценкам, потребуется около 35 дней для проверки контрольных сумм строк длиной до 6 байтов.Может кто-нибудь придумать какие-либо оптимизации или альтернативные подходы, которые могут быть быстрее?Обратите внимание, что я не могу хранить в памяти одновременно более двух файлов.

Майк

struct DataItem
{
    uint64_t data;
    ifstream ifs;
    unique_ptr<char[]> buf;

    explicit DataItem(const string &filename)
        : ifs(filename, ifstream::in | ifstream::binary)
    {
        constexpr size_t bufSize = 1'048'576;
        buf = make_unique<char[]>(bufSize);
        ifs.rdbuf()->pubsetbuf(buf.get(), bufSize);
        readNext();
    }

    void readNext()
    {
        if (ifs.is_open() && !ifs.eof())
            ifs.read(reinterpret_cast<char *>(&data), sizeof(uint64_t));
    }

    bool operator<(DataItem const &other)
    {
        return data < other.data;
    }

    bool operator>(DataItem const &other)
    {
        return data > other.data;
    }
};

int main(int argc, char *argv[])
{
    path givenPath;
    vector<DataItem> data;

    if (argc > 1)
        givenPath = path(argv[1]);
    else
        givenPath = path("*.dat");

    auto directory = givenPath;
    directory.remove_filename();
    if (directory.empty())
        directory = path("./");

    auto extension = givenPath.extension();

    for (auto &p : directory_iterator(directory))
        if (p.path().extension() == extension && is_regular_file(p))
            data.emplace_back(p.path().string());

    sort(data.begin(), data.end());

    uint64_t current = data.front().data;
    data.front().readNext();

    int progress = 0, loop = 0;
    while (!data.empty())
    {
        // bubble the new value to resort the data vector
        auto now = data.begin();
        auto next = now + 1;
        while ((next != data.end()) && (*now > *next))
        {
            swap(*now, *next);
            ++now;
            ++next;
        }

        if (current == data.front().data)
            cout << current << '\t' << (current >> 32) << endl;

        current = data.front().data;

        if (data.front().ifs.eof())
            data.erase(data.begin());
        else
            data.front().readNext();

        ++progress;
        if (progress >= 1'000'000)
        {
            {
                progress = 0;
                cerr << '.';
                ++loop;
                if (loop >= 10)
                {
                    loop = 0;
                    cerr << '|';
                }
            }
        }
    }
    return 0;
}

Ответы [ 2 ]

0 голосов
/ 10 июня 2018

Как вы заметили, тестирование качества хэш-функции (контрольная сумма является вариантом) быстро становится неразрешимым.

Поэтому сопротивление коллизиям обычно проверяется статистически, а не алгоритмически.Некоторые примеры включают спектральное тестирование , чтобы увидеть, насколько хорошо распределяется выходной сигнал, и тестирование по хи-квадрат , чтобы увидеть, насколько непредсказуемо изменяется выходной сигнал при некотором изменении входного сигнала.Также взгляните на NIST SP 800-22 , который определяет некоторые проверки криптографических хеш-функций.Конечно, словарное тестирование также полезно, но только до определенной глубины.

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

Сказав это, вам не нужно читать весь файл в памяти;Поскольку ваши файлы отсортированы, просто откройте все файлы одновременно и поддерживайте скользящее окно для каждого файла, перемещая «указатель» вперед при чтении и сравнении записей (количество движения будет отличаться для каждого файла).Хотя некоторая буферизация будет полезна.

0 голосов
/ 10 июня 2018

Когда ваш журнал находится в файлах, вы не должны сохранять их отсортированными во время выполнения.Просто напишите хэши по мере их появления в журнале.Запись журнала с хэшами позволяет распараллеливаться.Перед началом записи убедитесь, что вы собрали большие блоки (например, 16 МБ).

После завершения хеширования выполните сортировку.Избегайте пузырьковой сортировки, ей нужно время O (n ** 2).Сортировка слиянием отлично подходит для сортировки файлов.Это может быть легко распараллелено.Без распараллеливания требуется время O (n log (n)).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...