2017-04-22 25 views
2

我必须加载大文件(几GB)的数据,我想加载它们到二维向量。下面的代码可以完成这项工作,但它非常缓慢。更具体地说,目标是获得第二列中的值等于索引(_lh,_sh)的所有行。然后排除第4列值与第1行和第1行相同的行。 现在,我是新来的c + +和我平常Python代码(已经有这个问题的工作代码)。但我需要它尽可能快,所以我试图将我的Python代码重写为C++。但现在它比Python慢​​(并且只能将数据传递给矢量)......所以在我继续之前,我想改进它。 从我发现的类似问题中,问题将是动态向量,.push_back()和getline()。如何加快文本文件加载到多向量

对于在类似问题中提到的映射和块加载,我感到非常困惑,所以我不能根据这些改变代码。

你能帮我优化这段代码吗?

谢谢。

#include <iostream> 
#include <sstream> 
#include <fstream> 
#include <array> 
#include <string> 
#include <vector> 

using namespace std; 

int pixel(int radek, int sloupec, int rozmer = 256) { 
    int index = (radek - 1) * rozmer + sloupec; 
    int index_lh = (index - rozmer - 1); 
    int index_sh = (index - rozmer); 
    int index_ph = (index - rozmer + 1); 
    int index_l = (index - 1); 
    int index_p = (index + 1); 
    int index_ld = (index + rozmer - 1); 
    int index_sd = (index + rozmer); 
    int index_pd = (index + rozmer + 1); 
    array<int, 9> index_all = { {index, index_lh, index_sh, index_ph, index_l, index_p, index_ld, index_sd, index_pd } }; 
    vector<vector<string>> Data; 
    vector<string> Line; 
    string line; 

    for (int m = 2; m < 3; m++) { 
     string url = ("e:/TPX3 - kalibrace - 170420/ToT_ToA_calib_Zn_" + to_string(m) + string(".t3pa")); 
     cout << url << endl; 
     ifstream infile(url); 
     if (!infile) 
     { 
      cout << "Error opening output file" << endl; 
      system("pause"); 
      return -1; 
     } 
     while (getline(infile, line)) 
     { 
      Line.push_back(line); 
      istringstream txtStream(line); 
      string txtElement; 
      vector<string> Element; 
      while (getline(txtStream, txtElement, '\t')){ 
       Element.push_back(txtElement); 
      } 
      Data.push_back(Element); 
     } 
    } 
    cout << Data[1][0] << ' ' << Data[1][1] << ' ' << Data[1][2] << endl; 
    return 0; 
} 

int main() 
{ 
    int x = pixel(120, 120); 
    cout << x << endl; 
    system("pause"); 
    return 0; 
} 
+0

Boost有一些库,用于*非常*快速的读/写I/O操作。如果你可以使用它们当然。 – Rakete1111

+0

不是'for(int m = 2; m <3; m ++)'是否意味着循环只进行一次?那是故意的吗? – InternetAussie

+0

什么是慢?从文件加载到二维数组还是搜索? – arynaq

回答

2

如果经常对基础缓冲区进行重新分配,则向量可能会变慢。向量需要在连续内存的缓冲区上实现,并且每次超过缓冲区限制时,都必须分配一个新的更大的缓冲区,然后将内容从旧缓冲区复制到新缓冲区。如果您了解您需要多大的缓冲区(您不需要排除),您可以通过使用例如程序来帮助程序分配适当大小的缓冲区。 Data.reserve(n)(其中n大约是您认为需要的元素数量)。这个注意事项改变了矢量的“大小”,只是基础缓冲区的大小。作为结束语,我不得不说,我从来没有对此做过基准测试,因此这可能会或可能不会提高您的计划的性能。

编辑:虽然,我认为它有点更可能是性能是由线Data.push_back(Element);,它使元素向量的副本瓶装。如果您使用的是C++ 11,我相信可以通过采取类似Data.emplace_back(std::move(Element));的方法来解决此问题,在这种情况下,以后不能更改Element(它的内容已移动)。您还需要包含memorystd::move

+0

这有点帮助。代码在1:40分钟内运行400k行和6列之前。现在是1:01分钟......但仍然很慢。 比较8.7s中的Python完成和数据输出到文件。 – Pedro

+0

好的...与您的下一个建议它现在运行45秒。对'Element'和'Data'都使用'move',并删除了'Line.push_back(line);'(忘记从之前的尝试中删除它)。 – Pedro

+0

有趣的是,似乎有必要开始使用C++技巧,甚至可以接近这些类型的东西的python实现的速度:P和C++应该是快速的。如果你没有使用C语言的任何io函数,你可以尝试调用'std :: ios_base :: sync_with_stdio(false)',我已经体验到这个_can_可以相当大地加速数据流。 – mous

0

您也可以使用向量上的reserve(int),以便创建更接近目标大小。

这样也可以避免大量的向量在堆周围跳跃,因为向量只会被重新创建并通过目标大小。

你可以打电话,如果矢量通过先前预留的尺寸重新保留:

vector<int> vec; 
vec.reserve(10); 
for (int i=0;i < 1000; i++) 
{ 
    if (vec.size() == vec.capacity()) 
    { 
     vec.reserve(vec.size()+10); 

    } 
    vec.push_back(i); 
} 
1

您可以尝试使用旧的C文件中读取API(FILE*fopen()等)或设置std::istringstream一个更大的缓冲区如下

constexp std::size_t dimBuff { 10240 } // 10K, by example 
char myBuff[dimBuff]; 

// ... 

istringstream txtStream(line); 
txtStream.rdbuf()->pubsetbuf(myBuff, dimBuff); 

另一件事,你可以尝试使用,而不是std::vector小号std::deque S(但我不知道这是否有用)。

正如muos所建议的那样,您可以使用移动语义;你也可以使用emplace_back()

所以我建议尝试与

Element.push_back(std::move(txtElement)); 

Data.push_back(std::move(Element)); 

Element.emplace_back(std::move(txtElement)); 

Data.emplace_back(std::move(Element)); 

您还可以SWITH以下行(没有一个字符串的举动构造函数std::istringstream,如果我没有错)

Line.push_back(line); 
istringstream txtStream(line); 

增加移动语义(和emplace_back()

istringstream txtStream(line); 
Line.emplace_back(std::move(line)); 

PS:明明reserve()是有用的

+0

已经做到了穆斯回答。它有所帮助,但Python仍然快6倍。 – Pedro

+0

@Pedro - anser改进了其他一些建议。希望这可以帮助。 – max66

1

在while循环,你可以尝试从

while (getline(infile, line)) 
{ 
    Line.push_back(line); 
    istringstream txtStream(line); 
    string txtElement; 
    vector<string> Element; 
    while (getline(txtStream, txtElement, '\t')){ 
     Element.push_back(txtElement); 
    } 
    Data.push_back(Element); 
} 

改变线路:

while (getline(infile, line)) 
{ 
    Line.push_back(line); 
    istringstream txtStream(line); 
    string txtElement; 
    //vector<string> Element; [-] 
    Data.emplace_back(); // [+] 
    while (getline(txtStream, txtElement, '\t')) { 
     //Element.push_back(txtElement); [-] 
     Data.back().push_back(txtElement); // [+] 
    } 
    //Data.push_back(Element); [-] 
} 

这样,矢量在Data不需要移动或复制那里 - 他们已经构建,虽然是空的。 Data中的向量是默认构建的,其格式为.emplace_back()。我们通过.back()函数获得Data中的最后一个元素,并像往常一样将值推到.push_back()。希望这有助于:)