本来想一口气编辑一篇长文,排版意识较弱,一段一段地来吧。
一,搭建Fortran77编译环境(前文有综述,这里给出最省时方案)
(a)Win10下载并安装MinGW
假设安装在了D:\MinGW\,那么检查D:\MinGW\bin中是否存在gfortran.exe make.exe gdb.exe gcc.exe等。如果不存在,则从类似mingw32-make.exe 复制一份并改名为 make.exe等。
(b)将D:\MinGW\bin加入环境变量path中
然后打开cmd控制台,执行gfortran --version,看看有没有正常反馈,类似
GNU Fortran (MinGW.org GCC-6.3.0-1) 6.3.0
Copyright (C) ... ...
(c)类似执行 gcc --version 、gdb --version 、make --version等等
(d)如果以上可执行文件不存在,则需要打开桌面的MinGW Intaller 选择并安装。
二,编写编译运行blas示例程序
(a)编辑、编译一个fortran77的helloworld程序
新建一个空文件夹,新建一个新文件:helloWorld.f,打开文件,写入如下内容,每行开头要先tab键。
输入如下代码:
PROGRAM EX_SDOT
REAL VX(1:5),VY(1:5), RESULT_dot
INTEGER I
DO 10, I=1,5,1
VX(I)=I
VY(I)=I*10.0
10 CONTINUE
PRINT *, VX
PRINT *, VY
END
在cmd 控制台里面进行编译:
>gfortran helloWorld.f -o helloWorld
>helloWorld.exe
效果如图
(b)下载blas-3.8.0.tgz 或直接浏览页面下载:http://www.netlib.org/blas/blas-3.8.0.tgz
(c)解压到一个空闲文件夹中,在cmd进入到blas源码的目录,其中有一个makefile文件。
执行make,会生成一个blas的二进制打包文件:blas_LINUX.a
>make
blas的makefile很简洁,基本就是把每个fortran源文件编译成.o文件后,用ar工具打包起来成为blas_LINUX.a
(d)将上一步的.a文件拷贝到helloWorld.f所在的源文件夹中(可省略编译时指定库文件目录)。
并且重新编辑helloworld.f文件,加入对blas函数SDOT的调用,计算两个向量的点积:
PROGRAM EX_SDOT
REAL VX(1:5),VY(1:5), RESULT_dot
INTEGER I
DO 10, I=1,5,1
VX(I)=I
VY(I)=I*10.0
10 CONTINUE
PRINT *, 'VX = ',VX
PRINT *, 'VY = ',VY
PRINT *, ''
PRINT *, 'VX DOT VY = ', SDOT(5,VX,1,VY,1)
END
执行效果如图:
其中,SDOT函数的定义在blas源文件 sdot.f 中,原型如下:
REAL FUNCTION SDOT(N,SX,INCX,SY,INCY)
其中:
N 是说明两个相乘向量的元素个数,本例中每个向量VX和VY分别是5个元素;
SX 是第一个向量的形参变量名,本例中实参为VX;
INCX 是下一个参与点积运算的元素的步长,本例中元素是一个挨着一个元素,所以INCX为1;
SY 是第二个向量的形参变量名,实参为VY;
INCY 是指SY中下一个元素的步长,这里也是1.
三,对SDOT源代码的分析
(a)从源文件sdot.f中,删掉关键注释信息,只保留程序代码,如下:
* =====================================================================
REAL FUNCTION SDOT(N,SX,INCX,SY,INCY)
INTEGER INCX,INCY,N
REAL SX(*),SY(*)
REAL STEMP
INTEGER I,IX,IY,M,MP1
INTRINSIC MOD
STEMP = 0.0e0
SDOT = 0.0e0
IF (N.LE.0) RETURN
IF (INCX.EQ.1 .AND. INCY.EQ.1) THEN
* code for both increments equal to 1
* clean-up loop
M = MOD(N,5)
IF (M.NE.0) THEN
DO I = 1,M
STEMP = STEMP + SX(I)*SY(I)
END DO
IF (N.LT.5) THEN
SDOT=STEMP
RETURN
END IF
END IF
MP1 = M + 1
DO I = MP1,N,5
STEMP = STEMP + SX(I)*SY(I) + SX(I+1)*SY(I+1) +
$ SX(I+2)*SY(I+2) + SX(I+3)*SY(I+3) + SX(I+4)*SY(I+4)
END DO
ELSE
*
* code for unequal increments or equal increments
* not equal to 1
*
IX = 1
IY = 1
IF (INCX.LT.0) IX = (-N+1)*INCX + 1
IF (INCY.LT.0) IY = (-N+1)*INCY + 1
DO I = 1,N
STEMP = STEMP + SX(IX)*SY(IY)
IX = IX + INCX
IY = IY + INCY
END DO
END IF
SDOT = STEMP
RETURN
END
由源代码可以看出来,在元素步长都为1的情况下,SDOT函数计算点积,是按五个五个一组进行累加乘积的。这应该是针对cpu计算能力和内存访问的的一种优化。
而对于元素步长不等于1的情况,则是逐个相乘累加的,针对cpu没办法优化。
四,DDOT 和 DCOPY示例
DDOT和DCOPY参数的含义,跟sdot中的参数一样,三者的源代码实现结构也基本相同,只是元素都是DOUBLE PRICISION型的。DDOT也是两个向量点积;DCOPY则是将第二个向量中的元素拷贝到第一个向量中去。
PROGRAM EX_DDOT_DCOPY
DOUBLE PRECISION VX(1:5),VY(1:5), COPY_FROM_VY(1:5)
INTEGER I
DO 10, I=1,5,1
VX(I)=I
VY(I)=I*10.0
10 CONTINUE
PRINT *, 'VX = ',VX
PRINT *, 'VY = ',VY
PRINT *, ''
PRINT *, 'VX DOT VY = ', DDOT(5,VX,1,VY,1)
VX = 2.0*VX
PRINT *, 'VX DOT VY = ', DDOT(5,VX,1,VY,1)
CALL DCOPY(5,VY,1,COPY_FROM_VY,1)
PRINT *, 'VY = ',VY
PRINT *, 'COPY_FROM_VY = ',COPY_FROM_VY
END