标签:fortran和c++混合编程、fortran程序调用c++代码、c++程序内存不断增加
手头上有一个c++编写的某问题计算程序,然后需要利用该程序进行优化计算。于是想起8年前,用fortran写的优化计算程序。说实话,这些年下来基本已经没有碰优化计算了,没想到还有要用到的时候。当时用fortran实现了遗传(演化)算法nsga2,spea2,moea,emoea算法,以及自己创新的agea算法,我想完全可以用起来,这样不用重新再写了,于是赶紧行动起来。
首先测试一下还能不能跑fortran优化程序,初步用gfortran编译试了下,一堆错误。我仔细看了下还是跟编译器相关。因为当时写fortran程序用的是intel的编译器,其优化后的运行速度更快,但离开数值计算领域多年,计算机上都没有当年数值计算需要的环境了,只剩下mingw中的gcc套装,应付可能的需要,于是根据提示一一做修改,有些输出相关的就直接注释了,有些重要的输出,那么只能用gfortran能认的方式重写,去掉一些pause语句,运行一下测试算例,没问题了。这样就有了基础。
优化计算中,评价一个样本的是其适应度计算,优化不同的问题,核心是要修改适应度计算函数。因此这里的需求就是要将适应度计算,用之前解决问题的c++代码程序来实现。于是这就涉及到了c++和fortran的混合编程。这方面以前也基本没有涉及,所以还是要上网查一下。找到一篇文章(见附后的参考文献),介绍的很详细,仔细看了一下,看样子没有问题,于是开始行动。
首先还是用简单案例来先测试一下基本框架,否则运用不成熟的技术,可能遇到一堆的问题无法处置。
首先写一个fortran程序
program test
implicit none
integer::n,i
real*8::array(3)
interface
function plusfun(a, b) bind ( c )
use iso_c_binding
integer (c_int), VALUE::a
integer (c_int), VALUE::b
integer (c_int)::plusfun
end function
end interface
interface
function multiplyfun(a, b ) bind ( c )
use iso_c_binding
integer (c_int), VALUE::a
real (c_double)::b(*)
real (c_double)::multiplyfun
end function
end interface
n=10
do i=1,n
print *,'i=',i
end do
print *,plusfun(1,2)
n=3
array=(/2.0,3.0,4.0/)
print *,multiplyfun(n,array)
end program
其中首先测试了一下加法运算程序,后面的乘法运算程序主要是测试数组信息传递的问题。
其中的接口interface也是参考(文献1)做的。
然后是c++程序
//测试混合编程
#include
#include
#include "mixcppfor.hpp"
using namespace std; //
int plusfun(int a, int b)
{
int c=a+b;
return c;
}
double multiplyfun(int a, double b[])
{
double c=1.0;
for (int i=0;i
这个c++程序先用一个main主程序进行测试,函数没问题了,就删除。其中还有一个#include "mixcppfor.hpp"
头文件内容为:
extern "C"
{
int plusfun(int a, int b);
double multiplyfun(int a, double b[]);
}
因为着急用也没有查该文件的具体原理,但大概能猜到这是给外部调用提供接口的。
两个程序开始尝试编译,从文献1中看,它的makefile中编译命令似乎是pgf编译器的。而我的只有g++和gfortran,但我想应该也没啥问题,照猫画虎,尝试编译试试。
使用如下命令:
g++ -g -c -std=c++11 testmixc.cpp -o testmixc.o
gfortran -g -c testmix.f90 -o testmix.o
gfortran -lstdc++ -o testmix.exe testmix.o testmixc.o
似乎第一步,第二步都没有问题,但第三步链接时存在问题。不会吧,难道我也需要去使用pgf编译器么,装起来可是很麻烦了,因为多年没有用这些东西了。我想还是再查一下有没有相似问题看看吧,
各种查找,似乎都没有非常准确的,似乎没有办法了。最后还是找到一个帖子是水木社区里头的,它和我的需求很相象,问题也类似,在解决方案中提到一句“链接时加了…libstdc++.so.6果然就可以了”。我想我也可以试试的。
于是查找libstdc++.so.6
电脑中有一堆,还是用mingw下面的吧,我的是win7系统,于是就用F:\mingw\lib\gcc\mingw32\4.8.1\libstdc++.a
吧。
于是使用如下命令编译:
g++ -g -c -std=c++11 testmixc.cpp -o testmixc.o
gfortran -g -c testmix.f90 -o testmix.o
gfortran -lstdc++ -o testmix.exe testmix.o testmixc.o F:\mingw\lib\gcc\mingw32\4.8.1\libstdc++.a
果然成功了,那么现在所有的障碍都解决了。下面开始开始混合编程。
为了尽可能的减少问题出现,我还是先用简单示例测试,就是用原来的fortran中的优化问题的适应度函数来测试,原来在fortran中写的自然没有问题,那么把这个函数用c++来写,然后进行混合,是否会有问题,如果没有问题,那么一个新的问题的适应度函数直接用c++写就肯定没有问题了。
就测试一个三变量单目标问题。
本来一句fortran代码:
poptmp(i)%fitness(1)=(xtmp(1)-1.d0)**2+(xtmp(2)-2.d0)**2+(xtmp(3)-3.d0)**2
需要改成:
poptmp(i)%fitness(1)=funcquestiona(nvars,xtmp)
其中funcquestiona函数在c++中写,在fortran中还需要一个接口:
interface
function funcquestiona(a, b ) bind ( c )
use iso_c_binding
integer (c_int), VALUE::a
real (c_double)::b(*)
real (c_double)::funcquestiona
end function
end interface
而该函数本身为:
//第一个优化问题的函数
double funcquestiona(int a, double xtmp[])
{
double res=0.0;
for(int i=0;i
完成后测试,优化问题正常实现,没有问题,这样就做好了所有的准备,只要把之前的c++中的问题表示出来就好了,持续进行代码实现,最后编译运行也没有问题。
但运行中还是出现了问题,运行到一定过程后出现badlocate的问题,估计是内存的问题,于是再跑一看,发现程序占用内存不断增加,很快就过大了。
我想这可能是c++程序的问题,记得以前也有人提过这种现象,说实话离开数值计算领域后就很少编程了,c++也是偶尔用用不太熟悉了,偶尔用qt和python自娱自乐一下,这么专业的问题还是要看一下书的。于是找一下c++ primer,看了下还是内存delete的问题,类的实例delete掉是不够的,动态数组也要delete掉,这我也做了。但命令写错了,数组delete应是delete [],但修改后还是有内存增长,只是小一些了。于是查网络发现,多维数组,还是要多维处理。不是简单一个delete []就够了,比如一个二维数组,每一行都是要delete []的,完成后再对数组本身作一次delete []。修改完成后,再查看内存,发现程序没有问题了。
这样完成后,交给计算机就可以了,跑上,然后扣手机吧。
混合编程是个好东西,能把多年未用的好东西收拾起来,真的是很不错。通过这次实践,虽然没有说精通,但也是掌握了基本操作,以后有需要完全可以再深入一些。本文就是作为这一次实践的记录吧,也许后面又不用,久了也就忘了。