缓存敏感矩阵乘法的多级平铺

如何解决缓存敏感矩阵乘法的多级平铺

为了好玩,我正在编写自己的 GeMM 子程序。我已经设法使用 AVX256 内核在 L1 缓存上实现了平铺版本。我知道有一些循环不变量我可以从下面提升。

template<size_t N,size_t P,size_t M>
void intrinsic_multiply(double* A,double* B,void* C) {
    alignas(32) double _A[PACK_SIZE * PACK_SIZE];
    alignas(32) double _B[PACK_SIZE * PACK_SIZE];
    double* _C = (double*)(C);

    constexpr size_t N_rem = N%PACK_SIZE;
    constexpr size_t M_rem = M%PACK_SIZE;
    constexpr size_t P_rem = P%PACK_SIZE;

    constexpr size_t N_spill = N-N_rem+(PACK_SIZE)*(size_t)(N_rem!=0);
    constexpr size_t M_spill = M-M_rem+(PACK_SIZE)*(size_t)(M_rem!=0);
    constexpr size_t P_spill = P-P_rem+(PACK_SIZE)*(size_t)(P_rem!=0);

    for(size_t i = 0; i < N_spill; i += PACK_SIZE) {
        for(size_t j = 0; j < M_spill; j += PACK_SIZE) {
            for(size_t k = 0; k < P_spill; k += PACK_SIZE) {
                pad_cols<N>(A + i + k * N,_A,((size_t)(i+PACK_SIZE>N))*N_rem,((size_t)(k+PACK_SIZE>P))*P_rem);
                pad_cols<P>(B + k + j * P,_B,((size_t)(k+PACK_SIZE>P))*P_rem,((size_t)(j+PACK_SIZE>M))*M_rem);
                macro_kernal_intrinsic<N_spill>(_A,_C + i + (j * N_spill));
            }
        }
    }
}

我很难实现多级缓存平铺,因为缓存不是彼此的倍数。每个缓存的步幅大小的估计计算如下,CACHE_SIZE 以字节为单位给出。

static constexpr size_t L3_CACHESIZE = 6291456;
static size_t L3_STRIDE_SIZE = (size_t)floor((sqrt(L3_CACHESIZE/sizeof(double))));

static constexpr size_t L2_CACHESIZE = 262144;
static size_t L2_STRIDE_SIZE = (size_t)floor((sqrt(L2_CACHESIZE/sizeof(double))));

static constexpr size_t L1_CACHESIZE = 32768;
static size_t L1_STRIDE_SIZE = (size_t)floor((sqrt(L1_CACHESIZE/sizeof(double))));

static constexpr size_t PACK_SIZE = 64;

这给出了 L1 步幅大小为 64 和 L2 步幅大小为 181。显然这些不是彼此的倍数。我有两个选择 -

  1. 每次 L2 迭代只适合 4 个 L1 块,从 0 到 63 到 127。这似乎是我在利用我的 L2 缓存。
  2. 使用整个 L2 缓存并在最后一次迭代中用 0 填充 (64*3-180) 个元素。这会引入很多冗余操作,但只会将 L2 步幅大小减少 1。
  3. 为下一次迭代预取小数块。
  4. 有一种我不知道的规范方法。

在实践中处理不是彼此倍数的块大小的最佳方法是什么?

编辑 - 响应 MSalters:

我跑了

nm -AP -t d --demangle ./src/example-GeMM | grep "intrinsic"

给出

./src/example-GeMM: void intrinsic_multiply<2048ul,3136ul,2240ul>(double*,double*,void*) W 17081 434
./src/example-GeMM: void macro_kernal_intrinsic<2048ul>(double*,double*) W 20409 234
./src/example-GeMM: void micro_kernal_intrinsic<2048ul>(double*,double*) W 21343 454

意味着相关代码部分占用 (234+454+434)/262144

解决方法

我的假设是 L1 被拆分,然后 32Kb 只是 L1d 缓存。 L2 将是统一的,这意味着您不应该将其全部用于您的数据。

另外,我还不会太担心循环提升。有了所有的 constexpr,优化器就有机会发现提升机。一个问题是 C 似乎是输出变量。因为那是 void*,所以它可以作为 size_t 的别名。如果你写了 double* C,它就不能给 size_t 起别名。这是一个例子,类型安全不仅对安全有好处,而且对速度也有好处。

,

请参阅以下论文(以及scholar.google.com 上的类似论文)。我认为这个很适合你的问题。 (顺便说一句,我想你已经使用了这个“_mm256_mul_pd”。)反正你会在互联网上找到pdf(不想在这里链接)。

用于在 GPU 上实现高效 GEMM 的协调平铺和批处理框架, X Li,Y Liang,S Yan,L Jia,Y Li - 2019 年第 24 届研讨会论文集 - dl.acm.org

"基本问题是平铺、批处理以及它们的协同交互。平铺意味着将每个 GEMM 平铺成许多平铺。我们允许不同的 GEMM 有不同的平铺策略,而不是共享统一的平铺策略。如何统一不同的平铺策略进入单个内核是一个挑战。”

"给定一个大小为 M×N×K 的 GEMM,C 矩阵被划分为多个大小为 BY×BX 的瓦片。C 的每个瓦片需要访问大小为 BY×K 的 A 矩阵的整行部分和一个图 1(a) 中大小为 K×BX 的 B 矩阵的整个列部分。但是,A 的整个行带和 B 的列带太大,无法容纳在共享存储器和寄存器文件中。要使用片上内存,沿K维的工作负载必须被划分为许多段,如图1(b)所示。A的行段的每个段称为一个大小为BY×BK的A瓦片,列段的每个段的 B 块称为大小为 BK×BX 的 B 瓦片。最终结果可以通过沿 K 维累加每个线段的部分结果得到。"

评论:我喜欢考虑不同解决方案的想法 - 或者让它“优化”以找到最佳解决方案。 (比如:你为什么要找出哪种瓷砖尺寸效果最好,让计算机找出来。好吧,很明显,编程一个瓷砖尺寸的可变设置更费力)。嗯,这是研究(我只知道你最好按常规顺序存储和访问 RAM 中的数据 - 否则你会看到“缓存未命中”,这可能会导致巨大的性能损失。另一个想法:其他一些进程可能会喜欢也住在缓存中。我不确定你是如何考虑到这一点的 - 它没有被提及。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res