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-O2vs-O3: in some scenarios,O2performs better thanO3-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:
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 spaceid: index a single instance in parallel executionitem: 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 图中创建数据依赖关系,以在内核执行中进行排序
对于下面的例子而言,accessor 与 handler 做绑定,handler 与 queue 绑定,决定所在的设备,于是 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
Frequently used commands:
sycl-ls: listing available devicesexport SYCL_DEVICE_FILTER={CPU|GPU|FPGA}
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 viaDirect Memory Access.sycl::malloc_shared: allocate memory which can migrate between device and host by program automatically and implicitly. (Needwaitto ensure the only access from host or device)
Example:
data dependency
in_orderfeature will make all task serialized, avoiding the data race.
Example:
depends_oncan avoid making the situation the extreme by serializing all tasks.
Example:
Sub Groups
See Reference.

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有助于将执行映射到低级硬件,并可以帮助实现更高的性能。

工作组
work-group中的所有工作会被安排到一个子切片SubSlice中,该子切片拥有自己的本地内存SubSlice Local Memory,比访问 L3 Cache 快很多。Sub-group中的所有工作项都在单个Execution Units线程上执行。Sub-group中的每个工作项都映射到SIMD通道。
Why Sub-group?
work-iteminSub-groupcan communicate with each other usingshuffle operations, avoiding accessing local or global memory, providing higher performance.work-iteminSub-groupcan accesssub-group group algorithmwhich 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-groupget_local_range()returns the size(s) of work-groupget_global_id()returns the index(es) of the work-item within the global sizeget_global_range()returns the global sizeget_group_id()returns the index(es) of the work-groupget_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。
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
每一页写优化前后效果
使用的工具分析需要贴图
新设立 oneapi 奖项

L6.2 Optimization Examples
PAC2021 初赛优化样例
向量化 report
通过 -qopt-report=5 生成向量化报告,通过报告发现向量化不理想,于是对函数进行拆分,再继续通过 -qopt-zmm-usage=high 编译强制编译器使用高位宽指令集。
生成报告步骤:
icpx -fiopenmp -fopenmp-target=spir64 -qopt-report=5 foo.c生成两个yaml文件,其中一个名为foo.opt.yaml。使用
oneapi自带的opt-viewer.py生成html格式的文件:opt-viewer.py foo.opt.yaml输出foo.c.html使用
web浏览器可以打开查看报告。
MPI+OMP 混合并行
基于平台提供的众核的特点,使用混合并行,每个 MPI 程序每次处理一张图片,图片内部使用 OMP 进行并行,最后通过 Reduction 归约操作汇总到一个进程中写入结果文件。
GPU Offload 样例
对于需要在 GPU 上使用
openmp的程序而言,在编译的时候需要加入-fiopenmp -fopenmp-target=spir64的选项。对于
omp难以调试的问题可以使用环境变量LIBOMPTARGET_DEBUG=1/2来开启 debug 级别 log。
OpenMP 制导语句中的 map 表示数据的传输方向(一维数组):
to: copy to devicefrom: copy back to hosttofrom: copy to and backalloc: allocate on devicerelease: release the data allocated on device
对于高维数组而言:
FFT offloading
异步计算
通过 omp 的 nowait 可以实现派发任务到 target device 后,CPU 和 GPU 同时计算。
通过 depend 实现依赖控制。
Others
Using DPC++ with MPI
See reference.
The End
Last updated
Was this helpful?