Fortran软件使用KUPL接口
发表于 2025/12/23
0
Fortran软件如何使用KUPL接口
1 背景介绍
鲲鹏统一并行加速库(KUPL)是基于鲲鹏处理器深度优化的并行加速库,包括多线程编程,数据管理,矩阵编程三类基础与扩展功能,结合鲲鹏处理器硬件特性,提供优化的调度与同步算法、异步数据搬运等差异化能力,从高性能,高易用角度为鲲鹏平台应用加速提供助力。
详情链接:https://www.hikunpeng.com/developer/hpc/kupl
当前KUPL所有接口由C/C++、汇编语言实现,但在高性能计算领域,有许多软件是由Fortran语言编写的。
本文展示在Fortran软件中如何调用KUPL来进行优化。
2 基本流程
iso_c_binding 是 Fortran 2003 标准引入的一个内置模块,用于实现 Fortran 与 C 语言之间的互操作性。通过该模块,Fortran 程序可以安全、可移植地调用 C 函数,或被 C 程序调用。我们将使用该模块调用KUPL中的C/C++接口。
iso_c_binding 提供了一系列与 C 语言基本类型一一对应的 Fortran 类型种类(kind),例如:
| C类型 | Fortran对应 |
|---|---|
| int | integer(c_int) |
| double | real(c_double) |
| float | real(c_float) |
| char | character(c_char) |
| size_t | character(c_char) |
| void* | type(c_ptr) |
步骤一:声明C兼容的Fortran接口
在Fortran中使用 bind(C) 属性声明 C 兼容的接口,并通过 iso_c_binding 提供的类型传递参数。
请注意:C 默认按值传递,Fortran 默认按引用传递。
因此,声明参数时:
(1)如果 C 函数期望按值传参(如 int x),Fortran 声明的参数必须加 value 属性。如果 C 函数期望指针(如 int* x),Fortran 可直接声明。
(2)如果C接口接收的是结构体,在Fortran中同样需要使用 bind(C) 定义同样的结构体。
步骤二:在Fortran中调用声明的接口
如上一步所说,Fortran 默认按引用传递,一般情况下,Fortran 调用时可直接传变量(默认传地址)。
一个比较常见的特殊情况是,如果C 函数参数是 void* 或 type(c_ptr),需要使用 c_loc 函数用于获取一个 Fortran 变量的 C 兼容指针(type(c_ptr))。
步骤三:编译链接
将C接口编译成.o文件,如果Fortran声明的接口是独立文件,也需要编译出.mod和.o文件,Fortran可执行文件链接时需要加上这两个.o文件。
3 KUPL接口转换举例
3.1 kupl_parallel_for
将kupl_parallel_for封装一层C接口以更方便在软件中使用:
int kupl_parallel_for(kupl_parallel_for_desc_t *desc, kupl_pf_func_t func, void *args); // KUPL定义的接口
// 为方便使用封装一层
void c_kupl_parallel_for(int lower, int upper, void (*func)(kupl_nd_range_t *, void *, int, int), void *args)
{
kupl_nd_range_t range;
KUPL_1D_RANGE_INIT(range, lower, upper, 1);
kupl_parallel_for_desc_t desc = {
.range = &range,
.concurrency = g_threads,
.egroup = g_egroup,
.policy = KUPL_LOOP_POLICY_STATIC
};
kupl_parallel_for(&desc, func, args);
}Fortran接口的声明如下:
module kupl_mod
use iso_c_binding
implicit none
interface
subroutine f_kupl_parallel_for(lower, upper, func, args) bind(c, name="c_kupl_parallel_for")
import :: c_funptr, c_ptr, c_int
integer(c_int), value :: lower
integer(c_int), value :: upper
type(c_funptr), value :: func
type(c_ptr), value :: args
end subroutine f_kupl_parallel_for
end interface
end module kupl_modFortran调用:
type(cell_loop_stru), target :: args ! args要用到c_loc,需要target属性
type(c_funptr) :: func ! parallel接口需要回调函数,声明为c_funptr
func = c_funloc(cell_loop) ! cell_loop是KUPL定义的回调函数,需要c_funloc转换
call f_kupl_parallel_for(1, n, func, c_loc(args)) ! 直接传变量,args因为是void*,需要c_loc3.2 共享内存通信函数
KUPL提供了共享内存底层通信接口,以及基于这些接口的集合通信函数实现,常与MPI协作使用。
C接口:
int kupl_shm_comm_create(int size, int rank, int pid, kupl_shm_oob_cb_h oob_cbs, void *group, kupl_shm_comm_h *comm);
int kupl_shm_win_alloc(size_t size, kupl_shm_comm_h comm, void **baseptr, kupl_shm_win_h *win);
void c_kupl_shm_comm_create(int *shm_comm, int rank, kupl_shm_comm_h *kupl_comm)
{
MPI_Comm comm = MPI_Comm_f2c(*shm_comm); // MPI提供通信域的转换接口
int size;
MPI_Comm_size(comm, &size);
// ...一些赋值...
kupl_shm_comm_create(size, rank, pid, oob_cbs_h, (void *)comm, kupl_comm);
}Fortran接口:
! 接口用到了结构体需要定义
type, bind(c) :: kupl_shm_comm_h
type(c_ptr) :: ptr
end type kupl_shm_comm_h
type, bind(c) :: kupl_shm_win_h
type(c_ptr) :: ptr
end type
subroutine f_kupl_shm_comm_create(comm, rank, kupl_comm) bind(c, name="c_kupl_shm_comm_create")
import :: c_int, kupl_shm_comm_h
integer(c_int) :: comm
integer(c_int), value :: rank
type(kupl_shm_comm_h), intent(out) :: kupl_comm
end subroutine
function f_kupl_shm_win_alloc(buf_size, kupl_comm, baseptr, win) bind(c, name="kupl_shm_win_alloc") ! function定义需要返回值的接口
import :: c_size_t, c_ptr, kupl_shm_comm_h, kupl_shm_win_h, c_int
integer(c_size_t), value :: buf_size
type(kupl_shm_comm_h), value :: kupl_comm
type(c_ptr), intent(out) :: baseptr
type(kupl_shm_win_h), intent(out) :: win
integer(c_int) :: err ! 最后一个参数是函数返回值
end functionFortran调用:
type(kupl_shm_comm_h) :: kupl_comm
call f_kupl_shm_comm_create(mpi_comp_comm, mpi_comp_rank, kupl_comm)
type(c_ptr) :: local_buffer
type(kupl_shm_win_h) :: kupl_win
real(8), pointer :: mpi_send_buf(:)
err = f_kupl_shm_win_alloc(buf_size, kupl_comm, local_buffer, kupl_win)
CALL c_f_pointer(local_buffer, mpi_send_buf, [send_count]) ! local_buffer是出参,Fortran需要c_f_pointer转为Fortran指针4 注意事项
1、C 函数需要的入参是值,Fortran 的参数传递方式一定要加 value,否则会读错内存。
2、传给C接口的字符串必须以 \0 结尾:
msg = 'Hello' // c_null_char ! 必须补 \03、在 Fortran 中定义的二维(及以上)数组传递给 C 函数时,在内存中的排布方式(存储顺序)是列优先,而 C 语言默认使用行优先。因此将Fortran的数组传给C前,需要先将Fortran的数组按照行优先的逻辑重新排列再传入。
4、C 数组下标从 0 开始,Fortran 从 1 开始。在内存中传递的只是地址,与下标无关,但如果涉及到索引计算等操作时,需注意下标代表的含义。


