请指导我:是否有更有效的方法来执行此类任务:
花了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;
}
答案 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::map
或std::unordered_map
。如果您不关心搜索时间,则可能需要使用值创建一个struct
并将其存储到一个数组中(如果在编译时未知数量,则可以存储为std::vector
)。
找出瓶颈所在的最佳方法是 profile 。运行您的应用程序1E6重复并取平均值(您可能需要运行1E9以获得更好的准确性)。在互联网上搜索“基准C ++程序”。基准测试将向您展示如何获得更好的结果。