用Tab解析最大文件(〜2 Gb)的最快方法

时间:2019-01-01 20:23:55

标签: c++ file io

请指导我:是否有更有效的方法来执行此类任务:

  1. 从第一个制表符分隔的文件中读取键/值对到地图容器中
  2. 从第二个制表符分隔的文件(键值对)中读取数据,同时写入第三个制表符

花了3700秒的时间,用7,000,000行对fname1和fname2执行了这两个步骤。

#include <string>
#include <map>
#include <fstream>
string col1;
int x;
map<string, int> m;
ifstream is1(fname1, ios::in | ios::binary);
ifstream is2(fname2, ios::in | ios::binary);
ofstream os3(fname3, ios::out | ios::binary);
if (is1.is_open())
{
    while (is1 >> col1 >> x)
    {
        m[col1] = x;
    }
    is.close();
}
if (is2.is_open() && os3.is_open())
{
    while (is2 >> col1 >> x)
    {
        if (m.count(col1) > 0)
            x += m[col1];
        os3 << col1 << "\t" << x << endl;
    }
    is2.close();
    os3.close();
}

我做错了什么?有没有更有效的方法来执行此类任务? 还是文件I / O在大多数情况下是瓶颈?

更新:在这里,我放置了同一算法的两个实现。主要问题:为什么pythonic版本更快?我已经决定转向C ++,因为我听说它提供了更快的代码。我错了吗?

fname1,fname2-输入。 fname3-所需的输出。

fname1:

col1 col2 col3

1 1 1

2 2 2

fname2:

col1 col2 col3

1 1 2

3 3 3

fname3:

col1 col2 col3

1 1 3

2 2 2

3 3 3

def merge_two_files(fname1, fname2, fname3):
    fout=open(fname3,'w')
    fin1=open(fname1)
    d1=dict()
    for line in fin1:
        l=line.strip().split('\t')
        key='\t'.join(l[0:2])
        d1[key] = float(l[2])
    fin1.close()
    d2=dict()
    fin2=open(fname2)
    for line in fin2:
        l=line.strip().split('\t')
        key='\t'.join(l[0:2])
        d2[key] = float(l[2])
    fin2.close()
    for e in d1.viewkeys() & d2.viewkeys():
        line_out='\t'.join([e,'{:.2f}'.format(d1[e]+d2[e])])
        fout.write(line_out+'\n')
    for e in d1.viewkeys() - (d1.viewkeys() & d2.viewkeys())
        line_out='\t'.join([e,'{:.2f}'.format(d1[e])])
        fout.write(line_out+'\n')
    for e in d2.viewkeys() - (d1.viewkeys() & d2.viewkeys())
        line_out='\t'.join([e,'{:.2f}'.format(d2[e])])
        fout.write(line_out+'\n')

#include <fstream>
#include <string>
#include <unordered_map>
#include <set>

using namespace std;

int main() {
    unordered_map < string, float > map1, map2 ;
    set < string > s1, s2, both ;
    string col1, col2, key, fname1, fname2, fname3 ;
    float col3 ;
    ifstream f1 ( fname1, ios::in | ios::binary) ;
    ifstream f2 ( fname2, ios::in | ios::binary) ;
    ofstream f3 ( fname3, ios::out | ios::binary) ;

    if ( f1.is_open() ) {
        while ( f1 >> col1 >> col2 >> col3 )
        key= col1 + "\t" + col2 ;
        map1.insert(make_pair(key,col3)) ;
        s1.insert(key) ;
    }
    f1.close()
    if ( f2.is_open() ) {
        while ( f2 >> col1 >> col2 >> col3 ) {
        key= col1 + "\t" + col2 ;
        map2.insert(make_pair(key,col3)) ;
        s2.insert(key) ;
        }
    }
    f2.close() ;

    set_intersection(s1.begin(), s1.end(),
                     s2.begin(), s2.end(),
                     inserter(both, both.begin())) ;
    if ( f3.is_open() ) {
        for ( const auto& e : both ) {
            f3 << e << "\t" << map1.at(e) + map2.at(e) << "\n" ;
        }
        for ( const auto& kv : map1 ) {
            if ( both.count(kv.first) ) continue ;
            f3 << kv.first << "\t" << kv.second << "\n" ;
        }
        for ( const auto& kv : map2 ) {
            if ( both.count(kv.first) ) continue ;
            f3 << kv.first << "\t" << kv.second << "\n" ;
        }
    }
    f3.close() ;
    return 0;
}

2 个答案:

答案 0 :(得分:1)

至少在这些文件中的大量不同键的限制(我认为您在这里足够接近)的情况下,限制因素将是map键查找,而不是IO。在std::map中查找键具有时间复杂度O(ln n),其中n是容器中不同键的数量。

使用std::unordered_map,该费用平均分摊了O(1)的关键查找时间。

在开始根据错误的假设进行优化之前,请使用探查器告诉您大部分时间在哪里。在这种情况下,请执行相同的操作以验证我是否正确。

答案 1 :(得分:1)

瓶颈

您的瓶颈是1)文件I / O和2)格式转换。文件可能需要开销,例如旋转(硬盘驱动器),查找时间和实际读取时间。

流数据

无论文件位于闪存驱动器还是硬盘驱动器上,流式传输数据都是最高效的(也就是说,保持数据畅通)。例如,在1个事务中读取1k数据比在1k事务中读取1k数据更有效。您最有效的方法是将大量读入内存。您可能需要检查操作系统,以查看其是否支持内存映射。

转换数据

下一个瓶颈是解析并将数据从文本格式转换为内部数字格式。这需要时间。为了加快速度,请以二进制(内部)表示形式编写数据。由于Endianess或浮点问题,这可能无法实现。

存储数据

我想下一个瓶颈是将内部格式化的数据存储到数据结构中。对于键,值,对,您可能需要使用std::mapstd::unordered_map。如果您不关心搜索时间,则可能需要使用值创建一个struct并将其存储到一个数组中(如果在编译时未知数量,则可以存储为std::vector)。

分析

找出瓶颈所在的最佳方法是 profile 。运行您的应用程序1E6重复并取平均值(您可能需要运行1E9以获得更好的准确性)。在互联网上搜索“基准C ++程序”。基准测试将向您展示如何获得更好的结果。