Intel oneAPI笔记(4)--jupyter官方文档(Unified Shared Memory)学习笔记

前言

本文是对jupyterlab中oneAPI_Essentials/03_Unified_Shared_Memory文档的学习记录,主要包含对统一共享内存的讲解

USM概述

USM (Unified Shared Memory)是SYCL中基于指针的内存管理。对于使用malloc或new来分配数据的C和C++程序员来说应该很熟悉。当将现有的C/ C++代码移植到SYCL时,USM简化了程序员的开发

使用USM,开发人员可以在主机和设备代码中引用相同的内存对象

Types of USM

统一共享内存为管理内存提供了显式和隐式模型

USM初始化:下面的初始化显示了使用malloc_shared共享分配的示例,"q"队列参数提供了有关内存可访问的设备的信息

下面是在host上分配内存的方法:

释放USM

数据隐式移动

下面的SYCL代码显示了使用malloc_shared的USM的实现,其中数据在主机和设备之间隐式地移动。可以用最少的代码快速获得功能,开发人员不必担心在主机和设备之间移动内存

cpp 复制代码
#include <sycl/sycl.hpp>
using namespace sycl;

static const int N = 16;

int main() {
  queue q;
  std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n";

  //# USM allocation using malloc_shared
  int *data = malloc_shared<int>(N, q);

  //# Initialize data array
  for (int i = 0; i < N; i++) data[i] = i;

  //# Modify data array on device
  q.parallel_for(range<1>(N), [=](id<1> i) { data[i] *= 2; }).wait();

  //# print output
  for (int i = 0; i < N; i++) std::cout << data[i] << "\n";
  free(data, q);
  return 0;
}

运行结果

数据显示移动

下面的SYCL代码显示了使用malloc_device的USM实现,其中主机和设备之间的数据移动应该由开发人员使用memcpy显式地完成

cpp 复制代码
#include <sycl/sycl.hpp>
using namespace sycl;

static const int N = 16;

int main() {
  queue q;
  std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n";

  //# initialize data on host
  int *data = static_cast<int *>(malloc(N * sizeof(int)));
  for (int i = 0; i < N; i++) data[i] = i;

  //# Explicit USM allocation using malloc_device
  int *data_device = malloc_device<int>(N, q);

  //# copy mem from host to device
  q.memcpy(data_device, data, sizeof(int) * N).wait();

  //# update device memory
  q.parallel_for(range<1>(N), [=](id<1> i) { data_device[i] *= 2; }).wait();

  //# copy mem from device to host
  q.memcpy(data, data_device, sizeof(int) * N).wait();

  //# print output
  for (int i = 0; i < N; i++) std::cout << data[i] << "\n";
  free(data_device, q);
  free(data);
  return 0;
}

运行结果

代码解释

本代码让数据在主机区和设备区显示移动,提高了开发人员对数据的可控性

首先本代码使用malloc在主机分配内存,然后给这些内存赋值,然后使用malloc_device在设备区分配内存,然后把主机区的内存拷贝到设备区的这些内存中,然后在设备区加速处理这些数据之后再拷贝到主机区的原内存中,最后使用cout输出

USM的优势

SYCL*缓冲区功能强大且优雅,但是,在c++程序中用缓冲区替换所有指针和数组可能会给程序员带来负担,因此在这种情况下可以考虑使用USM

1.当把c++代码移植到sycl时,想要尽可能更改少的代码

2.当需要控制数据移动时,使用显式USM分配

3.在移植代码时使用共享分配可以快速获得功能

Data dependency in USM

程序员可以显式地使用wait对象,也可以使用命令组中的depends_on方法来指定在任务开始之前必须完成的事件列表

在下面的示例中,两个内核任务正在更新相同的数据数组,这两个内核可以同时执行,并且可能导致错误的结果

Different options to manage data dependency when using USM:

wait()

在内核任务上使用q.wait()来等待下一个依赖的任务可以开始,但是它会阻塞主机上的执行

in_order queue property

为队列使用in_order 队列属性,这将序列化所有内核任务。注意,即使队列没有数据依赖关系,执行也不会重叠

depends_on

在命令组中使用h.depends_on(e)方法来指定任务开始之前必须完成的事件

简化版

Code Example: USM and Data dependency

这个例子主要演示了上面三种方法的使用

初始代码

想要修改上面代码,只需下面三种方法三选一

使用wait

使用in_order queue property

使用depends_on

运行结果

Lab Exercise: Unified Shared Memory

实验要求

下面是我已经补全的代码和运行结果

cpp 复制代码
#include <sycl/sycl.hpp>
#include <cmath>
using namespace sycl;
static const int N = 1024;
int main() {
  queue q;
  std::cout << "Device : " << q.get_device().get_info<info::device::name>() << "\n";
  //intialize 2 arrays on host
  int *data1 = static_cast<int *>(malloc(N * sizeof(int)));
  int *data2 = static_cast<int *>(malloc(N * sizeof(int)));
  for (int i = 0; i < N; i++) {
    data1[i] = 25;
    data2[i] = 49;
  }
  //# STEP 1 : Create USM device allocation for data1 and data2
  //# YOUR CODE GOES HERE
  int *data_device1 = malloc_device<int>(N, q);
  int *data_device2 = malloc_device<int>(N, q);
  //# STEP 2 : Copy data1 and data2 to USM device allocation
  //# YOUR CODE GOES HERE  
  q.memcpy(data_device1, data1, sizeof(int) * N).wait();
  q.memcpy(data_device2, data2, sizeof(int) * N).wait();
  //# STEP 3 : Write kernel code to update data1 on device with sqrt of value
  q.parallel_for(N, [=](auto i) { 
    //# YOUR CODE GOES HERE 
    data_device1[i] = (int)std::sqrt(float(data_device1[i]));
  }).wait();
  //# STEP 3 : Write kernel code to update data2 on device with sqrt of value
  q.parallel_for(N, [=](auto i) { 
    //# YOUR CODE GOES HERE 
    data_device2[i] = (int)std::sqrt(float(data_device2[i]));
  }).wait();
  //# STEP 5 : Write kernel code to add data2 on device to data1
  q.parallel_for(N, [=](auto i) { 
  //# YOUR CODE GOES HERE 
      data_device1[i] += data_device2[i];
  }).wait();
  //# STEP 6 : Copy data1 on device to host
  //# YOUR CODE GOES HERE 
  q.memcpy(data1, data_device1, sizeof(int) * N).wait();
  //# verify results
  int fail = 0;
  for (int i = 0; i < N; i++) if(data1[i] != 12) {fail = 1; break;}
  if(fail == 1) std::cout << " FAIL"; else std::cout << " PASS";
  std::cout << "\n";
  //# STEP 7 : Free USM device allocations
  //# YOUR CODE GOES HERE
  free(data_device1, q);
  free(data_device2, q);
  free(data1);
  free(data2);
  //# STEP 8 : Add event based kernel dependency for the Steps 2 - 6
  return 0;
}

运行结果

注:

这里可能转成double或者什么也不转都会报错,我电脑对这一块好像仅支持单精度,只能转成float才能运行,具体原理也不理解

相关推荐
咖啡里的茶i1 分钟前
Vehicle友元Date多态Sedan和Truck
c++
海绵波波1077 分钟前
Webserver(4.9)本地套接字的通信
c++
黑叶白树10 分钟前
简单的签到程序 python笔记
笔记·python
@小博的博客13 分钟前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
幸运超级加倍~1 小时前
软件设计师-上午题-15 计算机网络(5分)
笔记·计算机网络
南宫生1 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
爱吃喵的鲤鱼1 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
懒惰才能让科技进步2 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
7年老菜鸡2 小时前
策略模式(C++)三分钟读懂
c++·qt·策略模式
Ni-Guvara2 小时前
函数对象笔记
c++·算法