NCL作为一门高级编程语言,包含了大量函数库,使得编程语法较为简洁方便,这也导致了在处理较大数据时运行速度的下降(Matlab、Python等也有同样的问题)。虽然如此,但是我们还是可以采取一些方法,提高NCL代码的运行效率。
1、尽量少用循环
NCL在做循环时特别费时间,这就需要我们在设计代码逻辑时尽量减少循环,对于循环,能合并的就合并,还有可以多用一些NCL的隐藏技能。比如:where、reshape、var(::-1)(数组倒置)、var(lat|:,lon|:,time|:)(数组行列变换)、isconfortm(x,y)(判断x,y两个变量是否有相同的shape和size)等命令,用以代替写循环赋值。
还有一种写法var=var>0,表示把var中小于0的数值都赋值为0。
还有一些内循环,可以用:来表示,比如:
do iyr = 0, nyr-1
do ilev = 0, nlev-1
do ilat = 0, nlat-1
do ilon = 0, nlon-1
var(iyr,ilev,ilat,ilon) = data(iyr+2,ilev,ilat,ilon)
end do
end do
end do
end do
可以改写为:
do iyr = 0, nyr-1
var(iyr,:,:,:) = data(iyr+2,:,:,:)
end do
2、读取变量的时候,用多少读多少,不要全部读取
NCL在处理较大数据量的时候,特别吃内存。这就需要对数据进行严格设计,只取需要的部分来进行处理。
当数据的空间分辨率比较高,而且变量的空间连续性也比较好,比如温度或者气压,就可以采用跳着读的方法,而不用把区域内的全部数据都读进去。比如在经纬度上,每两个格点读取一个温度值:
T = f->T(:,::2,::2)
3、对于不再用到的变量,立即delete
前面说到,NCL的变量特别吃内存,因此当后面不再使用到改变量时,要将其delete(删除)。
delete一个变量:delete(var)
delete多个变量:delete([/var1,var2,var3/])
4、用shell脚本写循环,再在循环中调用NCL脚本
shell脚本的运行效率比NCL要高的多,可以利用shell脚本来写循环。尤其是需要循环读取多个很大的文件时,即使在NCL脚本中删除了这些文件的变量名,但是NCL并不会释放这些文件,它们还会占用大量的内存,造成运算效率低下。
shell脚本的循环写法如下:
export datadirectory='/azong/data/' #文件路径
declare -i numOFfile=20 #文件总数,整形
#开始循环
for ((i=0;i<$numOFfile;i=i+1))
do
export fileNO=${i} #循环一次更新声明一次文件编号,由NCL中的getenv函数读取
ncl test.ncl
done
假设这里是要对每个文件中的变量var算平均值,那么test.ncl脚本如下:
begin
filedirectory = getenv("datadirectory") ;调用文件目录
fs = systemfunc("ls "+filedirectory+"*") ;列出文件
i = stringtoint(getenv("fileNO")) ;读文件号,转成整型
;读文件内容
f := addfile(fs(i),"r") ;对于f,赋值后并不保存
var = f->var ;赋值
data = avg(var) ;计算平均
print(data)
end
NCL脚本中的getenv是NCL自带函数,用于读取从shell脚本中声明的变量。比如该脚本中就是用getenv从shell脚本中读取了文件目录和文件号。
5、利用cdo进行预处理
对于grads格式的dat和ctl文件,可以先行利用cdo命令将其转为nc文件,再由NCL来处理,这样可以规避NCL读取dat文件时效率低下的问题。cdo命令如下:
cdo -f nc import_binary file.ctl ofile.nc
6、尽量减少,最好不要print和printVarSummary
程序中间print和printVarSummary,会造成计算的中断,极大地降低运算效率,所以调试的时候可以用print来监控程序运行情况,正式运行时就不要print了。
7、用NCL调用Fortran程序
用NCL调用Fortran程序,需要使用wrapi脚本来将Fortran程序编译成共享对象,从而使NCL可以用external来调用该共享对象。
以f90为例,具体的操作过程如下:
对于一个名为"cquad.f90"的程序:
subroutine cquad(a,b,c,nq,x,quad)
implicit none
integer, intent(in) ::nq
real, intent(in) ::a,b,c,x(nq)
real, intent(out) ::quad(nq)
integer ::i
quad = a*x**2+b*x+c
return
end subroutine cquad
第一步,创建一个名为“cquad90.stub"的文本:
C NCLFORTSTART
subroutine cquad(a,b,c,nq,x,quad)
real a,b,c
integer nq
dimension x(nq),quad(nq)
C NCLEND
第二步,生成共享对象。输入命令
WRAPIT cquad90.stub cquad.f90
从而生成共享对象“cquad90.so”
第三步,使用NCL调用共享对象
在ncl脚本里的第一行写上:external EX01 “./cquad90.so”