• 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的实现,其中数据在主机和设备之间隐式地移动。可以用最少的代码快速获得功能,开发人员不必担心在主机和设备之间移动内存

    1. #include
    2. using namespace sycl;
    3. static const int N = 16;
    4. int main() {
    5. queue q;
    6. std::cout << "Device : " << q.get_device().get_info() << "\n";
    7. //# USM allocation using malloc_shared
    8. int *data = malloc_shared<int>(N, q);
    9. //# Initialize data array
    10. for (int i = 0; i < N; i++) data[i] = i;
    11. //# Modify data array on device
    12. q.parallel_for(range<1>(N), [=](id<1> i) { data[i] *= 2; }).wait();
    13. //# print output
    14. for (int i = 0; i < N; i++) std::cout << data[i] << "\n";
    15. free(data, q);
    16. return 0;
    17. }

    运行结果

    数据显示移动

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

    1. #include
    2. using namespace sycl;
    3. static const int N = 16;
    4. int main() {
    5. queue q;
    6. std::cout << "Device : " << q.get_device().get_info() << "\n";
    7. //# initialize data on host
    8. int *data = static_cast<int *>(malloc(N * sizeof(int)));
    9. for (int i = 0; i < N; i++) data[i] = i;
    10. //# Explicit USM allocation using malloc_device
    11. int *data_device = malloc_device<int>(N, q);
    12. //# copy mem from host to device
    13. q.memcpy(data_device, data, sizeof(int) * N).wait();
    14. //# update device memory
    15. q.parallel_for(range<1>(N), [=](id<1> i) { data_device[i] *= 2; }).wait();
    16. //# copy mem from device to host
    17. q.memcpy(data, data_device, sizeof(int) * N).wait();
    18. //# print output
    19. for (int i = 0; i < N; i++) std::cout << data[i] << "\n";
    20. free(data_device, q);
    21. free(data);
    22. return 0;
    23. }

    运行结果

    代码解释

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

    首先本代码使用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

    实验要求

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

    1. #include
    2. #include
    3. using namespace sycl;
    4. static const int N = 1024;
    5. int main() {
    6. queue q;
    7. std::cout << "Device : " << q.get_device().get_info() << "\n";
    8. //intialize 2 arrays on host
    9. int *data1 = static_cast<int *>(malloc(N * sizeof(int)));
    10. int *data2 = static_cast<int *>(malloc(N * sizeof(int)));
    11. for (int i = 0; i < N; i++) {
    12. data1[i] = 25;
    13. data2[i] = 49;
    14. }
    15. //# STEP 1 : Create USM device allocation for data1 and data2
    16. //# YOUR CODE GOES HERE
    17. int *data_device1 = malloc_device<int>(N, q);
    18. int *data_device2 = malloc_device<int>(N, q);
    19. //# STEP 2 : Copy data1 and data2 to USM device allocation
    20. //# YOUR CODE GOES HERE
    21. q.memcpy(data_device1, data1, sizeof(int) * N).wait();
    22. q.memcpy(data_device2, data2, sizeof(int) * N).wait();
    23. //# STEP 3 : Write kernel code to update data1 on device with sqrt of value
    24. q.parallel_for(N, [=](auto i) {
    25. //# YOUR CODE GOES HERE
    26. data_device1[i] = (int)std::sqrt(float(data_device1[i]));
    27. }).wait();
    28. //# STEP 3 : Write kernel code to update data2 on device with sqrt of value
    29. q.parallel_for(N, [=](auto i) {
    30. //# YOUR CODE GOES HERE
    31. data_device2[i] = (int)std::sqrt(float(data_device2[i]));
    32. }).wait();
    33. //# STEP 5 : Write kernel code to add data2 on device to data1
    34. q.parallel_for(N, [=](auto i) {
    35. //# YOUR CODE GOES HERE
    36. data_device1[i] += data_device2[i];
    37. }).wait();
    38. //# STEP 6 : Copy data1 on device to host
    39. //# YOUR CODE GOES HERE
    40. q.memcpy(data1, data_device1, sizeof(int) * N).wait();
    41. //# verify results
    42. int fail = 0;
    43. for (int i = 0; i < N; i++) if(data1[i] != 12) {fail = 1; break;}
    44. if(fail == 1) std::cout << " FAIL"; else std::cout << " PASS";
    45. std::cout << "\n";
    46. //# STEP 7 : Free USM device allocations
    47. //# YOUR CODE GOES HERE
    48. free(data_device1, q);
    49. free(data_device2, q);
    50. free(data1);
    51. free(data2);
    52. //# STEP 8 : Add event based kernel dependency for the Steps 2 - 6
    53. return 0;
    54. }

     运行结果

    注:

     

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

  • 相关阅读:
    单片机对比:选择最适合你的单片机
    Spring Boot整合Zookeeper实现分布式锁
    音乐制作软件 Studio One 6 mac中文版软件特点
    Java实现快速排序以及原理
    宝塔Linux面板命令大全
    【学习笔记69】函数的柯里化
    【React】React学习:从初级到高级(三)
    Redis-6.2.6 Linux 离线安装教程,让你一路畅通无阻,5分钟轻松完成安装。
    浅谈用Redis实现分布式锁的方案及细节
    Request failed with status code 422
  • 原文地址:https://blog.csdn.net/m0_63222058/article/details/134277142