如何从 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 文件中的数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何快速将大型 CSV 文件导入数据库

如何快速处理大型csv文件?

我们有许多 EBCDIC 格式的大型机文件,Python 中有没有办法将大型机文件解析或转换为 csv 文件或文本文件?

在 R 中访问大型 csv:read.table.ffdf 变慢

如何有效且快速地将大型 (6 Gb) .csv 文件导入 R,而不会导致 R REPL 崩溃?

有效地将许多大型 CSV 文件中的 XYZ 坐标排序到小图块中