我有许多脚本,目前从一些.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%。节省不多......
是否有人建议我如何更有效地阅读这些数据?
感谢您的帮助。
答案 0 :(得分:11)
在磁盘上放置一个非常大的哈希的最简单方法是恕我直言,BerkeleyDB。它速度快,经过时间考验且坚如磐石,CPAN模块提供了一个绑定的API。这意味着你可以继续使用你的哈希,就好像它是一个内存数据结构,但它会自动通过BerkeleyDB读写到磁盘。
答案 1 :(得分:9)
答案 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之类的东西我从来不需要做这种事情,所以我没有太多其他东西可以提供。