从netCDF更快地阅读时间序列?

Dav*_*uer 13 io performance r hdf5 netcdf

我有一些大的netCDF文件,包含0.5度分辨率的地球的6小时数据.

每年有360个纬度点,720个经度点和1420个时间点.我有两个年度文件(12 GB ea)和一个110年数据(1.3 TB)的文件存储为netCDF-4(这里是1901年数据的一个例子,1901.nc,它的使用政策,以及原始,公共我开始使用的文件).

根据我的理解,从一个netCDF文件中读取它应该更快,而不是循环遍历由year和最初提供的变量分隔的文件集.

我想提取每个网格点的时间序列,例如从特定纬度和经度开始10或30年.但是,我发现这很慢.例如,从点位置读取10个值需要0.01秒,尽管我可以在0.002秒内从单个时间点读取10000个值的全局切片(维度的顺序是lat,lon,时间):

## a time series of 10 points from one location:
library(ncdf4)
met.nc <- nc_open("1901.nc")
system.time(a <- ncvar_get(met.nc, "lwdown", start = c(100,100,1), 
                                             count = c(1,1,10)))
   user  system elapsed 
  0.001   0.000   0.090 

## close down session

## a global slice of 10k points from one time
library(ncdf4)
system.time(met.nc <- nc_open("1901.nc"))
system.time(a <- ncvar_get(met.nc, "lwdown", start = c(100,100,1), 
                                             count = c(100,100,1)))
   user  system elapsed 
  0.002   0.000   0.002 
Run Code Online (Sandbox Code Playgroud)

我怀疑这些文件是为了优化空间层的读取而编写的,因为a)变量的顺序是lat,lon,time,b)这将是生成这些文件的气候模型的逻辑顺序和c)因为全局范围是最常见的可视化.

我试图重新排序变量,以便时间先到时:

ncpdq -a time,lon,lat 1901.nc 1901_time.nc
Run Code Online (Sandbox Code Playgroud)

(ncpdq来自NCO(netCDF运营商)软件)

> library(ncdf4)

## first with the original data set:
> system.time(met.nc <- nc_open("test/1901.nc"))
   user  system elapsed 
  0.024   0.045  22.334 
> system.time(a <- ncvar_get(met.nc, "lwdown", start = c(100,100,1), count = c(1, 1, 1000))
+ )
   user  system elapsed 
  0.005   0.027  14.958 

## now with the rearranged dimensions:
> system.time(met_time.nc <- nc_open("test/1901_time.nc"))
   user  system elapsed 
  0.025   0.041  16.704 
> system.time(a <- ncvar_get(met_time.nc, "lwdown", start = c(100,100,1), count = c(1, 1, 1000)))
   user  system elapsed 
  0.001   0.019   9.660 
Run Code Online (Sandbox Code Playgroud)

如何在一个点上优化读取时间序列而不是在一个时间点的大面积层?例如,如果文件的编写方式不同,例如time,lat,lon会更快吗?是否有一种"简单"的方法来转换netCDF-4文件中的维度顺序?

更新

(@mdsumner要求的基准)

library(rbenchmark)
library(ncdf4)
nc <- nc_open("1901.nc")
benchmark(timeseries = ncvar_get(nc, "lwdown", 
                                 start = c(1, 1, 50), 
                                 count = c(10, 10, 100)), 
          spacechunk = ncvar_get(nc, "lwdown", 
                                  start = c(1, 1, 50), 
                                  count = c(100, 100, 1)),           
          replications = 1000)

        test replications elapsed relative user.self sys.self user.child
2 spacechunk         1000   0.909    1.000     0.843    0.066          0
1 timeseries         1000   2.211    2.432     1.103    1.105          0
  sys.child
2         0
1         0
Run Code Online (Sandbox Code Playgroud)

更新2:

我已经开始在这里开发解决方案了.这些碎片在github.com/ebimodeling/model-drivers/tree/master/met/cruncep中的一组脚本中

脚本仍然需要一些工作和组织 - 并非所有脚本都有用.但阅读速度很快.与上述结果不完全相同,但在一天结束时,我可以立即从1.3TB文件(0.5度分辨率,2.5秒)中读取100年,6小时的时间序列:

system.time(ts <- ncvar_get(met.nc, "lwdown", start = c(50, 1, 1), count = c(160000, 1, 1)))
   user  system elapsed 
  0.004   0.000   0.004 
Run Code Online (Sandbox Code Playgroud)

(注意:维度的顺序已更改,如下所述:如何在使用ncdf4 :: ncvar_get时指定维度顺序?)

War*_* F. 10

我认为这个问题的答案不会重新排序数据,因为它会分块数据.有关分块netCDF文件含义的完整讨论,请参阅Unidata的主要netCDF开发人员Russ Rew的以下博客文章:

结果是,采用不同的分块策略可以大大提高访问速度,选择正确的策略并非易事.

在较小的样本数据集上,sst.wkmean.1990-present.nc我在使用您的基准测试命令时看到了以下结果:

1)Unchunked:

## test replications elapsed relative user.self sys.self user.child sys.child
## 2 spacechunk         1000   0.841    1.000     0.812    0.029          0         0
## 1 timeseries         1000   1.325    1.576     0.944    0.381          0         0
Run Code Online (Sandbox Code Playgroud)

2)Naively Chunked:

## test replications elapsed relative user.self sys.self user.child sys.child
## 2 spacechunk         1000   0.788    1.000     0.788    0.000          0         0
## 1 timeseries         1000   0.814    1.033     0.814    0.001          0         0
Run Code Online (Sandbox Code Playgroud)

天真的分块只是在黑暗中拍摄的; 我nccopy这样使用了这个工具:

$ nccopy -c"lat/100,lon/100,time/100,nbnds /"sst.wkmean.1990-present.nc chunked.nc

nccopy可以在此处找到该实用程序的Unidata文档.

我希望我可以推荐一种特定的策略来分块你的数据集,但它高度依赖于数据.希望上面链接的文章能够让您深入了解如何将数据分块以实现您正在寻找的结果!

更新

Marcos Hermida的以下博客文章展示了在阅读特定netCDF文件的时间序列时,不同的分块策略如何影响速度.这应该只是用作跳跃点.

关于通过nccopy明显悬挂重新组合; 该问题似乎与4MB的默认块高速缓存大小有关.通过将其增加到4GB(或更多),您可以将大型文件的复制时间从24小时缩短到11分钟以下!

有一点我不确定; 在第一个链接中,讨论是关于chunk cache,但是传递给nccopy的参数-m,指定了复制缓冲区中的字节数.-mnccopy 的参数控制块缓存的大小.