1介绍

有场景最直观的使用方法rhdf5或HDF5不会是最有效的。这可能是由于不熟悉的瓶颈在处理数据磁盘上而不是在内存中,或在HDF5库本身或特质rhdf5包中。这个描述是为了呈现一组提示为了规避一些常见的陷阱。

2阅读的数据子集

关于HDF5文件格式的很酷的特性之一是能够读数据的子集(一定)无需阅读整个文件,让这些操作的内存使用和执行时间降到最低。然而这并不总是像人们可能希望性能。

为了证明我们将创建一些示例数据。这需要一个矩阵的形式与100行和20000列,每列的内容在哪里的索引列即列10包含值重复,列20包含20多次等等。这只是我们可以很容易地检查我们提取正确的列。然后我们写这个矩阵HDF5文件,调用数据集“计数”。1你可能会看到一个警告关于分块,稍后我们将涉及

m1 < -矩阵(代表(1:20000,每个= 100),ncol = 20000, byrow = FALSE) ex_file < - tempfile (fileext =“.h5”) h5write (m1、文件= ex_file name =“计数”,级别= 6)
# #您创建了一个大型数据集压缩和分块。# #块大小等于数据集的维度。# #如果你想读数据集的子集,您应该testsmaller块大小来提高阅读时间。

2.1使用指数论点

现在我们将使用指数参数有选择性地提取第一个10000列,这需要多久的时间。

系统。时间(res1 < - h5read(文件= ex_file name =“计数”,指数=列表(NULL, 1:1)))
用户系统运行# # # # 0.019 0.024 0.044

下一个,而不是选择连续10000列我们会要求其他列。这应该还是返回相同的,因为我们的数据集的数据量不是分块涉及从磁盘读取相同的体积。

指数< -列表(NULL, seq(= 1 = 20000 = 2))系统。时间(它< - h5read(文件= ex_file name =“计数”,指数=指数))
用户系统运行# # # # 0.142 0.012 0.154

我们可以看到这是大大低于前面的示例。这是因为创建工会hyperslabs目前在HDF5非常缓慢(见联盟连任hyperslabs速度非常慢另一份报告的这种行为),性能损失相对于工会的数量呈几何倍数增长。当我们使用指数论点rhdf5为每个独立设置的值创建一个hyperslab我们要提取,然后合并它们。在我们的第一个例子这只需要创建一个100\ \(\倍)10000 hyperslab,作为在第二种情况下,我们需要10000 hyperslabs尺寸100\ \(\倍)1和9999合并操作。

2.2使用hyperslab选择

如果有一个地区想要访问规律,那么很可能你也可以申请使用HDF5 hyperslab选择方法^[定义hyperslab的参数选择开始,,,&并不是特别直观的如果你习惯于R的索引选择方法。更多的例子可以发现在讨论如何指定它们www.hdfgroup.org。下面的代码定义了参数选择每隔一列,在我们前一个示例一样。

开始< - c(1, 1)步< - c(1, 2)块< - c(100 1)计数< - c(10000)系统。时间(res3 < - h5read(文件= ex_file name =“计数”,开始=开始,步=跨步,块=块,数= count))
用户系统运行# # # # 0.156 0.000 0.156
res3相同的(它)
# # [1]

这显然是明显比使用更快指数参数在这个例子中,并调用相同的()证实了我们返回相同的数据。

rhdf5足够复杂的连续组合列成一个单一的电话,所以选择完全不相交的替代列代表一个糟糕的情况。影响会少得多,例如,我们要提取列1 - 5000和6001 - 11000。在这种情况下它可能不是明显有利于摆脱使用指数参数,但对于少连续选择利用hyperslab选择参数可以是非常有益的。

2.3不规则的选择

如果没有一个规律列你想选择,选项是什么?也许我们可以尝试最明显的是跳过的使用指数或hyperslab参数和使用10000个独立的读操作。下面我们选择一个随机选择的列,然后应用功能f1 ()依次对每个。

set.seed列(1234)< -样本(x = seq_len(20000),大小= 10000,取代= FALSE) % > % () f1 < -函数排序(关口,名称){h5read(文件= ex_file名称=名字,指数=列表(NULL,关口))}系统。时间(res4 < vapp (= f1 X =列,有趣,有趣。值=整数(长度= 100),name = '重要'))
用户系统运行# # # # 232.572 0.772 233.347

这显然是一个糟糕的主意,需要年龄!,供参考使用指数与这组参数列需要1.404秒。这个可怜的性能是由两件事情:

  1. 我们的数据集没有应用分块时创建。这意味着每个访问整个数据集从磁盘读取,我们最后做10000次。
  2. rhdf5做了很多的验证对象内部传递。在调用h5read ()HDF5标识符创建文件、数据集文件dataspace,和内存dataspace,每个检查的有效性。当只有一个调用此开销可以忽略不计h5read (),但成为重要当我们打10000个电话。

没有更多的你可以做如果没有分块数据集,并使用指数参数是合理的。然而在这种格式存储数据失败HDF5的关键工具,即快速随机访问。因此它可能很少遇到数据不进行分块。记住这一点,我们将创建一个新的数据集在我们的文件中,基于相同的矩阵但这次分裂成100\ \(\倍)100块。

h5createDataset(文件= ex_file数据集=“counts_chunked dim =暗(m1),存储。模式=“整数”块= c(100100),水平= 6)h5write (obj = m1、文件= ex_file name = " counts_chunked ")

如果我们重新运行相同的代码,但是读分块数据集,我们了解的多少时间浪费exctracted整个数据集。

系统。时间(res5 < vapp (= f1 X =列,有趣,有趣。值=整数(长度= 100),name = ' counts_chunked '))
用户系统运行# # # # 37.242 0.420 37.662

这仍然是相当缓慢的,剩下的时间是花费在与多个调用相关的开销h5read ()。减少这些函数f2 ()2这不是历史上最伟大的功能,诸如文件名称是硬编码的,但是它说明了技术。下面定义分裂列的列表我们想返回到集分组的参数block_size。在默认情况下这意味着1和100之间的任何列将被放置在一起,那么任何之间的101年和200年,等我们拉普兰人我们之前f1 ()函数在这些团体。这里的效果是减少调用的数量h5read ()的数量,同时保持hyperslab工会下降没有太多列在任何一个电话。

f2 < -函数(block_size = 100) {cols_grouped < -分裂(列,(栏1)% / % block_size) res < -拉普(cols_grouped、f1、名称= counts_chunked) % > %。调用(“cbind”。)} system.time (f2 ())
用户系统运行# # # # 1.792 0.004 1.797

我们可以看到这有一个显著的影响,尽管它仍然是一个数量级低于当我们处理定期间隔的子集。这里的效率取决于许多因素,包括数据集的大小块的稀疏列索引,和你不同block_size参数将产生不同的表演。下面的图显示了计时实现提供一个选择的值block_size。这表明最优参数,在这种情况下可能是一个1000年的块大小,花了1.01秒,明显高于当路过的所有列指数参数在一个电话。

2.4总结

有效地提取任意HDF5数据集的子集rhdf5是一个平衡的数量hyperslab工会,调用的数量h5read ()的次数,一块是阅读。(主要是)使用连续的子集指数论点是足够了,而对于定期间隔但不相交的子集hyperslab选择参数提供了一种有效的,如果稍微复杂,选择。否则寻找最优策略可能涉及一些实验以类似的方式,我们已经看到。

会话信息

# # R版本4.2.0 RC (2022-04-19 r82224) # #平台:x86_64-pc-linux-gnu(64位)# #下运行:Ubuntu 20.04.4 LTS # # # #矩阵产品:默认# #布拉斯特区:/home/biocbuild/bbs - 3.15 - bioc / R / lib / libRblas。所以# # LAPACK: /home/biocbuild/bbs - 3.15 - bioc / R / lib / libRlapack。# # # #语言环境:# # [1]LC_CTYPE = en_US。utf - 8 LC_NUMERIC = C # #[3]而= en_GB LC_COLLATE = C # # [5] LC_MONETARY = en_US。utf - 8 LC_MESSAGES = en_US。utf - 8 # # [7] LC_PAPER = en_US。utf - 8 LC_NAME = C # # [9] LC_ADDRESS C = C LC_TELEPHONE = # # [11] LC_MEASUREMENT = en_US。utf - 8 LC_IDENTIFICATION = C附加基本包:# # # # # #[1]统计图形grDevices跑龙套数据集方法基础# # # #其他附加包:# # [1]ggplot2_3.3.5 dplyr_1.0.8 rhdf5_2.40.0 BiocStyle_2.24.0 # # # #通过加载一个名称空间(而不是附加):# # [1]Rcpp_1.0.8.3 highr_0.9 bslib_0.3.1 # # [4] compiler_4.2.0 pillar_1.7.0 BiocManager_1.30.17 # # [7] jquerylib_0.1.4 rhdf5filters_1.8.0 tools_4.2.0 # # [10] digest_0.6.29 gtable_0.3.0 jsonlite_1.8.0 # # [13] evaluate_0.15 lifecycle_1.0.1 tibble_3.1.6 # # [16] pkgconfig_2.0.3 rlang_1.0.2 cli_3.3.0 # # [19] DBI_1.1.2 magick_2.7.3 microbenchmark_1.4.9 # # [22] yaml_2.3.5 xfun_0.30 fastmap_1.1.0 # # [25] withr_2.5.0 stringr_1.4.0 knitr_1.38 # # [28] generics_0.1.2 vctrs_0.4.1 sass_0.4.1 # # [31] grid_4.2.0 tidyselect_1.1.2 glue_1.6.2 # # [34] R6_2.5.1 fansi_1.0.3 rmarkdown_2.14 # # [37] bookdown_0.26 farver_2.1.0 Rhdf5lib_1.18.0 # # [40] purrr_0.3.4 magrittr_2.0.3 codetools_0.2-18 # # [43] scales_1.2.0 htmltools_0.5.2 ellipsis_0.3.2 # # [46] assertthat_0.2.1 colorspace_2.0-3 labeling_0.4.2 # # [49] utf8_1.2.2 stringi_1.7.6 munsell_0.5.0 # # [52] crayon_1.5.1