C++ 在c+中合并大型二进制文件+;优化
背景: 我编写了一个修改过的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字节字符串的校验和。有人能想出更快的优化或替代方法吗? 请注意,我的内存中不能同时有两个以上的文件 迈克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个字节。 这是
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;
}