Fortran软件使用KUPL接口
Fortran软件使用KUPL接口 关于Fortran软件如何使用KUPL接口介绍 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),例如:section: Fortran软件使用KUPL接口 关于Fortran软件如何使用KUPL接口介绍 Fortran软件如何使用KUPL接口 > 2 基本流程
source_type: html_table
fields:
- C类型
- Fortran对应
rows:
- C类型=int; Fortran对应=integer(c_int)
- C类型=double; Fortran对应=real(c_double)
- C类型=float; Fortran对应=real(c_float)
- C类型=char; Fortran对应=character(c_char)
- C类型=size_t; Fortran对应=character(c_char)
- C类型=void*; Fortran对应=type(c_ptr)步骤一:声明C兼容的Fortran接口
在Fortran中使用 bind© 属性声明 C 兼容的接口,并通过 iso_c_binding 提供的类型传递参数。
请注意:C 默认按值传递,Fortran 默认按引用传递。
因此,声明参数时:
(1)如果 C 函数期望按值传参(如 int x),Fortran 声明的参数必须加 value 属性。如果 C 函数期望指针(如 int* x),Fortran 可直接声明。
(2)如果C接口接收的是结构体,在Fortran中同样需要使用 bind© 定义同样的结构体。
步骤二:在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_mod
Fortran调用:
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_loc### 3.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© :: kupl_shm_comm_h type(c_ptr) :: ptr end type kupl_shm_comm_h type, bind© :: 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 function
Fortran调用:
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 ! 必须补 \0
3、在 Fortran 中定义的二维(及以上)数组传递给 C 函数时,在内存中的排布方式(存储顺序)是列优先,而 C 语言默认使用行优先。因此将Fortran的数组传给C前,需要先将Fortran的数组按照行优先的逻辑重新排列再传入。
4、C 数组下标从 0 开始,Fortran 从 1 开始。在内存中传递的只是地址,与下标无关,但如果涉及到索引计算等操作时,需注意下标代表的含义。
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐
所有评论(0)