有限差分法——拉普拉斯第4部分

有限差分法——拉普拉斯第4部分
提出了拉普拉斯算子有限差分法的HIP实现,并应用了四种不同的优化。在这些代码修改过程中,观察到由于全局内存的总取数减少,性能得到了逐步提高。然后,应用了进一步的优化,以在512×512×512上达到预期的性能目标MI250X GPU的单个GCD上的512个点网格。下面显示的是最终的HIP内核,称之为内核5:
//平铺因子
#define m 8
// 发布边界
#define LB 256
template <typename T>
__launch_bounds__(LB)
__global__ void laplacian_kernel(T * f, const T * u, int nx, int ny, int nz, T invhx2, T invhy2, T invhz2, T invhxyz2) {
    int i = threadIdx.x + blockIdx.x * blockDim.x;
    int j = m*(threadIdx.y + blockIdx.y * blockDim.y);
    int k = threadIdx.z + blockIdx.z * blockDim.z;
    // 如果此线程位于xz边界上,则退出
    if (i == 0 || i >= nx - 1 ||
        k == 0 || k >= nz - 1)
        return;
    const int slice = nx * ny;
    size_t pos = i + nx * j + slice * k;
    // 每条线在y方向上累积m个模板
    T Lu[m] = {0};
    // 可重用数据的标量
    T center;
    // z - 1, 循环平铺
    for (int n = 0; n < m; n++)
        Lu[n] += u[pos - slice + n*nx] * invhz2;
    // y - 1
    Lu[0]   += j > 0 ? u[pos - 1*nx] * invhy2: 0; // bound check
    // x 方向, 循环平铺
    for (int n = 0; n < m; n++) {
        // x - 1
        Lu[n] += u[pos - 1 + n*nx] * invhx2;
        // x
        center = u[pos + n*nx]; // 储存以供再次使用
        Lu[n] += center * invhxyz2;
        // x + 1
        Lu[n] += u[pos + 1 + n*nx] * invhx2;
        //重用:y+1用于前一个n
        if (n > 0) Lu[n-1] += center * invhy2;
        // 重用:y-1用于下一个n
        if (n < m - 1) Lu[n+1] += center * invhy2;
    }
    // y + 1
    Lu[m-1]  += j < ny - m ? u[pos + m*nx] * invhy2: 0; // 边界检验
    // z + 1, 循环平铺
    for (int n = 0; n < m; n++)
      Lu[n] += u[pos + slice + n*nx] * invhz2;
    // 仅当线程在y边界内时存储
    for (int n = 0; n < m; n++)
      if (n + j > 0 && n + j < ny - 1)
        __builtin_nontemporal_store(Lu[n],&f[pos + n*nx]);
}
}
第4部分的目的是探索此代码在各种AMD GPU和问题大小上的性能。作为本次调查的一部分,将总结拉普拉斯级数,从经验中吸取教训,并提供用户可以应用于类似问题的可能代码优化。的其余部分概述如下:
1)在多个AMD GPU上跨各种问题大小运行Kernel 5
2)检查二级缓存命中性能,并确定其降级背后的根本原因
3)提出规避二级缓存大小限制的技术
4)查看此拉普拉斯级数中进行的所有代码优化。
4.4.1 跨不同硬件和尺寸的性能
除了MI250X GPU,还考虑了以下AMD GPU:
1)RX 6900 XT
2)RX 7900 XTX
3)MI50
4)MI100
5)MI210
6)MI250
将从上述所有GPU上的问题大小nx,ny,nz=256256256开始,对Kernel 5进行缩放研究。每个维度都以256的倍数增加,直到达到全局问题大小nx,ny,nz=1024,1024。回想一下,品质因数(FOM)被定义为有效内存带宽:
theoretical_fetch_size = ((nx * ny * nz - 8 - 4 * (nx - 2) - 4 * (ny - 2) - 4 * (nz - 2) ) * sizeof(double);
theoretical_write_size = ((nx - 2) * (ny - 2) * (nz - 2)) * sizeof(double);
effective_memory_bandwidth = (theoretical_fetch_size + theoretical_write_size) / average_kernel_execution_time;
考虑到需要在整个内存子系统中移动的数据量最少,这是数据传输的平均速率。这里的目标是获得尽可能接近可实现设备HBM带宽的FOM。通过之前在各种线程块大小、启动边界和平铺因子上重新运行Kernel 5的实验,发现以下参数为512× 512× 512上的每个单独GPU提供了最高的FOM点网格,见表4-15。
表4-15 每个单独GPU提供了最高的FOM点网格

GPU

线程块

发布边界

平铺因子

RX 6900 XT

256 x 1 x 1

256

16

RX 7900 XTX

256 x 1 x 1

256

8

MI50

256 x 1 x 1

256

4

MI100

128 x 1 x 1

128

4

MI210

256 x 1 x 1

256

8

MI250

256 x 1 x 1

256

8

MI250X

256 x 1 x 1

256

8

从现在开始,MI250和MI250X GPU上的实验应在单个GCD上进行。描述了各种AMD GPU和问题大小的FOM,如图4-8所示。
 
图4-8 内核5在各种AMD GPU和问题大小上的性能
RX 6900 XT GPU没有足够的内存来执行最大的示例nx,ny,nz=10241924,1024,因此,图4-8中没有显示。RX 6900 XT和RX 7900 XTX GPU上所有尺寸的性能似乎相对一致,而当问题尺寸超过一定阈值时,所有Instinct™GPU的性能都会下降。在接下来的部分中,将研究Instinct™GPU性能下降的原因。
4.4.2 性能下降的根本原因
把注意力重新集中在MI250X GPU上。为了了解性能下降的原因,需要收集rocprof统计数据。将首先收集FETCH_SIZE,并将其与nx、ny、nz的每个组合的theological_FETCH_SIZE进行比较。理想情况下,FETCH_SZIE应等于理论量,因为每个线程加载的元素可以被相邻线程重用多达六次。MI100、MI210、MI250和MI250X GPU都有8MB的L2缓存,每个GCD在计算单元(CU)之间共享,因此,收集L2CacheHit将有助于确定缓存数据的重用程度。说明了在相同问题规模范围内的发现,如图4-9所示。
 
图4-9 在单个MI250X GCD上跨各种问题大小获取内核5的效率和L2缓存命中率
FOM、获取效率和L2缓存命中率之间存在明显的相关性。基线HIP内核表现出50%的获取效率,同时保持了65%的相对较高的L2缓存命中率。相信内核设计存在问题。在最后一个实验中,提取效率和二级缓存命中率都保持在33%左右,表明性能的限制因素是二级缓存的大小。
图4-9中另一个有趣的观察结果是,只有当nx或ny增加时,性能才会下降——nz的增加对性能几乎没有影响。从上面Kernel 5中的线程块配置和网格索引可以看出,在从下一个xy平面获取元素之前,将从全局内存中获取单个xy平面的所有元素。每个线程的模板计算具有-nx*ny、0和nx*ny的z方向步长,因此需要三个xy数据平面。如果三个xy平面太大,无法同时容纳在L2缓存中,那么每个线程都会循环缓存以从全局内存中获取额外的xy平面——例如,当只有一个xy平面适合(或部分适合)L2缓存时,线程需要从全局内存循环通过两个额外的xy面,因此获取效率降至33%。
进行第二个实验来进一步说明这一点。将检查nx和ny的各种值,以在性能开始下降之前确定最佳的z方向步幅。从约1 MB的起始z步长(相当于nx,
ny=512256)开始,nx固定为三个不同的大小,递增地增加ny,直到xy平面超过8 MB(nx*ny*sizeof(double))。如图4-10所示。
 
图4-10 单个MI250X GCD上不同固定nx值的nx*ny步幅增加对性能的影响
无论nx的大小如何,当单个xy平面在内存中超过约2.5 MB时,性能都会下降。事实证明,2.5 MB足够小,可以在8 MB的L2缓存中容纳三个连续的xy平面,这解释了为什么FOM的值更高。然而,步幅越大,缓存中的元素就越多,从而降低了L2CacheHit率,进而降低了FOM。nx*ny超过8 MB后性能水平下降,因为此时只有一个平面适合(或部分适合)缓存——这就是FETCH_SIZE比理论估计值大三倍的地方。
4.4.3关于Radeon™GPU性能的说明
RX 6900 XT和RX 7900 XTX GPU似乎都没有受到图1所示缩放研究的影响。与Instinct™GPU不同,L2缓存是所有CU共享的最后一个缓存,也称为最后一级缓存(LLC),Radeon™GPU具有额外的共享缓存级别:L3缓存也称为无限缓存。两个Radeon™GPU的L3缓存大小分别为128 MB和96 MB,这两个大小都足以容纳大小为nx,ny=10241024的多个xy平面。从这里开始,使用LLC来指代Radeon™GPU的L3缓存和Instinct™GPU的L2缓存。
快速进行一项新的缩放研究,以验证两个Radeon™GPU的LLC确实是更大尺寸性能的限制因素。为了确保不会用完内存,从nx,ny,nz=10241024,32开始,慢慢增加ny的值,如图4-11所示。
 
图4-11 RX 6900 XT和RX 7900 XTX GPU上nx*ny步幅增加对性能的影响
在图4-11中,当xy平面超过某些阈值时,有效存储器带宽仍然会恶化。当xy平面超过32MB时,RX 6900 XT性能会恶化,而当xy平面超出25MB时,RX 7900 XTX性能会恶化。RX 7900 XTX的LLC略小于RX 6900 XT,因此性能在较小的xy平面尺寸下开始下降。无论使用哪种AMD GPU,如果nx*ny太大,性能都会下降。
4.4.4解决缓存大小限制问题
如何解决这些较大问题的缓存大小问题呢?这在MI50等硬件上尤为重要,因为LLC只有4MB的L2缓存。即使nz很小,nx和ny的值很大,L2缓存也可能成为内存流量瓶颈。考虑在MI250X GCD上进行的接下来的几个实验中,nx,ny,nz=10241024102024的时间。以下是解决二级缓存瓶颈的两种可能策略:

 

FOM (GB/s)

取数效率(%)

L2 Cache命中率

内核5 – 基线

555.389

33.1

34.4

内核5 – 128x1x8

934.111

79.8

60.6

有一个显著的改进——FOM提高了68%,提取效率提高到近80%,L2缓存命中率提高到60%以上。修改线程块配置以包括z方向上的元素,使HIP内核能够显著提高其在这些较大问题规模上的性能。然而,提取效率仍有约20%的问题有待解决。
尝试的另一种修改是仍然使用原始的256×1×1,但配置HIP内核的网格启动和内核本身的索引。这需要在主机端和设备端修改代码。对于主机端,修改了网格启动配置:
1. 内核5(之前)
dim3 grid(
    (nx - 1) / block.x + 1,
    (ny - 1) / (block.y * m) + 1,
    (nz - 1) / block.z + 1);
2. 内核6(之后)
dim3 grid(
    (ny - 1) / (block.y * m) + 1,
    (nz - 1) / block.z + 1,
    (nx - 1) / block.x + 1);
第二个更改涉及在HIP内核内配置索引:
3. 内核5(之前)
int i = threadIdx.x + blockIdx.x * blockDim.x;
int j = m*(threadIdx.y + blockIdx.y * blockDim.y);
int k = threadIdx.z + blockIdx.z * blockDim.z;
4. 内核6(之后)
int i = threadIdx.x + blockIdx.z * blockDim.x;
int j = m*(threadIdx.y + blockIdx.x * blockDim.y);
int k = threadIdx.z + blockIdx.y * blockDim.z;
blockIdx.x和blockIdx.y索引分别在x和y方向上跨越pos,将大小为nx*ny的xy平面加载到缓存中。现在,它们分别在y和z方向上跨过pos,这将大小为
blockDim.x*ny的xy平面加载到缓存中。索引排序的洗牌使多个xy平面的较小部分能够填充L2缓存,从而增加了一些模板计算可以有效地重用其他线程块已经获取的所有数据的可能性。以下是性能数据,见表4-16。
表4-16 索引排序的洗牌后,性能数据

 

FOM (GB/s)

提取效率(%)

L2缓存命中率

内核5 – 基线

555.389

33.1

34.4

内核5 – 128x1x8

934.111

79.8

60.6

内核6 –网格索引

1070.04

95.4

66.2

这种修改后的网格索引实际上比之前修改的线程块大小表现更好。提取效率非常接近理论极限,L2缓存命中率略有提高。然而,这种解决方法也不完美——最后一个例子是在blockDim.x=256的情况下运行的,因此每个子xy平面大约占用2 MB的内存,从而仍然允许三个不同的子xy平面放入L2缓存。更大的nx*ny飞机将不可避免地面临与以前相同的问题。
4.4.5 选项2:拆分为子域
另一种更稳健地规避该问题的方法是将整个问题大小拆分为更小的域,以便三个xy平面可以放入L2缓存中并序列化内核启动。当前的xy平面大小nx,ny=10241024是之前考虑的xy平面尺寸nx,ny=512512的4倍,已知可以放入L2缓存三次,因此考虑将这个问题分解为4个子域。
先回到Kernel 5。首先修改主机端代码:
1. 内核5(之前)
dim3 grid(
    (nx - 1) / block.x + 1,
    (ny - 1) / (block.y * m) + 1,
    (nz - 1) / block.z + 1);
...
laplacian_kernel<<<grid, block>>>(d_f, d_u, nx, ny, nz,
    invhx2, invhy2, invhz2, invhxyz2);
2. 内核7(之后)
int bny = (ny - 1) / 4 + 1;
dim3 grid(
    (nx - 1) / block.x + 1,
    (bny - 1) / (block.y * m) + 1,
    (nz - 1) / block.z + 1);
...
for (int i = 0; i < 4; i++)
  laplacian_kernel<<<grid, block>>>(d_f, d_u, nx, ny, nz,
      invhx2, invhy2, invhz2, invhxyz2, bny*i);
对代码进行了三处修改。首先,使用bny=(ny-1)/4+1而不是ny修改了grid.y值,以表明只在y方向上迭代了四分之一的域。接下来,添加一个for循环来启动四个HIP内核,以计算四个子域中的每一个模板。最后,通过添加y方向偏移来修改内核参数。设备内核需要修改如下:
3. 内核5(之前)
__global__ void laplacian_kernel(T * f,
    const T * u, int nx, int ny, int nz,
    T invhx2, T invhy2, T invhz2, T invhxyz2) {
...
int j = m*(threadIdx.y + blockIdx.y * blockDim.y);
4. 内核7(之后)
__global__ void laplacian_kernel(T * f,
    const T * u, int nx, int ny, int nz,
    T invhx2, T invhy2, T invhz2, T invhxyz2, int sy) {
...
int j = sy + m*(threadIdx.y + blockIdx.y * blockDim.y);
内核中的其他一切都可以保持不变。检查了以下性能,见表4-17。
表4-17 内核中的其他一切都可以保持不变,检查了一些性能

 

FOM (GB/s)

取数效率 (%)

L2 Cache命中率

内核5 – 基线

555.389

33.1

34.4

内核5 – 128x1x8

934.111

79.8

60.6

内核6 –网格索引

1070.04

95.4

66.2

内核7 –子域拆分

1175.54

99.6

67.7

FOM甚至比以前更高。提取效率也接近100%,表明已经优化了内存移动。与之前的解决方法不同,这种方法对于处理任何大小的xy平面都是稳健的——xy平面越大,用户可以将问题拆分为的子域就越多。如果内核将在Radeon™和Instinct™GPU上运行,则这种方法特别有用,因为不同的LLC大小会极大地影响性能的截止点。尽管选择在y方向上分割问题大小,但也可以很容易地将这种优化应用于x方向。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/900730.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推荐几本书1《AI芯片开发核心技术详解》、2《智能汽车传感器:原理设计应用》、3《TVM编译器原理与实践》、4《LLVM编译器原理与实践》,谢谢

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该…

WebKit Inside: CSS 的匹配原理

WebKit Inside: CSS 的匹配原理相关文章WebKit Inside: CSS 样式表的解析 WebKit Inside: CSS 样式表的匹配时机 WebKit Inside: Acitvie 样式表 当WebView解析完所有外部与内联样式表,就要进入到CSS样式表的匹配阶段。 1 相关类图 WebKit中参与CSS样式表匹配的主要类如下图所…

助记词-公私钥-子私钥派生-钱包地址原理及实现

0x01.简介 现在各种DEX、钱包插件中的钱包导入及创建,大部分是通过助记词来备份的; 助记词是明文私钥的一种表现形式,最早由BIP39提出,为了帮助用户记住复杂的私钥; 一组助记词可以生成各个链上的公私钥,进而可以算出钱包地址;掌握了助记词,就代表掌握了该组助记词上的…

AI 代理的未来是事件驱动的

AI 代理即将彻底改变企业运营,它们具备自主解决问题的能力、适应性工作流以及可扩展性。但真正的挑战并不是构建更好的模型。 代理需要访问数据、工具,并且能够在不同系统之间共享信息,其输出还需要能被多个服务(包括其他代理)使用。这不是一个 AI 问题,而是一个基础设施…

树莓派 3B + Bookworm:mjpg-streamer 正确安装全流程(原创)

在树莓派 OS Bookworm 版本上安装 mjpg-streamer 并非像旧版本一样简单,许多网上的教程已经过时,甚至存在错误。我在尝试过程中遇到了多个问题,例如依赖库缺失、编译失败等,但最终成功解决并搭建了 远程视频流监控系统。本教程基于 树莓派 3B,整理了一套 完整、可复现 的 …

1.匀速圆周运动

1.平面中的匀速圆周运动 例子:一个物体在半径为r的圆形路径中以恒定大小的速度s移动。 建立一个二维坐标系,物体位于平面上,圆心在原点上。物体的瞬时速度v(t)总是与其运动轨迹相切,所以物体任意时刻的速度与轨迹圆相切,并且速度的大小:$|v(t)|=s$ 下图右侧的两个三角形,…

Fiddler如何抓取HTTPS请求

如果发现fiddler只能抓取http请求,但是抓取不到HTTPS请求,看查看是不是没有勾选解密https流量入口:Tools——>Options——>HTTPS,勾选以下选框设置完成过后可以正常抓取HTTPS的请求了

愿景2025|未来已来 各地未来产业加速布局

各地2025年政府工作报告显示,从东部沿海到中西部内陆,从人工智能到低空经济,从量子科技到生物制造,新兴产业和未来产业的布局正在加速展开,这些产业不仅成为各地抢占发展新赛道的重要抓手,更是推动经济高质量发展的新增长极。

Fiddler工具无法抓取请求的几种原因

1、设置了过滤: fiddler中支持我们设置过滤条件,这样fiddler就不会抓取所有的请求,比如我们要抓取一个指定ip地址的请求,就可以设置对应的过滤信息,但是结束过后可能忘记删除了,导致下一次使用fiddler的时候抓不到请求。 1、首先进入Fiddler界面 2、点击Filters,如果设置…

使用 INFINI Gateway 保护 Elasticsearch 集群之修改查询不合理参数(二)

本文将探讨如何使用 INFINI Gateway 修改查询不合理的参数,此方法同样适用于 Opensearch 和 Easysearch 。 在之前的文章中,我们介绍了如何使用 request_body_json_set 处理器修改不合理的查询参数,本篇将继续探讨如何使用 request_body_regex_replace 处理器修改不合理参数…

11判断

C 语言把任何非零和非空的值假定为 true,把零或 null 假定为 false。判断语句语句 描述if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。if...else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。嵌套 if 语句 您可以在一个…