为什么转置此std :: vector <std :: vector <std :: string>>这么慢?

时间:2019-10-03 08:47:10

标签: c++ string multidimensional-array transpose

我有一个大约400MB的1000行文件,该文件代表一些表示为字符串的数字数据。 我想转置数据以便每行只有1000个字符串,(以便我可以打开它并用pandas快速绘制)。

我将整个文件导入到要转置的字符串向量中(最终想写回文件)。

我使用两个嵌套循环来遍历2d结构,并将其写入一些std :: ofstream中。很长 然后,我尝试着眼于移调,并编写了以下代码:

//Read 400MB file, 90K strings per line and 1K lines, and store it into
std::vector<std::vector<std::string>> mData;

// ... 
// IO the file and populate mData with raw data 
// ...

//All rows have same number of string
size_t nbRows = mData.size();
size_t nbCols = mData[0].size();

std::vector<std::vector<std::string> > transposedData(nbCols);
for(size_t i = 0 ; i < nbCols  ; ++i)
{
    transposedData[i].resize(nbRows);
    for(size_t j = 0 ; j < nbRows ; ++j)
    {
        transposedData[i][j] = doc.mData[j][i];
    }
}

我认为几秒钟就足够了,但是要花几分钟。 另外,我尝试使用不同的输入尺寸(对于3MB的文件,每行只有更多的字符串,对于400MB的相同文件大小),并且输入速度要快得多。

编辑1

根据人们的建议,我使用callgrind进行了性能分析。 在此过程中,我收到了以下消息: ...线程1中的brk段溢出:无法增长到...

我分析了结果并在这里总结:
25%花费在basic_string的operator =中
21%的成本用于basic_string的构造(新时间只有3%)
外部向量在operator()[]中花费了14%
内部向量

上的operator()[]花费了11%

谢谢您的建议。

4 个答案:

答案 0 :(得分:1)

首先,在对一段代码缓慢的原因进行任何声明之前,您应该真正在计算机上测量其性能,然后通过手头的数据推断出为什么

这表示我对此很有信心,因为问题可能出在以下事实:您正在分配90k个字符串向量,每个向量的大小为1k。如您所知,内存分配是昂贵的,它可能可以解释您的性能损失。

以下是仅使用预先分配的1D数组来实现代码的方法。

size_t nbRows = mData.size();
size_t nbCols = mData[0].size();

auto get_idx = [](const int i, const int nr, const int j)
{
    return i*nr+j;
};

std::vector<std::string> transposedData(nbCols*nbRows);  
for(size_t i = 0 ; i < nbCols  ; ++i)
{
    for(size_t j = 0 ; j < nbRows ; ++j)
    {
        const int idx = get_idx(j, nbCols,i);
        transposedData[idx] = std::move(mData[j][i]);
    }
}

for(size_t i = 0 ; i < nbCols  ; ++i)
{
    for(size_t j = 0 ; j < nbRows ; ++j)
    {
        const int idx = get_idx(j, nbCols,i);
        cout<<transposedData[idx]<<" ";
    }
    cout<<endl;
}    

我想再次强调一下:分析您的代码。试用valgrind --tool= callgrindgprof之类的软件,使您可以分析和可视化有关应用程序的性能数据。

答案 1 :(得分:1)

该程序在多个级别都有冗余。

显而易见的是,您无需为转置文件而转置向量。

vector<vector<string> originalData;
// read the file to originalData

for(size_t i = 0 ; i < nbCols  ; ++i)
{
    for(size_t j = 0 ; j < nbRows ; ++j)
    {
        cout << originalData[j][i] << " ";
    }
    cout<<endl;
}

假设由于某些原因确实需要产生转置向量,编写转置循环的一种方法是

vector<vector<string>> transposedData (nbCols);
for (size_t j = 0; j < nbCols; ++j)
{
    transposedData[j].reserve(nrows);
    for (size_t i = 0; i < nbRows; ++i) 
    {
        transposedData[j].emplace_back(originalData[i][j]);
        // if keeping original veector is not needed ...
        // transposedData[j].emplace_back(std::move(originalData[i][j]));
    }
}

在我的(相当强壮)的机器上,大约需要6-7秒来转置1000x90000的3个字符的字符串矩阵。这并不是特别令人印象深刻,如果您不需要每天24小时转置数百万个元素的矩阵,那么它就可以满足您的需要而没有太多的开销。

答案 2 :(得分:0)

惩罚可能是由于您在for循环中过度使用了调整大小的原因。

根据reference

  

复杂度

     

当前大小和计数之间的线性差异。如果容量小于数量,则由于重新分配而可能导致额外的复杂性

内存分配的成本很高,因此您可能要避免过多使用内存。

正如其他人指出的那样,预先分配将是避免每次重新创建向量(调整大小)的有趣方法。

答案 3 :(得分:0)

我的计算机上没有足够的可用内存来执行此任务(请参见下文)。 将数据分为三个部分,我在几秒钟内解决了任务。 这是检查内存的代码的输出:

free ram 2.5GB  
IO populating mData with raw data  
free ram 0.2GB  
Empty string capacity : 15 bytes  
Intending to allocate 1.4 GB  
terminate called after throwing an instance of 'std::bad_alloc'  
  what() : std::bad_alloc  
Aborted