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

相关文章

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Mysql表的简单操作(基本技能)

《Mysql表的简单操作(基本技能)》在数据库中,表的操作主要包括表的创建、查看、修改、删除等,了解如何操作这些表是数据库管理和开发的基本技能,本文给大家介绍Mysql表的简单操作,感兴趣的朋友一起看... 目录3.1 创建表 3.2 查看表结构3.3 修改表3.4 实践案例:修改表在数据库中,表的操作主要

springboot简单集成Security配置的教程

《springboot简单集成Security配置的教程》:本文主要介绍springboot简单集成Security配置的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录集成Security安全框架引入依赖编写配置类WebSecurityConfig(自定义资源权限规则

如何使用Python实现一个简单的window任务管理器

《如何使用Python实现一个简单的window任务管理器》这篇文章主要为大家详细介绍了如何使用Python实现一个简单的window任务管理器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 任务管理器效果图完整代码import tkinter as tkfrom tkinter i

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

使用EasyExcel实现简单的Excel表格解析操作

《使用EasyExcel实现简单的Excel表格解析操作》:本文主要介绍如何使用EasyExcel完成简单的表格解析操作,同时实现了大量数据情况下数据的分次批量入库,并记录每条数据入库的状态,感兴... 目录前言固定模板及表数据格式的解析实现Excel模板内容对应的实体类实现AnalysisEventLis

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.

Java8需要知道的4个函数式接口简单教程

《Java8需要知道的4个函数式接口简单教程》:本文主要介绍Java8中引入的函数式接口,包括Consumer、Supplier、Predicate和Function,以及它们的用法和特点,文中... 目录什么是函数是接口?Consumer接口定义核心特点注意事项常见用法1.基本用法2.结合andThen链

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.