首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从文件中加载(大) std::vector<std::vector<float>>的更快方法

从文件中加载(大) std::vector<std::vector<float>>的更快方法
EN

Stack Overflow用户
提问于 2022-03-29 11:26:12
回答 3查看 371关注 0票数 0

我实现了一种将向量的std::vector保存到文件并使用以下代码读取它们的方法(在堆栈溢出中可以找到):

保留:

代码语言:javascript
复制
void saveData(std::string path)
{
    std::ofstream FILE(path, std::ios::out | std::ofstream::binary);

    // Store size of the outer vector
    int s1 = RecordData.size();
    FILE.write(reinterpret_cast<const char*>(&s1), sizeof(s1));

    // Now write each vector one by one
    for (auto& v : RecordData) {
        // Store its size
        int size = v.size();
        FILE.write(reinterpret_cast<const char*>(&size), sizeof(size));

        // Store its contents
        FILE.write(reinterpret_cast<const char*>(&v[0]), v.size() * sizeof(float));
    }
    FILE.close();
}

阅读:

代码语言:javascript
复制
void loadData(std::string path)
{
    std::ifstream FILE(path, std::ios::in | std::ifstream::binary);

    if (RecordData.size() > 0) // Clear data
    {
        for (int n = 0; n < RecordData.size(); n++)
            RecordData[n].clear();
        RecordData.clear();
    }

    int size = 0;
    FILE.read(reinterpret_cast<char*>(&size), sizeof(size));
    RecordData.resize(size);
    for (int n = 0; n < size; ++n) {
        int size2 = 0;
        FILE.read(reinterpret_cast<char*>(&size2), sizeof(size2));
        float f;
        //RecordData[n].reserve(size2); // This doesn't make a difference in speed
        for (int k = 0; k < size2; ++k) {
            FILE.read(reinterpret_cast<char*>(&f), sizeof(f));
            RecordData[n].push_back(f);
        }
    }
}

这是完美的,但是一个大数据集的加载(980 to,大小32000的内部向量,其中1600个)需要7-8秒(而不是保存,在1秒内完成)。因为我可以看到Visual中的内存使用量在加载过程中缓慢上升,所以我猜会有很多内存分配。不过,注释的RecordData[n].resize(size2);行并没有什么区别。

有人能给我一种更快的方式来加载这种数据吗?我的第一次尝试是将所有数据放入一个大的std::vector<float>中,但由于某种原因,这似乎会导致某种溢出(这是不应该发生的,因为40亿美元的sizeof(int) = 4应该足以满足一个索引变量的需要(std::向量在内部使用其他东西吗?)另外,如果有一个std::vector<std::vector<float>>的数据结构,那就太好了。在未来,我将不得不处理更大的数据集(我可能会使用<short>来节省内存,并将其作为一个固定的点号处理),因此加载速度将更加重要.

编辑:

我要指出的是,32000的内部向量和1600的外部向量只是一个例子。两者都可能不同。我想,我需要保存一个“索引向量”,作为第一个内部向量来声明其余的条目数量(就像我在评论中说的那样:我是第一次阅读文件/编写文件,并且已经使用std::vector超过一到两周了,所以我对此不太确定)。我将查看块阅读并在稍后的编辑中发布结果.

Edit2:

因此,这是一个版本的膜(感谢您)。我所做的唯一改变就是放弃RV& RecordData,因为对我来说这是一个全局变量。

奇怪的是,对于一个980 GB的文件,我的加载时间只从大约7000 ms降到了1500 ms,而对于一个2 GB文件,却没有从7429 ms降到644 ms(奇怪的是,不同系统的速度有多么不同;-)

代码语言:javascript
复制
void loadData2(std::string path)
{
    std::ifstream FILE(path, std::ios::in | std::ifstream::binary);

    if (RecordData.size() > 0) // Clear data
    {
        for (int n = 0; n < RecordData.size(); n++)
            RecordData[n].clear();
        RecordData.clear();
    }

    int size = 0;
    FILE.read(reinterpret_cast<char*>(&size), sizeof(size));
    RecordData.resize(size);
    for (auto& v : RecordData) {
        // load its size
        int size2 = 0;
        FILE.read(reinterpret_cast<char*>(&size2), sizeof(size2));
        v.resize(size2);

        // load its contents
        FILE.read(reinterpret_cast<char*>(&v[0]), v.size() * sizeof(float));
    }
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-03-29 12:28:04

这是Alan的注释的一个实现:当阅读时,使用一个FILE.read调用而不是多个单独的调用读取一个内部向量。这大大减少了我的系统上的时间:

以下是2GB文件的结果:

代码语言:javascript
复制
Writing    took 2283 ms
Reading v1 took 7429 ms
Reading v2 took 644 ms

下面是生成此输出的代码:

代码语言:javascript
复制
#include <vector>
#include <iostream>
#include <string>
#include <chrono>
#include <random>
#include <fstream>

using RV = std::vector<std::vector<float>>;

void saveData(std::string path, const RV& RecordData)
{
    std::ofstream FILE(path, std::ios::out | std::ofstream::binary);

    // Store size of the outer vector
    int s1 = RecordData.size();
    FILE.write(reinterpret_cast<const char*>(&s1), sizeof(s1));

    // Now write each vector one by one
    for (auto& v : RecordData) {
        // Store its size
        int size = v.size();
        FILE.write(reinterpret_cast<const char*>(&size), sizeof(size));

        // Store its contents
        FILE.write(reinterpret_cast<const char*>(&v[0]), v.size() * sizeof(float));
    }
    FILE.close();
}

//original version for comparison
void loadData1(std::string path, RV& RecordData)
{
    std::ifstream FILE(path, std::ios::in | std::ifstream::binary);

    if (RecordData.size() > 0) // Clear data
    {
        for (int n = 0; n < RecordData.size(); n++)
            RecordData[n].clear();
        RecordData.clear();
    }

    int size = 0;
    FILE.read(reinterpret_cast<char*>(&size), sizeof(size));
    RecordData.resize(size);
    for (int n = 0; n < size; ++n) {
        int size2 = 0;
        FILE.read(reinterpret_cast<char*>(&size2), sizeof(size2));
        float f;
        //RecordData[n].resize(size2); // This doesn't make a difference in speed
        for (int k = 0; k < size2; ++k) {
            FILE.read(reinterpret_cast<char*>(&f), sizeof(f));
            RecordData[n].push_back(f);
        }
    }
}

//my version
void loadData2(std::string path, RV& RecordData)
{
    std::ifstream FILE(path, std::ios::in | std::ifstream::binary);

    if (RecordData.size() > 0) // Clear data
    {
        for (int n = 0; n < RecordData.size(); n++)
            RecordData[n].clear();
        RecordData.clear();
    }

    int size = 0;
    FILE.read(reinterpret_cast<char*>(&size), sizeof(size));
    RecordData.resize(size);
    for (auto& v : RecordData) {
        // load its size
        int size2 = 0;
        FILE.read(reinterpret_cast<char*>(&size2), sizeof(size2));
        v.resize(size2);

        // load its contents
        FILE.read(reinterpret_cast<char*>(&v[0]), v.size() * sizeof(float));
    }
}

int main()
{
    using namespace std::chrono;
    const std::string filepath = "./vecdata";
    const std::size_t sizeOuter = 16000;
    const std::size_t sizeInner = 32000;
    RV vecSource;
    RV vecLoad1;
    RV vecLoad2;

    const auto tGen1 = steady_clock::now();
    std::cout << "generating random numbers..." << std::flush;
    std::random_device dev;
    std::mt19937 rng(dev());
    std::uniform_real_distribution<float> dis;
    for(int i = 0; i < sizeOuter; ++i)
    {
        RV::value_type inner;
        for(int k = 0; k < sizeInner; ++k)
        {
            inner.push_back(dis(rng));
        }
        vecSource.push_back(inner);
    }
    const auto tGen2 = steady_clock::now();

    std::cout << "done\nSaving..." << std::flush;
    const auto tSave1 = steady_clock::now();
    saveData(filepath, vecSource);
    const auto tSave2 = steady_clock::now();

    std::cout << "done\nReading v1..." << std::flush;
    const auto tLoadA1 = steady_clock::now();
    loadData1(filepath, vecLoad1);
    const auto tLoadA2 = steady_clock::now();
    std::cout << "verifying..." << std::flush;
    if(vecSource != vecLoad1) std::cout << "FAILED! ...";

    std::cout << "done\nReading v2..." << std::flush;
    const auto tLoadB1 = steady_clock::now();
    loadData2(filepath, vecLoad2);
    const auto tLoadB2 = steady_clock::now();
    std::cout << "verifying..." << std::flush;
    if(vecSource != vecLoad2) std::cout << "FAILED! ...";


    std::cout << "done\nResults:\n" <<
        "Generating took " << duration_cast<milliseconds>(tGen2 - tGen1).count() << " ms\n" <<
        "Writing    took " << duration_cast<milliseconds>(tSave2 - tSave1).count() << " ms\n" <<
        "Reading v1 took " << duration_cast<milliseconds>(tLoadA2 - tLoadA1).count() << " ms\n" <<
        "Reading v2 took " << duration_cast<milliseconds>(tLoadB2 - tLoadB1).count() << " ms\n" <<
        std::flush;
}
票数 3
EN

Stack Overflow用户

发布于 2022-03-29 11:52:53

首先,由于预先知道元素的数量,所以应该在向量中使用reserve空间,以防止向量增长时不必要的重新分配。其次,所有这些push_back都可能要花费您的钱。这个函数确实有一些开销。第三,就像Alan说的,一次读取整个文件不会有任何伤害,如果首先调整矢量的大小(而不是保留),就可以这样做。

因此,尽管如此,我还是会这样做的(一旦您将数据的大小读入size2):

代码语言:javascript
复制
RecordData.resize(size2);                // both reserves and allocates space for size2 items
FILE.read(reinterpret_cast<char*>(RecordData.data()), size2 * sizeof(float));

我认为这是最佳选择。

不幸的是,在这种情况下,国际海事组织,std::vector坚持零初始化所有的size2元素时,你调用resize,因为你马上要覆盖他们,但我不知道有一个容易防止这一点。您需要进入自定义分配程序,这可能是不值得的。

票数 3
EN

Stack Overflow用户

发布于 2022-03-29 11:48:07

代码语言:javascript
复制
//RecordData[n].resize(size2); // This doesn't make a difference in speed

如果您使用这一行(同时不更改其余代码),您应该期望代码更慢,而不是更快!

resize会更改向量的大小,然后将更多的元素推送到它,从而使向量的大小达到实际需要的两倍。

我想你想要的是reservereserve只分配容量而不改变矢量的大小,然后推送元素可能会更快,因为内存只分配一次。

或者使用resize,然后将其分配给已经存在的元素。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71661178

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档