FortranCL简单乘法

2023-12-26 06:58
文章标签 简单 乘法 fortrancl

本文主要是介绍FortranCL简单乘法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【FortranCL】FortranCL简单乘法

OpenCL简要介绍

OpenCL是面向由CPU、GPU和其他处理器组合构成的计算机进行编程的行业标准框架。OpenCL通过公布硬件来提供高度的可移植性(支持N卡、A卡、CPU等),而不是将硬件隐藏在精巧的抽象下,这表明OpenCL程序员必须显示的定义平台、上下文,以及在不同设备上的调度工作。
在一个典型的宿主机程序中,程序员不仅定义上下文和命令队列,定义内存和程序对象,还会构建宿主机上所需要的数据结构来支持应用。然后把重点转向命令队列,内存对象从宿主机移到设备上,内核参数关联到内存对象,然后提交到命令队列执行。内核完成工作时,计算中生成的内存对象可能会再复制到宿主机。
OpenCL框架基本工作流小结:
在这里插入图片描述
首先是一个定义上下文的宿主机程序,上图上下文包含两个Opencl设备,一个CPU一个GPU。接下来定义了命令队列,两个队列,一个面向GPU的有序命令队列,一个面向CPU的乱序命令队列。然后宿主机程序定义一个程序对象,这个程序对象编译后将为两个Opencl设备生成内核。接下来宿主机程序定义程序所需的内存对象,并将它们映射到内核的参数。最后,宿主机程序将命令放入命令队列来执行这些内核。

FortranCL简要介绍

  • FortranCL是Fortran 90的OpenCL接口。它允许程序员直接从Fortran调用OpenCL。内核仍然用C语言编写。
    代码包github地址
  • FortranCL的google官网只给出了一维向量和示例代码,虽然FortranCL接口尽可能接近OpenCL原来的API,也还是在参数等地方有些不同。例如函数clEnqueueNDRangeKernel在OpenCL中需要一个参数work_dim(说明工作项维度),而FortranCL是从global_work_size和local_work_size数组的维度获得的(它们必须具有相同的维度)。一些具体参数信息在FortranCL Wiki中有介绍。

构建FortranCL程序

  • VS-IVF使用FortranCL静态库构建程序:
    • 创建一个Fortran控制台应用程序
    • 设置以下项目属性:
      • Fortran->语言->启用Fortran2003语义
      • Fortran->General->Additional Include Directories->解压缩FortranCL-x64.zip的目录
      • Linker->General-> Additional Include Directories->用于OpenCL安装的英特尔SDK的Lib\x64目录
      • Linker->Input->Additional Dependencies->OpenCL.lib
      • 讲解压缩的zip中的FortranCL.lib作为源文件添加到项目中
    • 注意:需要使用生成->配置管理 来创建新的x64平台配置
    • 项目目录结构(cl文件需要与.f90在同一文件夹下):
    • 在这里插入图片描述

代码示例

mult.f90

program main
use cl
implicit none
integer,parameter ::nx=1000,ny=1000
double precision :: ax(nx,ny),ay(nx,ny),res(nx,ny),res_cpu(nx,ny)
type(cl_platform_id)  ::platform
type(cl_device_id)    ::device
type(cl_context)      ::context
type(cl_command_queue) ::command_queue
type(cl_program)     ::prog
type(cl_kernel)      ::kernel
type(cl_mem)         ::cl_ax,cl_ay,cl_res
integer    :: num, ierr, irec ,i ,j,CPU_START,CPU_END,GPU_START,GPU_END,INI_START,INI_END,BUILDK_START,BUILDK_END,CBUFFER_START,CBUFFER_END,WBUFFER_START,WBUFFER_END,RUNK_START,RUNK_END
integer(8)   ::bytes,globalsize(2),localsize(2)
character(len = 100)  :: info
integer, parameter :: iunit = 10
integer, parameter :: source_length = 5000
character(len = source_length) :: sourcecall RANDOM_NUMBER(ax)call RANDOM_NUMBER(ay)!=====================! initialization!=====================call system_clock(GPU_START)! get the platform idcall system_clock(INI_START)call clgetplatformids(platform, num, ierr)if(ierr /= cl_success) stop "cannot get cl platform."! get the device idcall clgetdeviceids(platform, cl_device_type_all, device, num, ierr)if(ierr /= cl_success) stop "cannot get cl device."! get the device name and print itcall clgetdeviceinfo(device, cl_device_name, info, ierr)print*, "cl device: ", info!get max groupsizecall clgetdeviceinfo(device, cl_device_max_work_group_size, info, ierr)! create the context and the command queuecontext = clcreatecontext(platform, device, ierr)command_queue = clcreatecommandqueue(context, device, cl_queue_profiling_enable, ierr)call system_clock(INI_END)!=====================! build the kernel!=====================call system_clock(BUILDK_START)! read the source file  open(unit = iunit, file = 'D:\DongLY\vsproject\OpenCLmult\OpenCLmult\mult.cl', access='direct', status = 'old', action = 'read', iostat = ierr, recl = 1)write(*,*) ierrif (ierr /= 0) stop 'cannot open file mult.cl'source = ''irec = 1doread(unit = iunit, rec = irec, iostat = ierr) source(irec:irec)if (ierr /= 0) exitif(irec == source_length) stop 'Error: CL source file is too big'irec = irec + 1end doclose(unit = iunit)! create the programprog = clCreateProgramWithSource(context, source, ierr)if(ierr /= CL_SUCCESS) stop 'Error: cannot create program from source.'! buildcall clBuildProgram(prog, '-cl-mad-enable', ierr)! get the compilation logcall clGetProgramBuildInfo(prog, device, CL_PROGRAM_BUILD_LOG, source, irec) !将source作为返回错误信息的日志承载体if(len(trim(source)) > 0) write(*,*)trim(source) !若有错误信息打印出来!write(*,*) "ierr2",ierrif(ierr /= CL_SUCCESS) stop 'Error: program build failed.'! finally get the kernel and release the programkernel = clCreateKernel(prog, 'mult', ierr)call clReleaseProgram(prog, ierr)call system_clock(BUILDK_END)!=====================! RUN THE KERNEL!=====================bytes = int(nx, 8)*int(ny, 8)*8_8cl_ax = clCreateBuffer(context, CL_MEM_READ_ONLY, bytes, ierr)cl_ay = clCreateBuffer(context, CL_MEM_READ_ONLY, bytes, ierr)cl_res = clCreateBuffer(context, CL_MEM_READ_WRITE, bytes, ierr)call system_clock(WBUFFER_START)call clEnqueueWriteBuffer(command_queue, cl_ax, cl_bool(.true.), 0_8, bytes, ax(1,1), ierr)call clEnqueueWriteBuffer(command_queue, cl_ay, cl_bool(.true.), 0_8, bytes, ay(1,1), ierr)call system_clock(WBUFFER_END)call system_clock(CBUFFER_START)call clSetKernelArg(kernel, 0, cl_ax, ierr)call clSetKernelArg(kernel, 1, cl_ay, ierr)call clSetKernelArg(kernel, 2, cl_res, ierr)call clSetKernelArg(kernel, 3, nx, ierr)call clSetKernelArg(kernel, 4, ny, ierr)call system_clock(CBUFFER_END)localsize(1) = 32localsize(2) = 32globalsize(1) = int(ny, 8)globalsize(2) = int(nx, 8)if(mod(globalsize(1), localsize(1)) /= 0) globalsize(1) = globalsize(1) + localsize(1) - mod(globalsize(1), localsize(1)) if(mod(globalsize(2), localsize(2)) /= 0) globalsize(2) = globalsize(2) + localsize(2) - mod(globalsize(2), localsize(2)) write(*,*) "localsize",localsizewrite(*,*) "globalsize",globalsize! execute the kernelcall system_clock(RUNK_START)call clEnqueueNDRangeKernel(command_queue, kernel,globalsize, localsize, ierr)call clEnqueueReadBuffer(command_queue, cl_res, cl_bool(.true.), 0_8, bytes, res(1,1), ierr)write(*,*) ierr   !打印出ierr可观察错误码,0为执行成功call system_clock(RUNK_END)call clFinish(command_queue, ierr)call clReleaseKernel(kernel, ierr)call clReleaseCommandQueue(command_queue, ierr)call clReleaseContext(context, ierr)call system_clock(GPU_END)!--------------------cpu----------------------------!---------------------------------------------------call system_clock(CPU_START)do j=2,nydo i=2,nxres_cpu(i,j) = ax(i,j)*ay(i,j)! write(*,*) "res_cpu(",i,",",j,"),",(j-1)*nx+i-1,":",res_cpu(i,j)end doend docall system_clock(CPU_END)write(*,*) 'CPU time is ' , (CPU_END-CPU_START)/10000.write(*,*) 'GPU time is ' , (GPU_END-GPU_START)/10000.write(*,*) 'INIT TIME is ' , (INI_END-INI_START)/10000.write(*,*) 'BUILDK TIME is ' , (BUILDK_END-BUILDK_START)/10000.write(*,*) 'CREATE BUFFER TIME is ' , (CBUFFER_END-CBUFFER_START)/10000.write(*,*) 'WRITE BUFFER TIME is ' , (WBUFFER_END-WBUFFER_START)/10000.write(*,*) 'RUNK TIME is ' , (RUNK_END-RUNK_START)/10000.pauseend program

mult.cl

__kernel void mult(__global double *ax, __global double *ay,__global double *res, const int nx, const int ny){int i = get_global_id(0);int j = get_global_id(1);int k = 1;if(i>=1&&i<=ny &&j>=1&&j<=nx)  res[i*nx + j] = ax[i*nx + j ] * ay[i*nx + j];
}

目前还存在一些问题,并且GPU时间明显慢于CPU(初始化创建上下文编译以及传输数据部分较为耗时)

这篇关于FortranCL简单乘法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/538480

相关文章

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

redis群集简单部署过程

《redis群集简单部署过程》文章介绍了Redis,一个高性能的键值存储系统,其支持多种数据结构和命令,它还讨论了Redis的服务器端架构、数据存储和获取、协议和命令、高可用性方案、缓存机制以及监控和... 目录Redis介绍1. 基本概念2. 服务器端3. 存储和获取数据4. 协议和命令5. 高可用性6.

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

四种简单方法 轻松进入电脑主板 BIOS 或 UEFI 固件设置

《四种简单方法轻松进入电脑主板BIOS或UEFI固件设置》设置BIOS/UEFI是计算机维护和管理中的一项重要任务,它允许用户配置计算机的启动选项、硬件设置和其他关键参数,该怎么进入呢?下面... 随着计算机技术的发展,大多数主流 PC 和笔记本已经从传统 BIOS 转向了 UEFI 固件。很多时候,我们也

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个