如何从 Perl 快速访问许多大型 CSV 文件中的数据?
Posted
技术标签:
【中文标题】如何从 Perl 快速访问许多大型 CSV 文件中的数据?【英文标题】:How can I access the data in many large CSV files quickly from Perl? 【发布时间】:2010-11-13 21:09:26 【问题描述】:我有许多脚本,它们目前从一些 .CSV 文件中读取大量数据。为了提高效率,我使用Text::CSV_XS 模块将它们读入,然后使用其中一列作为索引创建散列。但是,我有很多 很多 文件,而且它们都很大。每个脚本都需要重新读取数据。
问题是:我怎样才能持久存储这些 Perl 哈希,以便用最少的 CPU 读回它们?
组合脚本不是一种选择。我希望...
我应用了优化的第二条规则并使用 profiling 发现绝大多数 CPU(大约 90%)在:
Text::CSV_XS::fields
Text::CSV_XS::Parse
Text::CSV_XS::parse
所以,我制作了一个读取所有 .CSV 文件 (Text::CSV_XS) 的测试脚本,使用 Storable 模块转储它们,然后返回并使用 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)
因此,使用 Storable 的加载成本约为 25.6%,而 Text::CSV_XS 的加载成本约为 35%。节省不多...
有没有人建议我如何更有效地读取这些数据?
感谢您的帮助。
【问题讨论】:
【参考方案1】:恕我直言,在磁盘上放置一个非常大的散列的最简单方法是使用BerkeleyDB。它速度快、经过时间考验且坚如磐石,CPAN 模块提供了一个绑定的 API。这意味着您可以继续使用您的哈希,就像它是一个内存数据结构一样,但它会自动通过 BerkeleyDB 读取和写入磁盘。
【讨论】:
【参考方案2】:解析一次数据并将其放入SQLite db。使用DBI查询。
【讨论】:
你和friedo 都对我竖起大拇指。 如果您不需要写访问权限,这就是您要走的路。 感谢您的建议;这就是我的方式。测试结果发布在单独的答案中。【参考方案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%,这比我之前尝试过的 Storable 解决方案要好一些。然而,这不是一个公平的比较,因为使用 CSV_XS 解决方案我必须加载 整个 CSV 文件,但使用 SQLite 界面,我可以只加载我想要的部分。因此,在实践中,我期望比这个头脑简单的测试显示的改进更多。
我没有尝试过使用 BerkeleyDB(抱歉,friedo)代替 SQLite,主要是因为在我充分参与试用 SQLite 之前我没有看到这个建议。设置测试是一项非常重要的任务,因为我几乎从来没有机会使用 SQL 数据库。
不过,解决方案显然是将所有数据加载到数据库中并通过 DBI 模块进行访问。感谢大家的帮助。非常感谢所有回复。
【讨论】:
@Harold 感谢您接受我的回答,但更重要的是,非常感谢您提供了包含实际数字的精彩摘要。【参考方案4】:最好不要在每次运行脚本时都将整个列表拉入内存。使用磁盘数据库将允许您执行此操作。如果由于某种原因,您每次运行时都必须触摸 CSV 文件中的每个条目,我可能会建议将其存储在 RAM 磁盘而不是物理磁盘上。它显然适合内存,我认为通过更改存储它的磁盘格式不会有太大改进。真正加快速度的唯一方法是将其存储在更快的介质上。
【讨论】:
【参考方案5】:如果您只需要访问每个脚本中的部分数据,而不是全部,DBM::Deep 可能是您的最佳选择。
无论您做什么,磁盘/IO 都可能是您最大的瓶颈。也许您可以使用一个数据提供程序,将所有数据保存在一个映射缓存中——使用类似Sys::Mmap::Simple 这样的东西我从来不需要做这种事情,所以我没有太多其他东西可以提供。
【讨论】:
请解释为什么 DBM::Deep 更适合访问部分数据,好吗? DBM::Deep 是一个漂亮的模块:可以想象它在磁盘上存储 Perl 数据结构,而无需像使用 Storable 那样反序列化 整个 DB。话虽如此,如果您需要任何重要部分的数据,这将非常非常慢。它将便利置于性能之上。以上是关于如何从 Perl 快速访问许多大型 CSV 文件中的数据?的主要内容,如果未能解决你的问题,请参考以下文章
我们有许多 EBCDIC 格式的大型机文件,Python 中有没有办法将大型机文件解析或转换为 csv 文件或文本文件?
在 R 中访问大型 csv:read.table.ffdf 变慢