Last week I needed to get Fortran working with OpenCL. As the example-page is not up-to-date and not much documentation is on the interwebs outside the official page, this was not as straight-forward as I hoped. The test-suite and this article provided code I could actually use. First I wanted to have things in a module, second I needed to control which device I wanted to use, third I needed function-names that could be used in a larger project. The result is below, and hopefully usable for the Fortran folks around who want to add some OpenCL-kernels to their existing code.
It uses the two-step initialisation we know from C, for safe memory allocation. It is based on the utils.f90 from the test-suite.
The only good way to translate is the Rose-compiler – which is a pain to install. I tried various f2c-scripts (from the 90’s, but they all failed. I must say that continuous switching between Fortran-mode and C-mode was the hardest part of the porting.
If you have tips&tricks to use OpenCL from Fortran, let everybody know in the comments. Also let me know if the code doesn’t work for you, or you have improvements (like better error-handling).
The rest of utils.f90 (which I renamed to clutils.f90 for better integration) is mostly the same – only this subroutine needed changes:
(...) subroutine cl_initialize(platform_id, device_id, device, context, command_queue) !use ISO_C_BINDING type(cl_device_id), intent(out) :: device type(cl_context), intent(out) :: context type(cl_command_queue), intent(out) :: command_queue integer :: platform_id integer :: device_id integer :: platform_count, device_count, ierr character(len = 100) :: info type(cl_platform_id) :: platform type(cl_platform_id), allocatable, target :: platform_ids(:) type(cl_device_id), allocatable, target :: device_ids(:) ! get the platform ID call clGetPlatformIDs(platform_count, ierr) if(ierr /= CL_SUCCESS) call error_exit('Cannot get CL platform.') allocate(platform_ids(platform_count)) call clGetPlatformIDs(platform_ids, platform_count, ierr) if(ierr /= CL_SUCCESS) call error_exit('Cannot get CL platform.') if (platform_id .gt. platform_count .or. platform_id .lt. 1) platform_id = 0 platform = platform_ids(platform_id) ! get the device ID call clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, device_count, ierr) if(ierr /= CL_SUCCESS) call error_exit('Cannot get CL device.') allocate(device_ids(device_count)) call clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, device_ids, device_count, ierr) if(ierr /= CL_SUCCESS) call error_exit('Cannot get CL device.') if (device_id .gt. device_count .or. device_id .lt. 1) device_id = 1 device = device_ids(device_id) ! get the device name and print it call clGetDeviceInfo(device, CL_DEVICE_NAME, info, ierr) print*, "CL device: ", info ! create the context and the command queue context = clCreateContext(platform, device, ierr) command_queue = clCreateCommandQueue(context, device, CL_QUEUE_PROFILING_ENABLE, ierr) end subroutine cl_initialize (...)
It can be called from Fortran 90 like this (based on sum.f90):
module cltest use cl use clutils implicit none type(cl_device_id) :: device type(cl_context) :: context type(cl_command_queue) :: command_queue contains subroutine runcltest(clplatform_id, cldevice_num) type(cl_kernel) :: kernel integer :: data_size, ierr integer(8) :: size_in_bytes, globalsize, localsize real, allocatable :: vec1(:), vec2(:) type(cl_mem) :: cl_vec1, cl_vec2 INTEGER :: cldevice_num, clplatform_id call cl_initialize(clplatform_id, cldevice_num, device, context, command_queue) (...) call clReleaseKernel(kernel, ierr) call clReleaseCommandQueue(command_queue, ierr) call clReleaseContext(context, ierr) end subroutine runcltest end module cltest
The rest of the functions in FortranCL you can find in the reference-wiki.
Ah, the software has has compiled successfully, so I can continue programming. If you find the wrapper useful, don’t forget to thank Xavier Andrade Valencia for creating this wrapper.
Need help with porting Fortran to OpenCL? At StreamHPC can help you to get optimised code.