找回密码
 立即注册
首页 业界区 业界 MPK(Mirage Persistent Kernel)源码笔记(3)--- 系统 ...

MPK(Mirage Persistent Kernel)源码笔记(3)--- 系统接口

谭皎洁 前天 23:20
MPK(Mirage Persistent Kernel)源码笔记(3)--- 系统接口


目录

  • MPK(Mirage Persistent Kernel)源码笔记(3)--- 系统接口

    • 0x00 概述
    • 0x01 流程
    • 0x02 初始化
    • 0x03 定义计算图
    • 0x04 编译
    • 0x05 执行
    • 0xFF 参考


0x00 概述

因为转译系统需要通过persistent_kernel.py来完成,所以我们先介绍persistent_kernel.py。
persistent_kernel.py是 Persistent Kernel的Python接口,本质是Python到CUDA持久化内核系统的桥梁,允许用户用python定义复杂的计算图,然后在GPU上高效执行。主要功能包括:

  • 持久化内核管理。提供了 PersistentKernel 作为接口类来管理和执行持久化CUDA内核。
  • 内核编译。将Python定义的计算图编译为CUDA代码并生成共享库。集成了nvcc编译器来编译生成CUDA代码。
  • 内核执行。提供接口来初始化、启动和执行持久化内核。
此外,在 HARD_CODE 定义的C函数是底层入口点,具体如下:

  • init_func:初始化内核。
  • launch_func:启动内核执行。会调用到 launch_persistent_kernel。
  • finalize_func:清理和终止内核。
0x01 流程

persistent_kernel.py的工作流程如下:

  • 初始化:创建 PersistentKernel  类。
  • 定义计算图:使用各种layer方法(如embed_layer、attention_layer等)定义计算图。
  • 编译。调用compile()方法生成和编译CUDA内核。

    • 生成任务图。
    • 创建CUDA代码。
    • 调用nvcc编译器。
    • 创建Python绑定模块

  • 执行:调用call()方法启动内核执行。 self.launch_func()
  • 清理:调用finalize()方法或者自动析构。
具体如下图所示。
1.jpeg

0x02 初始化

初始化函数会创建 PersistentKernel  类。
因为此处只是系统接口,大部分有意义的工作在C++代码中实现,因此此处略过。
  1. class PersistentKernel:
  2.     def __init__(
  3.         self,
  4.         world_size: int,
  5.         mpi_rank: int,
  6.         num_workers: int,
  7.         num_local_schedulers: int,
  8.         num_remote_schedulers: int,
  9.         max_seq_length: int,
  10.         eos_token_id: int64,
  11.         meta_tensors: list[torch.Tensor],
  12.         profiler_tensor: torch.Tensor,
  13.         spec_decode_config: SpecDecodeConfig
  14.     ):
  15.         self.__finalized__ = False
  16.         self._is_compiled = False
  17.         self.world_size = world_size
  18.         self.mpi_rank = mpi_rank
  19.         self.num_workers = num_workers
  20.         self.num_local_schedulers = num_local_schedulers
  21.         self.num_remote_schedulers = num_remote_schedulers
  22.         self.max_seq_length = max_seq_length
  23.         self.eos_token_id = eos_token_id
  24.         self.kn_graph = KNGraph(CyKNGraph(disable_fingerprint=True))
  25.         self.meta_tensors = meta_tensors
  26.         self.profiler_tensor = profiler_tensor
  27.         self.use_nvshmem = True if world_size > 1 else False
  28.         self.spec_decode_config = spec_decode_config
  29.         self._spec_decode_handlers = {
  30.             "promptlookup": self.prompt_lookup_spec_handler,
  31.         }
  32.         self._spec_verify_handlers = {
  33.             "promptlookup": self.prompt_lookup_verify_handler,
  34.         }
复制代码
0x03 定义计算图

persistent_kernel.py 使用各种layer方法(如embed_layer、attention_layer等)定义计算图。简易流程如下:
2.png

对应的代码举例如下:
  1.     def attach_input(self, torch_tensor: torch.Tensor, name: str = None) -> DTensor:
  2.         """
  3.         将PyTorch张量附加到计算图,创建对应的DTensor(分布式张量)。
  4.         
  5.         参数:
  6.             torch_tensor: 待附加的PyTorch张量
  7.             name: 张量名称(必须指定)
  8.         返回:
  9.             与输入张量关联的DTensor实例
  10.         说明:
  11.             仅支持行优先(row-major)内存布局,通过步长校验确保布局正确性
  12.         """
  13.         # 提取张量维度与步长信息
  14.         dims = tuple([d for d in torch_tensor.shape])
  15.         strides = tuple([s for s in torch_tensor.stride()])
  16.         
  17.         # 校验是否为行优先布局(高维步长 = 低维步长 × 低维尺寸)
  18.         for d in range(len(dims) - 1):
  19.             assert strides[d] == strides[d + 1] * dims[d + 1]
  20.         
  21.         # 转换PyTorch数据类型为框架内部 dtype
  22.         dtype = convert_torch_type_to_dtype(torch_tensor.dtype)
  23.         
  24.         # 创建输入张量节点
  25.         t = self.kn_graph.new_input(dims=dims, strides=strides, dtype=dtype)
  26.         
  27.         # 断言名称非空(当前实现限制)
  28.         assert name is not None
  29.         
  30.         # 将DTensor与PyTorch张量绑定,并注册到计算图
  31.         self.kn_graph.attach_torch_tensor(t, torch_tensor, name)
  32.         return t
  33.     def new_tensor(
  34.         self,
  35.         dims: tuple,
  36.         strides: tuple = None,
  37.         dtype: dtype = bfloat16,
  38.         name: str = None,
  39.         io_category: str = "cuda_tensor",
  40.     ) -> DTensor:
  41.         """
  42.         创建新的DTensor并根据IO类别附加到计算图。
  43.         
  44.         参数:
  45.             dims: 张量维度元组
  46.             strides: 步长元组(默认自动按行优先计算)
  47.             dtype: 数据类型(默认bfloat16)
  48.             name: 张量名称(必须指定)
  49.             io_category: IO类别("cuda_tensor"或"nvshmem_tensor")
  50.         返回:
  51.             新创建的DTensor实例
  52.         说明:
  53.             支持CUDA本地张量与NVSHMEM分布式张量两种类型
  54.         """
  55.         # 若指定步长,校验是否为行优先布局
  56.         if strides is not None:
  57.             for d in range(len(dims) - 1):
  58.                 assert strides[d] == strides[d + 1] * dims[d + 1]
  59.         
  60.         # 创建张量节点
  61.         t = self.kn_graph.new_input(dims=dims, strides=strides, dtype=dtype)
  62.         
  63.         # 断言名称非空(当前实现限制)
  64.         assert name is not None
  65.         
  66.         # 根据IO类别绑定张量到计算图
  67.         if io_category == "cuda_tensor":
  68.             self.kn_graph.attach_cuda_tensor(t, name)  # 绑定CUDA张量
  69.         elif io_category == "nvshmem_tensor":
  70.             self.kn_graph.attach_nvshmem_tensor(t, name)  # 绑定NVSHMEM分布式张量
  71.         else:
  72.             raise RuntimeError(f"Invalid io_category: {io_category}")
  73.         return t
  74.     def fuse_tensors(
  75.         self, inputs: list[DTensor], fused_dim: int, num_groups: int, name: str = None
  76.     ) -> DTensor:
  77.         """
  78.         融合多个张量到单个张量(当前仅支持第0维融合)。
  79.         
  80.         参数:
  81.             inputs: 待融合的DTensor列表
  82.             fused_dim: 融合维度(必须为0)
  83.             num_groups: 分组数量
  84.             name: 融合后张量名称
  85.         返回:
  86.             融合后的DTensor实例
  87.         """
  88.         # 当前仅支持第0维融合
  89.         assert fused_dim == 0
  90.         
  91.         # 调用计算图的张量融合接口
  92.         t = self.kn_graph.fuse_tensors(inputs, fused_dim, num_groups, name)
  93.         return t
  94.     def embed_layer(
  95.         self,
  96.         input: DTensor,  # 输入张量 [batch_size, num_spec_tokens]
  97.         weight: DTensor,  # 嵌入权重 [vocab_size, hidden_size]
  98.         output: DTensor,  # 输出张量 [batch_size, hidden_size]
  99.         grid_dim: tuple,  # CUDA网格维度
  100.         block_dim: tuple,  # CUDA块维度
  101.         input_source: int = 0,  # 输入源类型(0: 全 tokens, 1: 输入 token)
  102.     ):
  103.         """
  104.         定义嵌入层计算,将输入张量通过嵌入权重映射到隐藏空间。
  105.         
  106.         参数:
  107.             input: 输入张量
  108.             weight: 嵌入权重张量
  109.             output: 输出张量(用于存储结果)
  110.             grid_dim: CUDA kernel的网格维度
  111.             block_dim: CUDA kernel的块维度
  112.             input_source: 输入源类型标记
  113.         说明:
  114.             内部创建线程块图(TBGraph),定义输入输出映射关系,并注册为"embedding"任务
  115.         """
  116.         # 创建线程块图(CyTBGraph为底层实现,64为共享内存大小)
  117.         tb_graph = TBGraph(CyTBGraph(grid_dim, block_dim, 1, 64))
  118.         
  119.         # 定义输入输出张量的维度映射规则
  120.         tb_graph.new_input(input, (-1, 1, -1), -1, True)  # 输入张量维度映射
  121.         tb_graph.new_input(weight, (1, -1, -1), -1, True)  # 权重张量维度映射
  122.         tb_graph.new_input(output, (1, 0, -1), -1, True)  # 输出张量维度映射
  123.         
  124.         # 将张量与线程块图关联
  125.         self.kn_graph.customized([input, weight, output], tb_graph)
  126.         
  127.         # 注册嵌入层任务,附加输入源参数
  128.         self.kn_graph.register_task(tb_graph, "embedding", [input_source])
  129.     def rmsnorm_linear_layer(
  130.         self,
  131.         input: DTensor,  # 输入张量
  132.         weight_norm: DTensor,  # 归一化权重
  133.         weight_linear: DTensor,  # 线性层权重
  134.         output: DTensor,  # 输出张量
  135.         grid_dim: tuple,  # CUDA网格维度
  136.         block_dim: tuple,  # CUDA块维度
  137.     ):
  138.         """
  139.         定义RMS归一化+线性变换组合层。
  140.         
  141.         参数:
  142.             input: 输入张量(2D)
  143.             weight_norm: RMS归一化权重(2D)
  144.             weight_linear: 线性层权重(2D)
  145.             output: 输出张量(2D)
  146.             grid_dim: CUDA kernel的网格维度
  147.             block_dim: CUDA kernel的块维度
  148.         说明:
  149.             先对输入执行RMS归一化,再通过线性层变换,输出结果存储到output
  150.         """
  151.         # 校验输入张量维度(当前仅支持2D张量)
  152.         assert input.num_dims == 2
  153.         assert weight_linear.num_dims == 2
  154.         assert output.num_dims == 2
  155.         
  156.         # 创建线程块图
  157.         tb_graph = TBGraph(CyTBGraph(grid_dim, block_dim, 1, 64))
  158.         
  159.         # 定义输入输出维度映射
  160.         tb_graph.new_input(input, (-1, -1, -1), 1, True)  # 输入张量
  161.         tb_graph.new_input(weight_norm, (-1, -1, -1), 0, True)  # 归一化权重
  162.         tb_graph.new_input(weight_linear, (0, -1, -1), 1, True)  # 线性层权重
  163.         tb_graph.new_input(output, (1, -1, -1), -1, True)  # 输出张量
  164.         
  165.         # 关联张量与线程块图
  166.         self.kn_graph.customized([input, weight_norm, weight_linear, output], tb_graph)
  167.         
  168.         # 注册RMS归一化+线性层任务
  169.         self.kn_graph.register_task(tb_graph, "rmsnorm_linear")
  170.     def attention_layer(
  171.         self,
  172.         input: DTensor,  # 输入张量 (batch_size, fused_outdim / world_size)
  173.         k_cache: DTensor,  # K缓存 (batch_size, seq_len, kv_heads, head_dim)
  174.         v_cache: DTensor,  # V缓存 (batch_size, seq_len, kv_heads, head_dim)
  175.         q_norm: DTensor,  # Q归一化权重 (可选)
  176.         k_norm: DTensor,  # K归一化权重 (可选)
  177.         cos_pos_embed: DTensor,  # 余弦位置编码 (可选)
  178.         sin_pos_embed: DTensor,  # 正弦位置编码 (可选)
  179.         output: DTensor,  # 输出张量 (batch_size, hidden_size / world_size)
  180.         grid_dim: tuple,  # CUDA网格维度
  181.         block_dim: tuple,  # CUDA块维度
  182.     ):
  183.         """
  184.         定义注意力层计算,支持 rotary 位置编码与 Q/K 归一化。
  185.         
  186.         参数:
  187.             input: 输入张量(2D)
  188.             k_cache: 键缓存张量(4D)
  189.             v_cache: 值缓存张量(4D)
  190.             q_norm: Q归一化权重(可选,1D)
  191.             k_norm: K归一化权重(可选,1D)
  192.             cos_pos_embed: 余弦位置编码(可选,2D)
  193.             sin_pos_embed: 正弦位置编码(可选,2D)
  194.             output: 输出张量(2D)
  195.             grid_dim: CUDA kernel的网格维度
  196.             block_dim: CUDA kernel的块维度
  197.         说明:
  198.             自动检测是否启用 rotary 编码与 Q/K 归一化,动态调整计算逻辑
  199.         """
  200.         # 校验输入输出张量维度
  201.         assert input.num_dims == 2  # (batch_size, fused_outdim / world_size)
  202.         assert output.num_dims == 2  # (batch_size, hidden_size / world_size)
  203.         assert k_cache.num_dims == 4  # (batch_size, seq_len, kv_heads, head_dim)
  204.         assert v_cache.num_dims == 4  # (batch_size, seq_len, kv_heads, head_dim)
  205.         
  206.         # 提取注意力头相关参数
  207.         head_dim = k_cache.dim(3)  # 头维度
  208.         num_kv_heads = k_cache.dim(2)  # KV头数量
  209.         num_q_heads = output.dim(1) // head_dim  # Q头数量
  210.         
  211.         # 检测是否启用 rotary 位置编码
  212.         rotary_embed = 0
  213.         if cos_pos_embed is not None or sin_pos_embed is not None:
  214.             assert cos_pos_embed.num_dims == 2  # (seq_len, head_dim)
  215.             assert sin_pos_embed.num_dims == 2  # (seq_len, head_dim)
  216.             assert cos_pos_embed.dim(1) == head_dim
  217.             assert sin_pos_embed.dim(1) == head_dim
  218.             rotary_embed = 1  # 标记启用rotary编码
  219.         
  220.         # 检测是否启用Q/K归一化
  221.         qk_norm = 0
  222.         if q_norm is not None or k_norm is not None:
  223.             assert q_norm.num_dims == 1  # (head_dim)
  224.             assert k_norm.num_dims == 1  # (head_dim)
  225.             qk_norm = 1  # 标记启用Q/K归一化
  226.             assert q_norm.dim(0) == head_dim
  227.             assert k_norm.dim(0) == head_dim
  228.         
  229.         # 注意力层参数列表
  230.         params = [num_q_heads, num_kv_heads, qk_norm, rotary_embed]
  231.         
  232.         # 创建线程块图
  233.         tb_graph = TBGraph(CyTBGraph(grid_dim, block_dim, 1, 64))
  234.         
  235.         # 定义输入输出维度映射
  236.         tb_graph.new_input(input, (0, 1, -1), -1, True)  # 输入张量
  237.         tb_graph.new_input(k_cache, (0, 2, -1), 1, True)  # K缓存
  238.         tb_graph.new_input(v_cache, (0, 2, -1), 1, True)  # V缓存
  239.         tb_graph.new_input(q_norm, (-1, -1, -1), -1, True)  # Q归一化权重
  240.         tb_graph.new_input(k_norm, (-1, -1, -1), -1, True)  # K归一化权重
  241.         tb_graph.new_input(cos_pos_embed, (-1, -1, -1), -1, True)  # 余弦位置编码
  242.         tb_graph.new_input(sin_pos_embed, (-1, -1, -1), -1, True)  # 正弦位置编码
  243.         tb_graph.new_input(output, (0, 1, -1), -1, True)  # 输出张量
  244.         
  245.         # 关联所有张量与线程块图
  246.         self.kn_graph.customized(
  247.             [
  248.                 input,
  249.                 k_cache,
  250.                 v_cache,
  251.                 q_norm,
  252.                 k_norm,
  253.                 cos_pos_embed,
  254.                 sin_pos_embed,
  255.                 output,
  256.             ],
  257.             tb_graph,
  258.         )
  259.         
  260.         # 注册注意力层任务,附加参数
  261.         self.kn_graph.register_task(tb_graph, "attention", params)
复制代码
0x04 编译

persistent_kernel.py的compile 函数主要功能是将定义好的内核图(kernel graph)编译成可执行的 CUDA 代码,并生成一个 Python 共享库(.so 文件),以便在 Python 环境中调用执行,具体如下:

  • 生成任务图和 CUDA 代码。

    • 调用 self.kn_graph.generate_task_graph 方法,基于当前定义的内核图(KNGraph)生成任务图(task graph)和对应的 CUDA 代码。这一步会根据图中的操作(如矩阵乘法、元素级运算等)生成优化后的 CUDA 实现。

  • 准备编译环境。

    • 创建临时目录用于存放生成的代码文件和编译产物。
    • 将生成的 CUDA 代码写入 .cu 文件。
    • 将任务图的 JSON 表示写入文件,便于调试或后续分析。

  • 配置编译参数

    • 获取 CUDA 编译器(nvcc)路径。
    • 确定 Python 头文件路径,以便生成的库可以与 Python 交互。
    • 获取 Mirage 框架的头文件和依赖库路径。
    • 如果使用 NVSHMEM(多 GPU 通信库),则还需要配置 NVSHMEM 和 MPI 的头文件及库路径。

  • 执行编译

    • 构建完整的 nvcc 编译命令,包括源文件、包含路径、编译选项、目标架构等。
    • 调用 subprocess.check_call 执行编译命令,生成一个 Python 可导入的共享库(.so 文件)。

  • 加载编译结果

    • 使用 importlib.util.spec_from_file_location 和 importlib.util.module_from_spec动态加载编译生成的 .so 文件作为 Python 模块。
    • 从加载的模块中获取初始化、执行和终结函数(init_func, launch_func, finalize_func),并保存为 PersistentKernel 对象的成员变量,供后续调用。

流程图如下:
3.png

具体代码如下:
  1. def compile(
  2.     self,
  3.     **kwargs,
  4. ):      
  5.     # 从关键字参数中获取输出目录,默认为None
  6.     output_dir = kwargs.get("output_dir", None)
  7.     # 获取Mirage相关的核心路径(根目录、包含目录、依赖目录)
  8.     MIRAGE_ROOT, INCLUDE_PATH, DEPS_PATH = get_key_paths()
  9.     # 创建临时目录用于存放编译过程中的中间文件
  10.     tempdir_obj = tempfile.TemporaryDirectory()
  11.     tempdir = tempdir_obj.name
  12.     # 生成任务图:根据GPU数量和当前GPU ID划分计算任务
  13.     results = self.kn_graph.generate.generate_task_graph(num_gpus=self.world_size, my_gpu_id=self.mpi_rank)
  14.     # 定义CUDA代码和编译产物的临时路径
  15.     cuda_code_path = os.path.join(tempdir, "test.cu")  # 生成的CUDA源代码路径
  16.     so_path = os.path.join(tempdir, "test.cpython-38-x86_64-linux-gnu.so")  # 编译后的的共享库路径
  17.     # 定义任务图JSON文件的临时路径
  18.     json_file_path = os.path.join(tempdir, "task_graph.json")
  19.     # 将任务图数据写入JSON文件
  20.     with open(json_file_path, "w") as f:
  21.         f.write(results["json_file"])
  22.     # 将生成的CUDA代码与硬编码补充内容合并后写入文件
  23.     with open(cuda_code_path, "w") as f:
  24.         f.write(results["cuda_code"] + HARD_CODE)
  25.         
  26.     # 若指定了输出目录,将生成的CUDA代码和JSON文件复制到该目录
  27.     if output_dir is not None:
  28.         os.makedirs(output_dir, exist_ok=True)  # 确保输出目录存在(已存在则不报错)
  29.         shutil.copy(cuda_code_path, os.path.join(output_dir, "test.cu"))  # 复制CUDA代码
  30.         shutil.copy(json_file_path, os.path.join(output_dir, "task_graph.json"))  # 复制任务图JSON
  31.     # 检查nvcc(CUDA编译器)是否存在
  32.     cc = shutil.which("nvcc")
  33.     if cc is None:
  34.         # 若未找到nvcc,抛出运行时错误提示用户安装CUDA
  35.         raise RuntimeError(
  36.             "nvcc not found. Please make sure you have installed CUDA."
  37.         )
  38.     # 确定Python的默认安装路径方案(适配不同Python版本的API差异)
  39.     # Python 3.10及以上版本使用get_default_scheme方法
  40.     if hasattr(sysconfig, "get_default_scheme"):
  41.         scheme = sysconfig.get_default_scheme()
  42.     else:
  43.         # 旧版本Python使用内部方法_get_default_scheme
  44.         scheme = sysconfig._get_default_scheme()
  45.     # 修正Debian系统中的路径方案,确保与系统Python兼容
  46.     if scheme == "posix_local":
  47.         scheme = "posix_prefix"
  48.     # 获取Python的头文件包含目录(用于编译时链接Python库)
  49.     py_include_dir = sysconfig.get_paths(scheme=scheme)["include"]
  50.     # 从环境变量中获取Mirage的安装路径(若已设置)
  51.     if "MIRAGE_HOME" in os.environ:
  52.         MIRAGE_HOME_PATH = os.environ.get("MIRAGE_HOME")
  53.     # 初始化NVSHMEM和MPI相关的路径变量(用于分布式通信)
  54.     NVSHMEM_INC_PATH = None  # NVSHMEM头文件目录
  55.     NVSHMEM_LIB_PATH = None  # NVSHMEM库文件目录
  56.     MPI_INC_PATH = None  # MPI头文件目录
  57.     MPI_LIB_PATH = None  # MPI库文件目录
  58.     # 若启用NVSHMEM(NVIDIA共享内存库),配置其相关路径
  59.     if self.use_nvshmem:
  60.         # 配置NVSHMEM头文件路径
  61.         if "NVSHMEM_INC_PATH" in os.environ:
  62.             # 优先使用环境变量中指定的路径
  63.             NVSHMEM_INC_PATH = os.environ.get("NVSHMEM_INC_PATH")
  64.             header_file_path = os.path.join(NVSHMEM_INC_PATH, "nvshmem.h")
  65.         else:
  66.             # 未指定则使用默认路径
  67.             NVSHMEM_INC_PATH = "/usr/include/nvshmem_12/"
  68.             header_file_path = os.path.join(NVSHMEM_INC_PATH, "nvshmem.h")
  69.         # 配置NVSHMEM库文件路径
  70.         if "NVSHMEM_LIB_PATH" in os.environ:
  71.             NVSHMEM_LIB_PATH = os.environ.get("NVSHMEM_LIB_PATH")
  72.             lib_file_path = os.path.join(NVSHMEM_LIB_PATH, "libnvshmem.a")
  73.         else:
  74.             NVSHMEM_LIB_PATH = "/usr/lib/x86_64-linux-gnu/"
  75.             lib_file_path = os.path.join(NVSHMEM_LIB_PATH, "libnvshmem.a")
  76.         # 配置MPI头文件路径(NVSHMEM依赖MPI)
  77.         if "MPI_INC_PATH" in os.environ:
  78.             MPI_INC_PATH = os.environ.get("MPI_INC_PATH")
  79.             header_file_path = os.path.join(MPI_INC_PATH, "mpi.h")
  80.         else:
  81.             MPI_INC_PATH = "/usr/include/"
  82.             header_file_path = os.path.join(MPI_INC_PATH, "mpi.h")
  83.         # 配置MPI库文件路径
  84.         if "MPI_LIB_PATH" in os.environ:
  85.             MPI_LIB_PATH = os.environ.get("MPI_LIB_PATH")
  86.             lib_file_path = os.path.join(MPI_LIB_PATH, "libmpi.so")
  87.         else:
  88.             NVSHMEM_LIB_PATH = "/usr/lib/"
  89.             lib_file_path = os.path.join(MPI_LIB_PATH, "libmpi.so")
  90.     # 获取当前GPU的计算能力(如86对应A100,75对应T4等)
  91.     target_cc = (
  92.         torch.cuda.get_device_properties(0).major * 10
  93.         + torch.cuda.get_device_properties(0).minor
  94.     )
  95.     # 生成CUDA编译命令
  96.     cc_cmd = get_compile_command(
  97.         target_cc=target_cc,  # GPU计算能力
  98.         cc=cc,  # nvcc编译器路径
  99.         file_name=cuda_code_path,  # 输入的CUDA源代码
  100.         py_include_dir=py_include_dir,  # Python头文件目录
  101.         mirage_home_path=MIRAGE_HOME_PATH,  # Mirage根目录
  102.         mirage_inc_path=INCLUDE_PATH,  # Mirage头文件目录
  103.         mirage_deps_path=DEPS_PATH,  # Mirage依赖目录
  104.         nvshmem_inc_path=NVSHMEM_INC_PATH,  # NVSHMEM头文件目录
  105.         nvshmem_lib_path=NVSHMEM_LIB_PATH,  # NVSHMEM库目录
  106.         mpi_inc_path=MPI_INC_PATH,  # MPI头文件目录
  107.         mpi_lib_path=MPI_LIB_PATH,  # MPI库目录
  108.         py_so_path=so_path,  # 输出的共享库路径
  109.         profiling=True if self.profiler_tensor is not None else False,  # 是否启用性能分析
  110.         use_nvshmem=self.use_nvshmem,  # 是否使用NVSHMEM
  111.         num_workers=self.num_workers,  # 工作线程数量
  112.         num_local_schedulers=self.num_local_schedulers,  # 本地调度器数量
  113.         num_remote_schedulers=self.num_remote_schedulers,  # 远程调度器数量
  114.     )
  115.     # 执行编译命令,生成共享库
  116.     subprocess.check_call(cc_cmd)
  117.     # 动态导入编译生成的共享库
  118.     import importlib.util
  119.     # 创建模块规格:指定模块名称和共享库路径
  120.     spec = importlib.util.spec_from_file_location("__mirage_launcher", so_path)
  121.     # 从规格创建模块
  122.     mod = importlib.util.module_from_spec(spec)
  123.     # 执行模块加载
  124.     spec.loader.exec_module(mod)
  125.     # 绑定模块中的核心函数(初始化、启动、结束)
  126.     self.init_func = getattr(mod, "init_func")
  127.     self.launch_func = getattr(mod, "launch_func")
  128.     self.finalize_func = getattr(mod, "finalize_func")
  129.     # 打印编译完成提示
  130.     print("Finished megakernel compilation...")
  131.     # 收集元数据张量的内存地址指针
  132.     meta_tensors_ptr = [tensor.data_ptr() for tensor in self.meta_tensors]
  133.     # 获取性能分析缓冲区的内存地址(若启用性能分析)
  134.     profiler_buffer_ptr = (
  135.         self.profiler_tensor.data_ptr() if self.profiler_tensor is not None else 0
  136.     )
  137.     # 调用初始化函数,传入必要的参数
  138.     self.init_func(
  139.         meta_tensors_ptr,  # 元数据张量指针列表
  140.         profiler_buffer_ptr,  # 性能分析缓冲区指针
  141.         self.mpi_rank,  # 当前MPI进程编号
  142.         self.num_workers,  # 工作线程数量
  143.         self.num_local_schedulers,  # 本地调度器数量
  144.         self.num_remote_schedulers,  # 远程调度器数量
  145.         self.max_seq_length,  # 最大序列长度
  146.         self.eos_token_id,  # 结束符token ID
  147.     )
  148.     # 标记编译完成状态
  149.     self._is_compiled = True
复制代码
总的来说,compile 函数的作用是将用户通过 PersistentKernel API 定义的计算图转换为高度优化的 CUDA 代码,并将其编译为可在当前系统上运行的 Python 模块,从而实现高性能的 GPU 计算。编译成功后,用户可以通过调用 init_func, launch_func, finalize_func 来初始化、执行和清理内核。
0x05 执行

persistent_kernel.py调用call()方法启动内核执行。
  1.     def __call__(self, **kwargs):
  2.         self.launch_func()
  3.         if self.profiler_tensor is not None:
  4.             from .profiler_persistent import export_to_perfetto_trace
  5.             export_to_perfetto_trace(
  6.                 self.profiler_tensor, f"mirage_{self.mpi_rank}.perfetto-trace"
  7.             )
复制代码
launch_func()函数会调用launch_persistent_kernel()来启动内核。
  1. static PyObject *launch_func(PyObject *self, PyObject *args) {
  2.   launch_persistent_kernel();
  3.   Py_RETURN_NONE;
  4. }
复制代码
0xFF 参考

如何评价CMU将LLM转化为巨型内核的Mirage Persistent Kernel(MPK)工作?
Mirage: A Multi-Level Superoptimizer for Tensor Programs 简记  尘伊光
OSDI2025论文笔记:Mirage: A Multi-Level Superoptimizer for Tensor Programs  画饼充饥
Mirage: A Compiler for High-Performance Tensor Programs on GPUs
https://mirage-project.readthedocs.io/en/latest/mugraph.html
https://mirage-project.readthedocs.io/en/latest/transpiler.html
https://zhihaojia.medium.com/compiling-llms-into-a-megakernel-a-path-to-low-latency-inference-cf7840913c17
舍弃CUDA编程!CMU等用代码将LLM编译成巨型内核,推理延迟降6.7倍  机器之心Pro

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

昨天 21:56

举报

懂技术并乐意极积无私分享的人越来越少。珍惜
您需要登录后才可以回帖 登录 | 立即注册