随机访问文件中数据的最佳方法是什么?

时间:2017-01-31 08:01:11

标签: c++ multithreading c++11 file-io fstream

我一直在寻找这个问题的答案,但我没有找到。我目前正在使用fstream在标识为std::ios_base::in | std::ios_base::out | std::ios_base::binary的硬盘上打开文件以进行随机访问。

文件中存在的数据是记录,每条记录的大小为16 bytes。大约0.479604 seconds336.357 kb个记录并行写入文件(两个线程)。我没有使用任何特定的策略来最佳地执行读写操作。没有线程,相同的操作花了0.352716 seconds(时间差是预期的)。

这些是我用于执行读写操作的方法:

void FileBascIO::createFile(const std::string& fName) {
    std::fstream fcreate(fName.c_str(), std::ios_base::out | std::ios_base::binary);
}


FileBascIO::returnTypeRead FileBascIO::readFromFile(const std::string& fName, int64_t pos,
                        FileFlagType relativeInitial, char* data, uint64_t size) {

    std::fstream fio (fName.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
    fio.seekg(pos, relativeInitial);
    return fio.read(data, size);
}


FileBascIO::returnTypeWrite FileBascIO::writeToFile(const std::string& fName, int64_t pos,
                        FileFlagType relativeInitial, char* data, uint64_t size) {

    std::fstream fio (fName.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary);

    if (!fio) {
        fio.close();
        fio.clear();
        createFile (fName);
        fio.open(fName.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
    }
    fio.seekp(pos, relativeInitial);
    return fio.write(data, size);
}

以上代码效率不高,我不知道如何提高效率。我做了一些搜索,我开始明白在执行操作之前,应该预先读取整个文件或文件的大块,并将其存储在RAM中。这是因为硬盘中数据的物理位置很重要。一旦字段更新,整个文件必须以大块写回硬盘。

在顺序写入16个字节的例子中,首先将数据首先存储在RAM存储器中并将其写入大块的文件中是不是更好?如果我手动执行此操作,我应该选择哪种块大小?什么是更有效地实现随机读/写访问的最佳替代方法?

修改

从以下评论中,解决此问题的最佳方法是使用文件的内存映射,并让操作系统处理所有优化内容,如@Sami Kuhmonen所指出的那样。我还没试过这个。另一个建议是不要像@ Jean-BaptisteYunès所指出的那样多次关闭和打开文件。应用该解决方案后,即使使用线程,代码也会在0.166191秒内运行。

更新后的来源:

标题

    struct FstreamPtrContainer {
    std::map <std::string, std::fstream*> fileStreamMap;
    ~FstreamPtrContainer();
};

struct FileBascIO{



    static FstreamPtrContainer fileStreamContainer;

    typedef decltype( ((std::fstream*) nullptr)->read((char*) nullptr, uint64_t() ))  returnTypeRead;
    typedef decltype( ((std::fstream*) nullptr)->write((char*) nullptr, uint64_t() )) returnTypeWrite;
    typedef decltype( std::ios_base::beg )                                            FileFlagType;

    static returnTypeRead readFromFile (const std::string& fName, int64_t pos,
                                        FileFlagType relativeInitial, char* data, uint64_t size);

    static returnTypeWrite writeToFile (const std::string& fName, int64_t pos,
                                        FileFlagType relativeInitial, char* data, uint64_t size);

    static void createFile (const std::string& fName);

    static void flushAll();

};

在cpp文件中

FstreamPtrContainer::~FstreamPtrContainer() {
    for (auto it : fileStreamMap){
        it.second->close();
        delete it.second;
    }
}



FstreamPtrContainer FileBascIO::fileStreamContainer;

void FileBascIO::createFile(const std::string& fName) {
    std::fstream fcreate(fName.c_str(), std::ios_base::out | std::ios_base::binary);
}

void FileBascIO::flushAll() {
    for (auto it : fileStreamContainer.fileStreamMap) {
        it.second->flush();
    }
}


FileBascIO::returnTypeRead FileBascIO::readFromFile(const std::string& fName, int64_t pos,
                        FileFlagType relativeInitial, char* data, uint64_t size) {

    auto it = fileStreamContainer.fileStreamMap.find(fName);
    std::fstream* fio;

    if (it == fileStreamContainer.fileStreamMap.end()) {
        fio =  new std::fstream (fName.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
        fileStreamContainer.fileStreamMap [fName] = fio;
    }
    else {
        fio = it->second;
        fio->flush();
    }

    fio->seekg(pos, relativeInitial);
    return fio->read(data, size);
}


FileBascIO::returnTypeWrite FileBascIO::writeToFile(const std::string& fName, int64_t pos,
                        FileFlagType relativeInitial, char* data, uint64_t size) {

    auto it = fileStreamContainer.fileStreamMap.find(fName);
    std::fstream* fio;

    if (it == fileStreamContainer.fileStreamMap.end())  {
        fio  = new std::fstream(fName.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary);

        if (!(*fio)) {
            fio->close();
            fio->clear();
            createFile (fName);
            fio->open(fName.c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::binary);
        }

        fileStreamContainer.fileStreamMap [fName] = fio;

    } else{
        fio = it->second;
        fio->flush();
    }

    fio->seekp(pos, relativeInitial);
    return fio->write(data, size);
}

现在执行时间几乎减半。

0 个答案:

没有答案