C++ 在c+中合并大型二进制文件+;优化

C++ 在c+中合并大型二进制文件+;优化,c++,optimization,file-io,merge,C++,Optimization,File Io,Merge,背景: 我编写了一个修改过的adler32校验和例程,它生成64位校验和。 我想检查大写字母和小写字母、数字和x区域中的冲突。 我已经编写了一个生成器程序,可以逐步遍历这个范围内的所有字符串组合,直到给定的最大长度。 它有一个大小达4G的排序二进制文件的输出,以及校验和的结果。4G限制是由于内存大小,虽然较大的文件意味着较少的文件,这大大加快了合并和检查的速度。 最多6个字节字符串的总数据大小为64*64*64*64*64*63=67645734912个整数或541GB,每个整数8个字节。 这是

背景: 我编写了一个修改过的adler32校验和例程,它生成64位校验和。 我想检查大写字母和小写字母、数字和x区域中的冲突。 我已经编写了一个生成器程序,可以逐步遍历这个范围内的所有字符串组合,直到给定的最大长度。 它有一个大小达4G的排序二进制文件的输出,以及校验和的结果。4G限制是由于内存大小,虽然较大的文件意味着较少的文件,这大大加快了合并和检查的速度。 最多6个字节字符串的总数据大小为64*64*64*64*64*63=67645734912个整数或541GB,每个整数8个字节。 这是我的限制,因为下一次迭代将导致文件总数达到64*541GB或超过34 TB

问题是: 我有122个uint64_t的二进制文件,其中大部分是4GB大小的。每个单独的文件都已排序。我需要检查这些文件中是否有任何重复的值

下面的代码似乎可以工作,但估计需要大约35天才能完成最多6字节字符串的校验和。有人能想出更快的优化或替代方法吗? 请注意,我的内存中不能同时有两个以上的文件

迈克

struct数据项
{
uint64_t数据;
国际单项体育联合会;
独特的_ptrbuf;
显式数据项(常量字符串和文件名)
:ifs(文件名,ifstream::in | ifstream::binary)
{
constexpr size\u t bufSize=1'048'576;
buf=使_唯一(bufSize);
ifs.rdbuf()->pubsetbuf(buf.get(),bufSize);
readNext();
}
void readNext()
{
如果(ifs.is_open()&&!ifs.eof())
如果读取(重新解释铸件和数据),尺寸(uint64 t);
}
布尔运算符(数据项常量和其他)
{
返回数据>其他数据;
}
};
int main(int argc,char*argv[])
{
路径给定路径;
矢量数据;
如果(argc>1)
给定路径=路径(argv[1]);
其他的
给定路径=路径(“*.dat”);
自动目录=给定路径;
目录。删除_filename();
if(directory.empty())
目录=路径(“./”);
自动扩展=给定路径扩展();
for(自动&p:directory\u迭代器(directory))
if(p.path().extension()==扩展名&是常规文件(p))
data.emplace_back(p.path().string());
排序(data.begin(),data.end());
uint64\u t current=data.front().data;
data.front().readNext();
int progress=0,loop=0;
而(!data.empty())
{
//对新值进行气泡处理以使用数据向量
现在自动=data.begin();
自动下一步=现在+1;
而((下一步!=data.end())&&(*现在>*下一步))
{
交换(*现在,*下一个);
++现在;
++其次;
}
if(当前==data.front().data)

cout当日志位于文件中时,不应在执行期间对其进行排序。只需在日志中出现哈希值时写入哈希值。使用哈希值写入日志有助于并行化。确保在开始写入之前收集大数据块(例如16 MB)


完成散列后进行排序。避免冒泡排序,它需要O(n**2)时间。合并排序非常适合基于文件的排序。它可以轻松地进行并行化。如果不进行并行化,它需要O(n log(n))时间。

正如您所注意到的,测试散列函数的质量(校验和是它的一个变体)很快就会变得棘手

因此,抗碰撞性通常通过统计而不是算法进行测试。一些示例包括查看输出的分布情况,以及在输入发生某些变化时,输出的变化是如何不可预测。 另请看,它定义了加密哈希函数的一些检查。 当然,字典测试也很有用,但只在一定程度上有用

还要注意,针对短序列的测试通常不能提供足够的质量证明,因为哈希函数在较长序列上的行为可能会有很大的不同。 您至少还应该检查较长但基本相似的数据集是否不会发生冲突,并检查边缘情况,例如当某些内部值变为0或其他临界值时(这取决于被测试函数的具体情况)


话虽如此,您不需要在内存中读取整个文件;因为您的文件已排序,只需一次打开所有文件,并为每个文件保持一个滑动窗口,在读取和比较条目时向前移动“指针”(每个文件的移动量不同)。一些缓冲会有帮助。

您考虑过内存映射文件吗?谢谢您的建议。我对内存映射文件一无所知,但它们看起来很有希望。谢谢您的回答。似乎我没有很好地解释我自己。提供排序日志的程序在此之前运行。此程序合并了记录文件并在不写入合并文件的情况下查找匹配项。您的回答帮助我意识到不写入合并文件是一个错误。我的“服务器”(旧i5)现在正在运行所有内核,将123个日志文件减少到16个。再次感谢您为我指明了正确的方向。
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;
}