C++ C++;二进制文件I/O操作速度减慢。。。数据库如何处理二进制文件?

C++ C++;二进制文件I/O操作速度减慢。。。数据库如何处理二进制文件?,c++,fstream,binaryfiles,c++-ios,C++,Fstream,Binaryfiles,C++ Ios,我正在尝试制作一个简单的基于文件的哈希表。这是我的insert成员函数: private: std::fstream f; // std::ios::in | std::ios::out | std::ios::binary public: void insert(const char* this_key, long this_value) { char* that_key; long that_value; long this_hash = std::hash<

我正在尝试制作一个简单的基于文件的哈希表。这是我的
insert
成员函数:

private: std::fstream f;  // std::ios::in | std::ios::out | std::ios::binary

public: void insert(const char* this_key, long this_value) {
    char* that_key;
    long that_value;
    long this_hash = std::hash<std::string>{}(this_key) % M;
    long that_hash;  // also block status

    long block = this_hash;
    long offset = block * BLOCK_SIZE;
    while (true) {
        this->f.seekg(offset);
        this->f.read((char*) &that_hash, sizeof(long));
        if (that_hash > -1) {  // -1 (by default) indicates a never allocated block
            this->f.read(that_key, BLOCK_SIZE);
            if (strcmp(this_key, that_key) == 0) {
                this->f.seekp(this->f.tellg());
                this->f.write((char*) &this_value, sizeof(long));
                break;
            } else {
                block = (block + 1) % M;  // linear probing
                offset = block * BLOCK_SIZE;
                continue;
            }
        } else {
            this->f.seekp(offset);
            this->f.write((char*) &this_hash, sizeof(long));  // as block status
            this->f.write(this_key, KEY_SIZE);
            this->f.write((char*) &this_value, sizeof(long));
            break;
        }
    }
}
private:std::fstream f;//std::ios::in | std::ios::out | std::ios::binary
public:void insert(常量字符*此_键,长此_值){
char*那把钥匙;
长时间使用该值;
long this_hash=std::hash{}(this_key)%M;
long表示_hash;//还包括块状态
长块=这个散列;
长偏移=块*块大小;
while(true){
此->f.seekg(偏移量);
this->f.read((char*)&that_散列,sizeof(long));
if(that_hash>-1){/-1(默认情况下)表示从未分配的块
此->f.read(该键,块大小);
if(strcmp(这个密钥,那个密钥)==0){
this->f.seekp(this->f.tellg());
this->f.write((char*)&this_值,sizeof(long));
打破
}否则{
block=(block+1)%M;//线性探测
偏移=块*块大小;
继续;
}
}否则{
此->f.seekp(偏移量);
this->f.write((char*)&this_hash,sizeof(long));//作为块状态
此->f.写入(此键,键大小);
this->f.write((char*)&this_值,sizeof(long));
打破
}
}
}
测试了多达10米的键、值对和50000017个块。(二进制文件大小约为3.8GB)

然而,使用50米键和250000013个块的测试速度非常慢。。。(在本例中,二进制文件大小超过19GB)。1000
insert
s通常需要4~5ms,但异常情况下需要超过2000ms。它变得越来越慢,然后需要40~150毫秒。。。(x10~x30较慢…)我绝对不知道

  • 是什么导致此异常二进制文件I/O速度减慢
  • 文件大小是否会影响
    seekg
    &
    seekp
    和其他I/O操作?(但我找不到关于这个问题的任何参考资料…)
  • 键、值存储和数据库如何避免这种I/O速度减慢
  • 我怎样才能解决这个问题

数据大小

通常,磁盘驱动器块大小是2的幂,因此,如果数据块大小也是2的幂,则基本上可以消除数据块跨越磁盘块边界的情况

在您的情况下,64字节的值(如果不需要存储哈希,则为32字节)可能会有更好的性能

插入顺序

为了提高性能,您可以做的另一件事是增加哈希顺序,以减少必须从磁盘加载数据的时间

通常,当数据读取或写入磁盘时,操作系统会一次读取/写入一个大卡盘(可能是4k),因此,如果写入算法是一种及时在本地写入数据的方法,那么您将减少数据必须实际读取或写入磁盘的时间

考虑到您进行了大量的插入,一种可能是一次处理1000甚至10000个键/值对的批量插入。基本上,您将在内存中积累数据并对其进行排序,一旦有足够的项(或插入完毕),您将按顺序写入数据

这样,您应该能够减少非常慢的磁盘访问。如果您使用的是传统硬盘,这一点可能更为重要,因为移动磁头的速度很慢(在这种情况下,对磁头进行碎片整理可能会很有用)。此外,请确保硬盘有足够的可用空间

在某些情况下,本地缓存(在应用程序中)可能也很有用,特别是当您知道如何使用数据时

文件大小与冲突的关系

使用散列时,您希望找到文件大小和冲突之间的最佳点。如果你有太多的碰撞,那么你将浪费大量的时间,在某个点上,当几乎每次插入都很难找到一个空闲的位置时,它可能会退化

另一方面,如果您的文件确实非常大,您可能最终会遇到这样的情况:您可能会用主要为空的数据填充RAM,并且在几乎所有插入操作中仍然需要用磁盘中的数据替换数据

例如,如果您的数据是20GB,并且可以在内存中加载(比如2GB),那么如果插入是随机的,那么90%的时间您可能需要真正访问硬盘

配置

选项将取决于操作系统,这超出了编程论坛的范围。如果你想优化你的电脑,那么你应该去别处看看

阅读

阅读操作系统(文件系统、缓存层…)和算法(外部排序算法、B-树和其他结构)可能会有帮助,以便更好地理解

备选方案

  • 额外RAM
  • 快速固态硬盘
  • 多线程(例如输入和输出线程)
  • 重写算法(例如,一次读取/写入整个磁盘页)
  • 更快的CPU/64位计算机
  • 使用针对此类场景设计的算法
  • 使用数据库
  • 分析代码
  • 调谐参数

导致大数据速度减慢的一个原因是,任何缓存最终都会被填满,因此在某个时候,您可能必须始终从慢速介质重新加载数据。如何防止经济放缓?(1) 添加更多内存,(2)确保数据的结构能够最大限度地减少缓存的失效(预期在某个时间点访问的数据的位置)。因此,数据的物理结构应该与访问模式兼容。在您的情况下,如果线性探测经常发生在数据较大的情况下,那么您将得到一个减速,您所说的“添加更多内存”是什么意思?如果它是关于RAM的,那么它几乎使用