首页 / 资讯中心 / 文章详情

【学习】【NV】基于pipeline的生产者与消费者

【学习】【NV】基于pipeline的生产者与消费者 ★ FEATURED ARTICLE
0. Cutlass 模型Pipeline 是什么一段环形 bufferN 个 stage 一组 mbarrier。Producer 负责把数据写进 bufferConsumer 负责把数据读出来用。两者通过 barrier 互相等待不直接同步。四个动作记住这四个词所有阶段都围绕它们动作角色阻塞?语义acquireProducer阻塞等 buffer 空消费者已用完拿到写入权commitProducer非阻塞写完了通知消费者可以读waitConsumer阻塞等 buffer 满生产者已写好拿到读取权releaseConsumer非阻塞读完了通知生产者可以重写1. 阶段一旧 APIC显式 PipelineState适合C 直接写 kernel、熟悉cutlass::PipelineAsyncT的开发者。状态stage/phase由你自己持有并推进。// 4-stage PipelinestaticconstexprintNumStages4;usingMainloopPipelinetypenamecutlass::PipelineAsyncNumStages;usingPipelineStatetypenamecutlass::PipelineStateNumStages;// 2 个 producer 线程 1 个 consumer 线程typenameMainloopPipeline::Params params;params.producer_arv_count2;params.consumer_arv_count1;MainloopPipelinepipeline(shared_storage.storage,params);// Producer 侧thread 0、1if(thread_idx0orthread_idx1){PipelineState smem_pipe_writecutlass::make_producer_start_stateMainloopPipeline();for(;iter0;--iter){pipeline.producer_acquire(smem_pipe_write);// 等 buffer 空// ... 生产数据写 SMEM ...// 若涉及内存写需保证写对 consumer 可见pipeline.producer_commit(smem_pipe_write);// 通知可读smem_pipe_write;// 手动推进 stage核心差异点}}// Consumer 侧thread 2elseif(thread_idx2){PipelineState smem_pipe_read;for(;iter0;--iter){pipeline.consumer_wait(smem_pipe_read);// 等 buffer 满// ... 消费数据读 SMEM ...pipeline.consumer_release(smem_pipe_read);// 通知可重写smem_pipe_read;// 手动推进 stage}}使用者注意make_producer_start_state让管线初始为空时第一次 acquire 直接成功管线非空时可跳过首轮 acquireproducer_commit对 TMA 生产者是 no-opTMA 指令自带事务计数手动smem_pipe_write是最容易写错的一行——这就是新 API 要消灭的东西。2. 阶段二新 APIPipelineProducer / PipelineConsumer适合所有新项目。状态由角色对象内部管理__state方法自动advance()你不再碰 stage 索引。# 创建 5 级流水线pipelinePipelineAsync.create(num_stages5,# 环形 buffer 深度producer_groupproducer_warp,# 生产者协作组warpconsumer_groupconsumer_warp,# 消费者协作组barrier_storagesmem_ptr,# mbarrier 数组所在 SMEM)# 一次拿到两个角色对象producer,consumerpipeline.make_participants()# Producer 侧 foriinrange(num_iterations):handleproducer.acquire_and_advance()# 等 buffer 空 自动推进 stage# 写入流水线 buffer如 TMA 发指令 / 写 SMEMhandle.commit()# 信号buffer 已满# Consumer 侧 foriinrange(num_iterations):handleconsumer.wait_and_advance()# 等 buffer 满 自动推进 stage# 读取流水线 bufferhandle.release()# 信号buffer 已空和阶段一的对照使用者视角的差异旧 API阶段一新 API阶段二pipeline.producer_acquire(state)stateproducer.acquire_and_advance()原子完成pipeline.consumer_wait(state)stateconsumer.wait_and_advance()原子完成状态变量在函数外自己管理状态在对象__state里handle 携带 barriercommit/release 直接调 pipeline 方法handle.commit()/handle.release()句柄驱动出错难定位commit()传错 handle 会断言失败保护性更强3. 阶段三非阻塞与多组编排进阶用法3.1 非阻塞探测try_acquire / try_wait场景等待期间想先干别的如计算元数据、推进另一条流水线。官方示例PipelineProducer / PipelineConsumer 文档原文# Producer 侧先探测再决定阻塞 foriinrange(iterations):try_acquire_tokenproducer.try_acquire()# 非阻塞探测# ... 做与 buffer 无关的独立工作 ...# tokenTrue → 立即返回tokenFalse → 阻塞至 buffer 空handleproducer.acquire_and_advance(try_acquire_token)handle.commit()# Consumer 侧先探测再决定阻塞 foriinrange(iterations):try_wait_tokenconsumer.try_wait()# 非阻塞探测# ... 做与 buffer 无关的独立工作 ...handleconsumer.wait_and_advance(try_wait_token)handle.release()3.2 多组多深度流水线PipelineOrder场景长序列链式 GEMM、专家多阶段需要按 group 排序后一组等前一组完成。官方示例原文# 3 组 × 每组 2 个 stagepipeline_orderPipelineOrder.create(barrier_storagesmem_ptr,depth2,# 每组 stage 数length3,# 组总数group_id0,# 当前组 ID0/1/2producer_groupproducer_warp,)# 生产者-消费者通用循环每个 stage 先等上一组再干活再发信号forstageinrange(num_stages):pipeline_order.wait()# 等前一 group 完成# ... 当前 stage 的工作生产或消费...pipeline_order.arrive()# 向后一 group 发完成信号3.3 角色复用与退出安全# clone多 warp 共享同一状态副本如 2 个 producer warp 各持一个 cloneproducer2producer.clone()# tailkernel 退出前调用避免 mbarrier arrive 信号悬空producer.tail()4. 阶段四专用 Pipeline 类选型TMA/UMMA/CLC/多消费场景选择表我是什么生产者 我是什么消费者→ 用哪个类。我的生产者我的消费者用这个类典型算子cp.asyncHopper 手动拷贝普通线程PipelineCpAsyncHopper load mainloopTMA搬运 A/B 分片普通线程PipelineTmaAsyncHopper GEMM mainloopTMA搬运UMMAtcgen05计算PipelineTmaUmmaBlackwell GEMM mainloop普通线程写UMMA消费PipelineAsyncUmmaBlackwell 输入融合UMMA生产accumulator普通线程PipelineUmmaAsyncBlackwell 累加/取回硬件自动信号CLC普通线程PipelineClcFetchAsync动态调度取 work tileTMAUMMA 普通线程双消费PipelineTmaMultiConsumersAsync一份数据两路用epilogue 写回TMA store—PipelineTmaStore配TmaStoreFence多级 epilogue buffer以 Blackwell GEMM 主循环为例PipelineTmaUmma按官方 create 签名整理的使用形态示意pipelinePipelineTmaUmma.create(num_stages4,producer_grouptma_warp,# TMA 生产 warpconsumer_groupmma_warp,# tcgen05/UMMA 消费 warptx_count256*1024,# 每 stage TMA 事务字节数barrier_storagesmem_ptr,cta_layout_vmnkcta_layout,# cluster 布局2-CTA 必填mcast_mode_mn(1,1),# M/N 维多播enable_multicast_signalingTrue,# 自动重算 arrive 数)producer,consumerpipeline.make_participants()# ProducerTMA 发布拷贝指令事务计数由硬件自动 arrivefork_tileinrange(k_tiles):handleproducer.acquire_and_advance()tma.load(tile_a[k_tile],smem_a)# 发 TMA不阻塞handle.commit()# TMA 场景下 commit 为 no-op# Consumertcgen05 MMA 消费fork_tileinrange(k_tiles):handleconsumer.wait_and_advance()mma(smem_a,smem_b,tmem_c)# UMMA 从 SMEM 读、写 TMEMhandle.release()2-CTA 提示PipelineTmaUmma内部用_compute_is_leader_cta/_compute_peer_cta_mask处理 leader/follower 与多播 arrive——使用者只需正确传cta_layout_vmnk不要在代码里手写 CTA 判断。5. 阶段五最新 Task Scheduling声明式 producer/consumer 工作CUTLASS 4.8.x latest 实验模块。不再写循环 acquire/commit而是用装饰器声明producer 干什么、consumer 干什么框架编译期生成同样的 mbarrier PTX零运行时开销。官方示例task_scheduling.resourceslatest 文档原文# 资源一份由 pipeline 守护的共享内存 classMySmem(MemoryResource):item:TaskLocalVariableTaskLocalVariable.uninitialized()def__post_init__(self):self.itemTaskLocalVariable(dtype...,default...)# Consumer 工作从资源读出数据作为数据流 token 返回consumer_work(returnsitem)cute.jitdefload(self,stage_info):returnself.tensor[stage_info.loop_offset]# Producer 工作把上游 token 写进资源producer_workcute.jitdefstore(self,stage_info,*,item):# item 由捕获的 schedule 传入self.tensor[stage_info.loop_offset]item管线配置用工厂方法选择类型PipelineConfig官方cfgPipelineConfig.create_tma_umma_pipeline_cfg(num_stages4,num_bytes256*1024,producer_grouptma_warp,consumer_groupmma_warp,advance_on_waitTrue,# wait 游标与 release 游标分离见下)多命名工作方法schedule-list 标签选择官方consumer_workcute.jitdefk_desc(self,stage_info):...consumer_workcute.jitdefv_desc(self,stage_info):...# schedule 列表里用标签指定调哪个# (smem_kv, ScheduleStage.ConsumerWork, k_desc)advance_on_waitTrue的双游标官方设计说明consumer 可wait 到 buffer N1的同时仍持有 buffer N 未 release——consumer_state在 wait 时推进、consumer_release_state在 release 时推进。适合消费者需要先看下一份、再慢慢还上一份的算子如双缓冲 accumulate。6. 选型清单使用者决策表你的情况选它C 老项目、已用PipelineAsync阶段一可暂不迁移新项目、Python DSL 或 C 新示例阶段二PipelineProducer/Consumer需要等待间隙做独立工作阶段三try_acquire/try_wait链式/多专家流水线排序阶段三PipelineOrderHopper/Blackwell GEMM mainloop阶段四按 TMA/UMMA 角色选类2-CTA tcgen05阶段四PipelineTmaUmma必须传cta_layout_vmnk动态调度取 tile阶段四PipelineClcFetchAsync一份数据两路消费阶段四PipelineTmaMultiConsumersAsync想完全声明式、不写循环阶段五 Task Scheduling实验追求低门槛/可维护阶段二或五7. 常见错误使用者踩坑清单旧 API 忘state→ stage 不推进死锁或读到旧数据新 API 不存在此问题。handle 传错角色→commit()/release()断言失败新 API 的保护机制别 catch 掉。tx_count写错→ TMA 事务计数与 barrier 期望不符consumer 永远等不到。稀疏/条件加载场景不设expected_tx→ 每迭代字节数变化时需动态覆盖PipelineTmaUmma.producer_acquire(expected_tx...)。2-CTA 忘传cta_layout_vmnk/mcast_mode_mn→ leader/peer 信号错乱。kernel 退出前不调tail()→ mbarrier arrive 信号悬空后续 kernel 行为未定义。defer_syncTrue当默认用→ 跳过内置 fencesync 需自行保证内存序仅性能敏感且清楚后果时开启。阶段五里 producer 状态用错→ 官方明确producer state 不重定向每个 producer 任务独立跟踪resource.producer_state。附主要来源NVIDIA CUTLASScpp/pipeline.html4.5.1“Pipeline example”https://docs.nvidia.com/cutlass/4.5.1/media/docs/cpp/pipeline.htmlNVIDIA CUTLASScutlass.pipeline4.7.0https://docs.nvidia.com/cutlass/4.7.0/media/docs/pythonDSL/cute_dsl_api/pipeline.htmlNVIDIA CUTLASStask_scheduling.resourceslatesthttps://docs.nvidia.com/cutlass/latest/media/docs/pythonDSL/ts_api/resources.html
阅读完成 · 觉得有帮助?
咨询建站