我实现了一种将向量的std::vector保存到文件并使用以下代码读取它们的方法(在堆栈溢出中可以找到):
保留:
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();
}阅读:
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(奇怪的是,不同系统的速度有多么不同;-)
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));
}
}发布于 2022-03-29 12:28:04
这是Alan的注释的一个实现:当阅读时,使用一个FILE.read调用而不是多个单独的调用读取一个内部向量。这大大减少了我的系统上的时间:
以下是2GB文件的结果:
Writing took 2283 ms
Reading v1 took 7429 ms
Reading v2 took 644 ms下面是生成此输出的代码:
#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;
}发布于 2022-03-29 11:52:53
首先,由于预先知道元素的数量,所以应该在向量中使用reserve空间,以防止向量增长时不必要的重新分配。其次,所有这些push_back都可能要花费您的钱。这个函数确实有一些开销。第三,就像Alan说的,一次读取整个文件不会有任何伤害,如果首先调整矢量的大小(而不是保留),就可以这样做。
因此,尽管如此,我还是会这样做的(一旦您将数据的大小读入size2):
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,因为你马上要覆盖他们,但我不知道有一个容易防止这一点。您需要进入自定义分配程序,这可能是不值得的。
发布于 2022-03-29 11:48:07
//RecordData[n].resize(size2); // This doesn't make a difference in speed如果您使用这一行(同时不更改其余代码),您应该期望代码更慢,而不是更快!
resize会更改向量的大小,然后将更多的元素推送到它,从而使向量的大小达到实际需要的两倍。
我想你想要的是reserve。reserve只分配容量而不改变矢量的大小,然后推送元素可能会更快,因为内存只分配一次。
或者使用resize,然后将其分配给已经存在的元素。
https://stackoverflow.com/questions/71661178
复制相似问题