前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NCL专辑 | 提高NCL代码的运行效率的各种方法

NCL专辑 | 提高NCL代码的运行效率的各种方法

作者头像
郭好奇同学
发布2020-12-22 14:28:57
4.1K0
发布2020-12-22 14:28:57
举报
文章被收录于专栏:好奇心Log好奇心Log

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”

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 好奇心Log 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档