Lectures

L1 Intro

硬件平台

  • 计算节点:

    • 计算节点:高性能刀片或者机架服务器

    • 胖服务器节点:SMP架构高性能服务器,具备多CPU和大内存容量

    • GPU计算节点:服务器中安装GPGPU卡,使用GPU进行运算加速

  • 三平面组网:

    • 数据网络:用于存储或者数据传输

    • 计算网络:用于计算过程中的消息传递

    • 管理网络:用于集群系统管理

HPC典型业务负载和应用

Intel 特性

  • AI量化

  • Sapphire Rapids: 提供 AMX 矩阵运算指令集,提供超AVX512 16倍的性能

  • GPU: Ponte Vecchio

    • 128X Cores

    • 64MB L1 cache + 408MB L2 Cache + 128GB HBM2e

  • Optane 持久内存

  • DAOS 文件存储格式

    • 极高IO带宽和IOPS

    • 克服给予块和POSIX接口的性能瓶颈,支持HPC/AI/BIG DATA多种业务接口

  • oneAPI 统一的软件堆栈

    • Low-Level Libraries:

      • oneMKL: Math Kernel Library

      • oneDNN: Deep Neural Network

      • oneDTL: Data Transform Library

      • oneIPL: Image Process Library

      • oneVPL: Video Process Library

      • oneTBB: Threading Building Blocks

      • oneCCL: Collective Communication Library

      • oneDPL: DPC++ Library

      • etc

    • Languages:

      • DPC++

      • SYCL

      • etc

    • DPCT: Intel DPC++ Compatibility Tool

      • 协助开发者完成一次性代码迁移,从CUDA到DPC++

    • CFlags:

      • Profile guided optimization(multi-step build):

        • -prof-gen: generation

        • -prof-use: usage

      • -fast: -ipo -O3 -no-prec-div -static -fp-mode fast

      • -fiopenmp -fopenmp-targets=spir64: offloading openmp on gpu device

      • -O2 vs -O3: in some scenarios, O2 performs better than O3

      • -mprefer-vector-width=512: LLVM cflag, indicating the preference to AVX512

      • -ipo: multi-file inter-procedural optimization, aiming at the potential optimization in caller/callee chain

      • -pad: padding the array to rearrange the memory distribution to make full use of cache

    • Compilers:

Driver
Target
OpenMP support
OpenMP offload Support

icc

CPU

Y

N

dpcpp

CPU,GPU,FPGA

N

N

icx

CPU,GPU

Y

Y

ifort

CPU

Y

N

ifx

CPU,GPU

Y

Y

L2.1 DPC++

Intel Advisor

  • Offload Advisor: 预估卸载至加速器的性能

  • Roofline Analysis: 优化用于内存和计算的 CPU/GPU 代码

  • Vectorization Advisor: 添加和优化矢量化

  • Threading Advisor: 为非线程化应用添加有效的线程化功能

  • 流图分析器:高校创建和分析流图

DPC++

Device Selector

device_selector supports selecting specific devices in runtime, executing the kernel with information provided by user heuristically. Example:

queue

  • Queue is the execution of submitting commands to SYCL runtime.

  • Queue is the mechanism of submitting tasks to devices.

  • Queue:Device is M:1 relationship.

kernel

  • Kernel encapsulates the data and function when executing in device.

  • Kernel is not constructed explicitly by user.

  • Kernel is constructed when calling kernel scheduling functions like parallel_for.

The lambda function submitted in queue is called command group.

Basic Parallel Kernel

  • range: describe the parallel execution iteration space

  • id: index a single instance in parallel execution

  • item: a single instance, can be used to query the range and other attributes

ND-Range Kernel

  • ND-Range 内核是另一种表示并行性的方法,通过提供对本地内存的访问以及将执行映射到硬件上的计算单元实现底层性能调整。

  • nd_range 将整个迭代空间划分为多个较小的工作组,工作组中的工作被安排在硬件的单个计算单元上。

  • 讲内核执行分组到工作组中,可以控制工作分配中的资源使用和负载均衡。

  • nd_range 的第一个参数表示整个迭代空间的大小(global_size),第二个参数表示工作组的大小(work-group size)。

缓冲区内存模式

  • 缓冲区表示数据的所有权,不表示数据具体的存储位置

  • 缓冲区:将数据封装在 SYCL 应用中

  • 访问器:用于实际访问缓冲区中的数据

    • 在 SYCL 图中创建数据依赖关系,以在内核执行中进行排序

对于下面的例子而言,accessorhandler 做绑定,handlerqueue 绑定,决定所在的设备,于是 accessor 提供到对应设备的访问能力。

异步执行

SYCL 应用分为两个部分:

  • 主机代码

  • 内核执行图

除了同步操作,这些执行都是互相独立的

  • 主机代码提交工作(q.submit)以构建graph图(同时自身可以执行计算操作)

  • 内核执行和数据移动的graph图由SYCL运行时管理与主机代码异步zhixing

若存在多个内核,SYCL会根据accessor自动解决数据依赖:

数据依赖图可以分析得到:

同步操作

主机访存器

主机访问设备上的数据通过创建 host_accessor 实现。 因为数据可以还在计算过程中,所以主机访存器的创建是一个阻塞的同步操作,只有等到内核均完成执行并且数据可以通过主机访存器提供给主机后才返回。

缓冲区销毁

Example

L2.2 oneMKL

运行库链接方式:查询onemkl link line advisor

Contacts: [email protected]

USM (Unified Shared Memory)

类似于 CUDA 中的 Unified Memory。

  • 使用 Buffer API:buf 规定了数据的所有权,用户无需关心函数执行结果

  • 使用 USM API:用户定义了数据的共享存在方式,需要对任务完成与否负责

L3.1 Compiler Guidance

L3.2 SYCL Features

USM

Setup

USM is bind to specific queue, not accessible to other queue.

Usage

Before using USM, the SYCL provides the buffer method:

After using USM, developer does not need to care the data flow:

Ways

  • sycl::malloc_device: allocate memory on device, no accessible to host, need explicit data copy to access on host.

  • sycl::malloc_host: allocate memory on host, accessible to device via Direct Memory Access.

  • sycl::malloc_shared: allocate memory which can migrate between device and host by program automatically and implicitly. (Need wait to ensure the only access from host or device)

Example:

data dependency

  • in_order feature will make all task serialized, avoiding the data race.

Example:

  • depends_on can avoid making the situation the extreme by serializing all tasks.

Example:

Sub Groups

See Reference. hierarchy

A sub-group is a collection of contiguous work-items in the global index space that execute in the same VE thread. When the device compiler compiles the kernel, multiple work-items are packed into a sub-group by vectorization so the generated SIMD instruction stream can perform tasks of multiple work-items simultaneously. Properly partitioning work-items into sub-groups can make a big performance difference.

  • Sub-group同时执行的或具有额外调度保证的工作项 work-item 的子集。

  • 利用 Sub-group 有助于将执行映射到低级硬件,并可以帮助实现更高的性能。

Intel Graphic Gen11 Architecture
  • 工作组 work-group 中的所有工作会被安排到一个子切片 SubSlice 中,该子切片拥有自己的本地内存 SubSlice Local Memory,比访问 L3 Cache 快很多。

  • Sub-group 中的所有工作项都在单个 Execution Units 线程上执行。

  • Sub-group 中的每个工作项都映射到 SIMD 通道。

Why Sub-group?

  • work-item in Sub-group can communicate with each other using shuffle operations, avoiding accessing local or global memory, providing higher performance.

  • work-item in Sub-group can access sub-group group algorithm which implements frequently used parallel strategies.

    • sycl::shift_group_left(sg, x, 1)

    • sycl::shift_group_right(sg, x, 1)

    • sycl::select_from_group(sg, x, id)

    • sycl::permute_group_by_xor(sg, x, mask)

    • sycl::group_broadcast(sg, x, id)

    • sycl::reduce_over_group(sg, x, op)

    • sycl::exclusive_can_over_group(sg, x, op)

    • sycl::inclusive_can_over_group(sg, x, op)

    • sycl::any_off_group(sg, x)

    • sycl::all_off_group(sg, x)

    • sycl::none_off_group(sg, x)

Example of sub-group group algorithm:

Some attribute function of work item:

  • get_local_id() returns the index(es) of the work-item within its work-group

  • get_local_range() returns the size(s) of work-group

  • get_global_id() returns the index(es) of the work-item within the global size

  • get_global_range() returns the global size

  • get_group_id() returns the index(es) of the work-group

  • get_group_range() returns the size(s) of work-group within the parent global size

These attributes return the id<n> data, thus needing the index to access(e.g. item.get_local_id()[0]), or passing the index to function as a parameter to fetch the one(e.g. item.get_local_id(0)). Example:

But it is not the same as in sub-group, the functions below all return type id<1>, needing to retrieve by indexing ret[0]:

  • get_local_id() returns the id of work item in its sub-group.

  • get_local_range() returns the range of items in its sub-group.

  • get_group_id() returns the id of its sub-group in the work-group.

  • get_group_range() returns the range of sub-groups in the work-group.

Sub-group size will be chosen by compiler implicitly, but also can be assigned with supported size(related to hardware). Query the supported sizes by get_info:

User can define the size used for sub group by adding [[intel::reqd_sub_group_size(8)]]:

How to implement the get_sub_group_linear_id() function? sub_group_linear_id = sub_group_id in work_group + sub_group_range * work_group_id

Reduction

Before introducing simplified reduction in SYCL 2020, a reduction in global size is implemented with 2 steps, reduction on work group and accumulation on work-group result:

After simplified reduction, we can use reduction object to make this thing:

Multiple Reductions in one kernel:

L4 Profiler

VTune Profiler

Intel® VTune™ Profiler Performance Analysis Cookbook: Link

Profiling types

  • hotspots

  • memory-consumption

  • uarch-exploration

  • memory-access

  • threading

  • hpc-performance

  • system-overview

  • graphic-rendering

  • io

  • fpga-interaction

  • gpu-offload

  • gpu-hotspots

  • throttling

  • platform-profiler

  • cpugpu-concurrency

  • tsx-exploration

  • tsx-hotspots

  • sgx-hotspots

Example:

Advisor

Roofline

Command Lines to run roofline example:

Vectorization

Offload Modeling

The easiest way to run Offload Advisor is to use the batch mode that consists in running 2 scripts available is the folder $APM ($APM is available when Advisor is sourced).

  • collect.py: Used to collect data such as timing, flops, tripcounts and many more

  • analyze.py: Creating the report

To be more specific, collect.py runs the following analyses:

  • survey: Timing your application functions and loops, reading compiler diagnostics

  • tripcount: With flops and cache simulation to count the number of iterations in the loops as well as the number of operations and memory transfers

  • dependency: Check if you have data dependency in your loops, preventing it to be good candidates for offloading or vectorization

Offload Advisor is currently run from the command-line as below. Once the run is complete you can view the generated report.html.

L5 GPU Optimization Guide

  • Amdahl's Law: 并行加速比取决于可并行部分的比重。

    • $acc = \frac{W_s+W_p}{W_s+\frac{W_p}{p}}$

  • Locality Matters: 加速卡的专用内存也有层级

    • latency: register > cache > DRAM/HBM

    • 三原则:

      • 尽可能在加速器上保留数据

      • 内核执行时访问连续的内存块

      • 重构代码以获得更高的数据重用

GPU Architecture

See reference.

  • Xe Tile

    • 4HBM

    • 64GB Memory

    • Up to 4096 SMT threads

    • 4 Xe Slice

      • 16 Xe Core (a.k.a Dual Sub Slice(DSS) or Sub Slice(SS))

        • 一次分配一个 work-group

        • 8 vector engine (a.k.a. Execution Unit(EU))

          • 执行 SIMD 指令,一个时钟周期执行 32 个数据的指令。

          • 一次分配一个 sub-group

          • 512 FP16 operations/cycle

          • 256 FP32 operations/cycle

          • 256 FP64 operations/cycle

        • 8 matrix engine

          • 8192 INT8 operations/cycle

          • 4096 BF16 operations/cycle

          • 2048 FP32 operations/cycle

        • 512K L1 cache/SLM

        • 512B/cycle load/store memory access bandwidth

  • 本次 PAC 是内部版本(ATS-P),有两个 Tile,每个含 30 Xe Core,即 480个 EU。共 960 EU。

  • What is EU?

    An Execution Unit (EU) is the smallest thread-level building block of the Intel® Iris® Xe-LP GPU architecture. Each EU is simultaneously multithreaded (SMT) with seven threads. The primary computation unit consists of a 8-wide Single Instruction Multiple Data (SIMD) Arithmetic Logic Units (ALU) supporting SIMD8 FP/INT operations and a 2-wide SIMD ALU supporting SIMD2 extended math operations. Each hardware thread has 128 general-purpose registers (GRF) of 32B wide.

Tile -> Slice -> Core -> EU

Thread mapping and GPU utilization

See Reference

  • Thread context: 线程上下文,内核应该有足够多的线程来占满 GPU context

    • EU 上可放线程数量:Xe-LP 7 thread, Xe-HPC 8 thread

  • SIMD unit: 程序应该向量化利用 SIMD 指令。

    • Xe-LP 执行 Int 是 SIMD32。

    • 在 ATS-P 上,FP32 和 FP64 都是 SIMD16。

  • SYCL 中限制 workgroup size 是 512。

Calculation: 以 Xe-LP(Low Power低功耗家用版本) 为例,其上有 6 个 sub-slice,每个 sub-slice 上有 16 个 EU,每个 EU 有 7 个 thread。下面的例子中 sub-group 大小为 8。

Work-items
Group Size
Threads
Xe-core Util
Xe-core Occu

64x64x128=524288

(R=1) 128

16

16/112=14%

100% with 7 work-groups

64x64x128=524288

(R=2) 128x2

2x16=32

32/112=28.6%

86% with 3 work-groups

64x64x128=524288

(R=3) 128x3

3x16=48

48/112=42.9%

86% with 2 work-groups

64x64x128=524288

(R=4) 128x4

4x16=64

64/112=57%

57% maximum

64x64x128=524288

(R=5) 640+

Fail to launch

Explanation: 当 group size = 128,每个 Sub-slice 每次分配到 128 个 item,因为 sub-group = 8,所以需要 128/8=16 个 EU 完成计算。每个 Sub-slice 上刚好有 16个 EU,因此每个 EU 只需要一个 Thread 即可完成,共用 16 Threads。 所以 Thread 对应的 Xe-core Util 计算为: $\frac{16Thread}{7\frac{Threads}{EU}\times16EU}=\frac{1}{7}=14.2%$ 因为任务总量足够多(64x64x128=524288),因此一次 Dispatch 可以放满所有的 Threads,Xe-core Occupation 为 100%。

当 group size = 512,每个 Sub-slice 每次分配到 512 个 item,因为 sub-group = 8,因此需要 512/8=64 个 EU 完成计算。每个 Sub-slice 上有 16 个 EU,因此每个 EU 需要压 64/16=4 个线程,共用 64 Threads。 所以 Thread 对应的 Xe-core Util 计算为:$\frac{64Thread}{7\frac{Threads}{EU}\times16EU}=\frac{4}{7}=57%$ 即使任务总量足够多,但是每个 workgroup 都至少需要 EU 压上 4 个线程,对于只有 7 个 thread context 的 Xe-LP 而言,无法容纳第二个 workgroup,因此每次 Dispatch 只能分配 1个 workgroup,Xe-core Occupation 为 $\frac{4}{7}=57%$

Conclusion: 因为 Dispatch 会有额外开销,所以尽量提高 Xe-Occupation 以实现最佳性能。

Exercise: ATS-P 有两个 Tile,每个 Tile 上 30 个 Xe-core,每个 Xe-core 上 16 个 EU,即 480 个 EU,共 960 个 EU。

  • 在 ATS-P 上,假设总数据量是 96x512,数据类型是 FP64

    • Q1: 当 workgroup size = 512 时,一个 workgroup 给 Xe-core 上分配几个线程?

    • Q2: 一个 Xe-core 可以分配多少个 workgroup?

    • Q3: 把全部数据分配到 ATS-P 上,需要几次 dispatch?

  • A1: 对于数据类型是 FP64 的可以使用 SIMD16,所以可以设置 sub-group size=16,每次可以执行 512/16=32 个线程。

  • A2: ATS-P 每个 Xe-core 上一共有 16*8=128 个线程,因此可以分配 128/32=4 个 work-group。

  • A3: 数据一共需要 96x512/512=96 个 work-group。每次每个 Xe-core 可以分配4个,一次可以分配 2*30*4=240 个 work-group。因此理论上一次可以分配完。

SLM (Shared Local Memory)

  • 所有 work item 之间通信/共享:使用 global memory,带宽低延迟高。

  • 一个 subgroup(EU) 里面的 work item 可以共享数据,但是范围太小。

  • 一个 workgroup(Xe-core) 里可以共享数据,使用 SLM。 ATS-P 上有 64K 大小的 SLM。

  • SLM 是显式的调用,通过 sycl::malloc_shared,或者 accessor<..., target::local>

  • 可以根据 work item 使用的空间大小确定一个 Xe-core 上可以放的 work group size。例如一个 work item 使用 512B 的数据的话,workgroup size最大为 65536/512=128。此时 thread context 无法使用满。

  • 如何设置每个 work item 使用的数据大小来填满 thread context?

    • Xe-core 有 16EU x 8 Threads = 128 Threads,每个 Thread 可以处理 SIMD16 的 FP32/FP64,因此设置 sub-group size 为 16,则可以处理 128x16=2048 个 work-item。 65536/2048=32B/work item。

  • SLM Bank Conflict: SLM 上的 Cache Line 也是 64 字节,被分为 16 个 Bank,每个 Bank 有 4 个字节。16 个 work item可以并行访问 16 个 Bank。但是访问同一 Bank 里面的不同地址会导致 Bank Conflict。

Multi-card usage

See Reference:Considerations for programming to multi-tile and multi-card under Level-Zero backend. Example of fetching all available devices in machine(here we only collect level-zero devices):

L6.1 Report

  1. 每一页写优化前后效果

  2. 使用的工具分析需要贴图

  3. 新设立 oneapi 奖项

L6.2 Optimization Examples

PAC2021 初赛优化样例

向量化 report

通过 -qopt-report=5 生成向量化报告,通过报告发现向量化不理想,于是对函数进行拆分,再继续通过 -qopt-zmm-usage=high 编译强制编译器使用高位宽指令集。

生成报告步骤:

  1. icpx -fiopenmp -fopenmp-target=spir64 -qopt-report=5 foo.c 生成两个 yaml 文件,其中一个名为 foo.opt.yaml

  2. 使用 oneapi 自带的 opt-viewer.py 生成 html 格式的文件:opt-viewer.py foo.opt.yaml 输出 foo.c.html

  3. 使用 web 浏览器可以打开查看报告。

MPI+OMP 混合并行

基于平台提供的众核的特点,使用混合并行,每个 MPI 程序每次处理一张图片,图片内部使用 OMP 进行并行,最后通过 Reduction 归约操作汇总到一个进程中写入结果文件。

GPU Offload 样例

  • 对于需要在 GPU 上使用 openmp 的程序而言,在编译的时候需要加入 -fiopenmp -fopenmp-target=spir64 的选项。

  • 对于 omp 难以调试的问题可以使用环境变量 LIBOMPTARGET_DEBUG=1/2 来开启 debug 级别 log。

OpenMP 制导语句中的 map 表示数据的传输方向(一维数组):

  1. to: copy to device

  2. from: copy back to host

  3. tofrom: copy to and back

  4. alloc: allocate on device

  5. release: release the data allocated on device

对于高维数组而言:

FFT offloading

异步计算

通过 ompnowait 可以实现派发任务到 target device 后,CPU 和 GPU 同时计算。 通过 depend 实现依赖控制。

Others

Using DPC++ with MPI

See reference.

The End

Last updated

Was this helpful?