如何从Perl快速访问许多大型CSV文件中的数据?

时间:2009-07-24 20:34:52

标签: perl csv persistence

我有许多脚本,目前从一些.CSV文件中读取大量数据。为了提高效率,我使用Text::CSV_XS模块读取它们,然后使用其中一列作为索引创建哈希。但是,我有一个很多的文件,它们非常大。每个脚本都需要重新读取数据。

问题是:如何持久存储这些Perl哈希值,以便用最少的CPU读回所有这些哈希值?

组合脚本不是一种选择。我希望......

我应用了第二条优化规则并使用分析来发现绝大多数CPU(大约90%)都在:

Text::CSV_XS::fields
Text::CSV_XS::Parse
Text::CSV_XS::parse

所以,我制作了一个读取所有.CSV文件( Text :: CSV_XS )的测试脚本,使用 Storable 模块转储它们,然后返回并使用可存储模块阅读它们。我描述了这个,所以我可以看到CPU时间:

$ c:/perl/bin/dprofpp.bat
Total Elapsed Time = 1809.397 Seconds
  User+System Time = 950.5560 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c  Name
 25.6   243.6 243.66    126   1.9338 1.9338  Storable::pretrieve
 20.5   194.9 194.92 893448   0.0002 0.0002  Text::CSV_XS::fields
 9.49   90.19 90.198 893448   0.0001 0.0001  Text::CSV_XS::Parse
 7.48   71.07 71.072    126   0.5641 0.5641  Storable::pstore
 4.45   42.32 132.52 893448   0.0000 0.0001  Text::CSV_XS::parse
 (the rest was in terms of 0.07% or less and can be ignored)

因此,与 Text :: CSV_XS 相比,使用可存储成本大约需要25.6%才能重新加载,大约35%。节省不多......

是否有人建议我如何更有效地阅读这些数据?

感谢您的帮助。

5 个答案:

答案 0 :(得分:11)

在磁盘上放置一个非常大的哈希的最简单方法是恕我直言,BerkeleyDB。它速度快,经过时间考验且坚如磐石,CPAN模块提供了一个绑定的API。这意味着你可以继续使用你的哈希,就好像它是一个内存数据结构,但它会自动通过BerkeleyDB读写到磁盘。

答案 1 :(得分:9)

解析数据并将其放入SQLite db。使用DBI进行查询。

答案 2 :(得分:3)

好吧,我接受了SinanÜnür的建议(谢谢!)并创建了一个SQLite数据库并重新运行我的测试程序,比较通过CSV文件获取数据,而不是从SQLite数据库中获取数据:

$ c:/perl/bin/dprofpp.bat
Total Elapsed Time = 1705.947 Seconds
  User+System Time = 1084.296 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c  Name
 19.5   212.2 212.26 893448   0.0002 0.0002  Text::CSV_XS::fields
 15.7   170.7 224.45    126   1.3549 1.7814  DBD::_::st::fetchall_hashref
 9.14   99.15 99.157 893448   0.0001 0.0001  Text::CSV_XS::Parse
 6.03   65.34 164.49 893448   0.0001 0.0002  Text::CSV_XS::parse
 4.93   53.41 53.412 893574   0.0001 0.0001  DBI::st::fetch
   [ *removed the items of less than 0.01 percent* ]

CSV_XS的总数为34.67%,而SQLite的总数为20.63%,这比我之前尝试的可存储解决方案要好一些。但是,这不是一个公平的比较,因为使用CSV_XS解决方案我必须加载整个 CSV文件,但是使用SQLite界面,我可以加载我想要的部分。因此,在实践中,我期望比这个简单的测试表明更多的改进。

我没有尝试使用BerkeleyDB(抱歉,炒作)而不是SQLite,主要是因为在我参与尝试SQLite之前我没有看到这个建议。设置测试是一项非常重要的任务,因为我几乎从未有机会使用SQL数据库。

但是,解决方案显然是将所有数据加载到数据库中并通过DBI模块进行访问。谢谢大家的帮助。所有回复都非常感谢。

答案 3 :(得分:2)

每次运行脚本时,最好不要将整个列表拉入内存。使用磁盘数据库将允许您执行此操作。如果出于某种原因,每次运行时都必须触摸CSV文件中的每个条目,我可能会建议将其存储在RAM磁盘而不是物理磁盘上。它显然适合内存,我不认为通过更改存储它的磁盘格式可以获得很大的改进。真正加速它的唯一方法是将它存储在更快的介质上。

答案 4 :(得分:1)

如果您只需要访问每个脚本中的部分数据,而不是全部,DBM::Deep可能是您最好的选择。

无论你做什么,磁盘/ IO都可能是你最大的瓶颈。也许你可以使用一个数据提供程序来保存mmapped缓存中的所有数据 - 使用像Sys::Mmap::Simple之类的东西我从来不需要做这种事情,所以我没有太多其他东西可以提供。