如何优化ERA5-Land数据集的xarray/dask处理,减少耗时与内存占用
处理ERA5-Land海量风场数据的性能优化问题
我正在用ERA5-Land数据集计算风相关变量,能得到结果但处理海量数据效率极低。数据存储在可访问的超算平台,可选配置包括4CPU/18GB、7CPU/32GB、14CPU/63GB、28CPU/126GB。数据按月度文件存储,我按年读取处理,共涉及74年数据。
当前代码实现
导入与初始化
# Imports import geopandas as gpd from osgeo import gdal import numpy as np import matplotlib.pyplot as plt import glob import pickle import xarray as xr import time # setting up my workers (e.g. 7) from dask.distributed import Client client = Client(n_workers=7) client # Declaring variables directory = 'PATH_TO_LOCAL_DIR' years = np.arange(1950,2023+1) lon0 = 129.7 lat0 = - 27.9 lonbounds = [113.0,145.0] latbounds = [-36.0,-16.0] d = 300e-6 z = 10 g = 9.8 z0 = 1e-2 rho_s = 2650 rho_f = 1.2 kappa = 0.4 phi = 0.6 fluxconstant = 5 thresholdconstant = 0.082 usimths = np.linspace(0.01,0.8,80,dtype=np.float32) r = 6371.229*1e+3 s2yr = 1/60/60/24/365 uabinwidth = 3*np.pi/180 uabine = np.arange(-180*np.pi/180,(180+1e-4)*np.pi/180,uabinwidth) uabinm = uabine[1:]-uabinwidth/2
数据读取
# Opening the data. Currently I just grab the first year worth of data era5 = xr.open_mfdataset(glob.glob('u10_era5-land_oper_sfc_%d*.nc'%(years[i],years[i]))) era5 = era5.reindex(latitude=list(reversed(era5.latitude))) era5 = era5.sel(longitude=slice(lonbounds[0],lonbounds[-1]),latitude=slice(latbounds[0],latbounds[-1])) era5v = xr.open_mfdataset(glob.glob('v10_era5-land_oper_sfc_%d*.nc'%(years[i],years[i]))) era5v = era5v.reindex(latitude=list(reversed(era5v.latitude))) era5v = era5v.sel(longitude=slice(lonbounds[0],lonbounds[-1]),latitude=slice(latbounds[0],latbounds[-1])) era5 = era5.merge(era5v)
读取后数据集结构:
<xarray.Dataset> Dimensions: (longitude: 321, latitude: 201, time: 8759) Coordinates: * longitude (longitude) float32 113.0 113.1 113.2 113.3 ... 144.8 144.9 145.0 * latitude (latitude) float32 -36.0 -35.9 -35.8 -35.7 ... -16.2 -16.1 -16.0 * time (time) datetime64[ns] 1950-01-01T01:00:00 ... 1950-12-31T23:00:00 Data variables: u10 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> v10 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray>
风场变量计算
# Calculation era5 = era5.assign(ua_from=np.arctan2(-era5.v10,-era5.u10))#.compute() <--- not sure if compute in between is a good idea? for j in range(len(usimths)): era5 = era5.assign(temp=(((era5.u10**2+era5.v10**2)*(kappa/np.log(z/z0))**2-usimths[j]**2)*fluxconstant*usimths[j]/g*rho_f/rho_s).where(((era5.u10**2+era5.v10**2)**0.5*(kappa/np.log(z/z0)))>usimths[j],0))#.compute() era5['qm_{}'.format(str(round(usimths[j],2)))] = era5.temp era5 = era5.drop_vars('temp')
计算后数据集结构:
<xarray.Dataset> Dimensions: (longitude: 321, latitude: 201, time: 8759) Coordinates: * longitude (longitude) float32 113.0 113.1 113.2 113.3 ... 144.8 144.9 145.0 * latitude (latitude) float32 -36.0 -35.9 -35.8 -35.7 ... -16.2 -16.1 -16.0 * time (time) datetime64[ns] 1950-01-01T01:00:00 ... 1950-12-31T23:00:00 Data variables: (12/83) u10 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> v10 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> ua_from (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> qm_0.01 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> qm_0.02 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> qm_0.03 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> ... ... qm_0.75 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> qm_0.76 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> qm_0.77 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> qm_0.78 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> qm_0.79 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray> qm_0.8 (time, latitude, longitude) float32 dask.array<chunksize=(52, 100, 166), meta=np.ndarray>
风向分箱统计
# this part takes some time and I am still unsure if I should compute or not in between ua_from = era5.ua_from.compute().values #<-- need these for below bin_arrays_mean = [] bin_arrays_sum = [] for ident in range(len(uabine)-1): era5tempid = era5.drop_vars(["u10","v10","ua_from"]) era5tempid = era5tempid.where((ua_from >= uabine[ident]) & (ua_from < uabine[ident+1]) & (era5tempid > 0)) bin_arrays_mean.append(era5tempid.mean(dim="time", skipna=True))#.compute()) bin_arrays_sum.append(era5tempid.sum(dim="time", skipna=True))#.compute()) # merge and rearrange for desired dataset era5ang_mean = xr.concat(bin_arrays_mean,dim="ang_bins").assign_coords({"ang_bins":np.arange(120, dtype=np.uint32)}) era5ang_sum = xr.concat(bin_arrays_sum,dim="ang_bins").assign_coords({"ang_bins":np.arange(120, dtype=np.uint32)}) qm_arrays_mean = [] for var in era5ang_mean: qm_arrays_mean.append(era5ang_mean[var].rename("qm")) qm_arrays_sum = [] for var in era5ang_sum: qm_arrays_sum.append(era5ang_sum[var].rename("qm")) era5_bins_mean = xr.concat(qm_arrays_mean, dim="usimths").assign_coords({"usimths":usimths}) era5_bins_sum = xr.concat(qm_arrays_sum, dim="usimths").assign_coords({"usimths":usimths}) era5_bins_comb = xr.Dataset({"qmmean":era5_bins_mean, "qmsum":era5_bins_sum})
最终数据集结构(单经度示例):
<xarray.Dataset> Dimensions: (latitude: 201, ang_bins: 120, usimths: 80) Coordinates: longitude float32 113.0 * latitude (latitude) float32 -36.0 -35.9 -35.8 -35.7 ... -16.2 -16.1 -16.0 * ang_bins (ang_bins) uint32 0 1 2 3 4 5 6 7 ... 113 114 115 116 117 118 119 * usimths (usimths) float32 0.01 0.02 0.03 0.04 0.05 ... 0.77 0.78 0.79 0.8 Data variables: qmmean (usimths, ang_bins, latitude) float32 nan nan nan ... nan nan nan qmsum (usimths, ang_bins, latitude) float32 0.0 0.0 0.0 ... 0.0 0.0 0.0
最后需要用era5_bins_comb.to_netcdf()保存结果。
核心问题
- 计算效率极低:风向分箱的
for ident in range(len(uabine)-1):循环是耗时核心,用14CPU时单循环约6分钟,74年总耗时预估888小时;尝试嵌套遍历80个变量+120个风向箱的方案,单次嵌套循环20秒,总耗时仍需约53小时。 - Dask任务流优化差:当前Dask的任务调度没有充分发挥算力优势。
.compute()时机困惑:不确定何时调用.compute()能平衡内存占用与计算效率,担心提前计算导致内存溢出,延后计算又拖慢速度。- 存储瓶颈:最终结果保存时面临内存不足或写入耗时过长的问题。
优化需求
- 重构代码,减少冗余循环(尤其是风向分箱的循环),利用向量化计算替代循环。
- 优化Dask配置与任务调度,充分利用超算的多CPU资源。
- 明确
.compute()的合理使用时机,避免内存浪费或计算阻塞。 - 优化结果存储方式,解决内存不足与写入耗时问题。
内容的提问来源于stack exchange,提问作者Dominik N.
相关产品推荐
相关产品推荐

