大多数浏览器和
Developer App 均支持流媒体播放。
-
利用 Metal 3 锁定并优化 GPU 二进制文件
了解如何利用离线编译减少 App 内卡顿,缩短启动时间和加载时间,同时完全在项目构建期间生成您的 GPU 二进制文件。我们还将向您介绍如何通过“Optimize for size”编译器选项改善大型 GPU 程序的总编译时间和二进制文件大小。
资源
相关视频
WWDC23
WWDC22
WWDC21
WWDC20
-
下载
♪ ♪ Galo Avila: 大家好 欢迎大家 我是 Galo Avila GPU 软件开发经理 在本次讲座中 我和 Eylon 将很高兴与大家分享 如何使用 Metal 3 改进 App 的 GPU 二进制生成 首先 我讲一下 如何通过离线编译帮助你 减少 App 掉帧 首次启动和新关卡加载时间 然后 Eylon 将介绍如何通过使用 优化大小的编译器选项 来调整代码扩展转换 并缩短编译时间
离线编译可以让你将 GPU 二进制 生成转移到项目构建期间 为了充分了解采用这种方式可以为你的 Metal 应用程序带来的好处 我首先回顾一下已有的 生成 GPU 二进制的方式 在你的 Metal app 中 GPU 二进制在构建和运行时生成 例如 假设你从源码实例化一个 Metal 库 这将在 App 运行时生成 Apple's Intermediate Representation 也被称为 AIR 这会是一个 CPU 密集型操作 你可以通过将源代码 预编译成 Metal 库 然后从文件实例化 将这一步转移到 App 构建时 一旦将 Metal 库放入内存中 创建包含状态和函数的 管线状态描述符 就是一个轻量级操作 直到开始创建管线状态对象 这会是另一个 CPU 密集型操作 这里会实时生成 GPU 二进制
由于在运行时生成 GPU 二进制 可能是 CPU 密集型操作 Metal 会帮助你加速 管线状态对象的创建 当你实例化 PSO 时 Metal 将你的 GPU 二进制 存储在文件系统缓存中 每创建一个新的 PSO 就会添加所有新生成的函数 因此 之前生成的 任何被引用的二进制 都可以从缓存中加载
Metal 还允许你使用二进制存档 明确控制何时何地 缓存 GPU 二进制 只需使用 PSO 描述符通过存档文件 缓存 GPU 二进制 多少次都可以 这样 创建 PSO 就变成了 一个轻量级操作 二进制存档支持更灵活的缓存 但它们仍然必须在运行时生成 在许多情况下 你真正想要的 是在构建时生成这些存档 现在终于可以实现了 通过离线二进制生成 你可以在项目构建时 指定一个名为 Metal 管线脚本的新产物 与 Metal 源代码或 Metal 库配合 管线脚本在编译器工具链上 相当于 API 中的管线描述符的集合 编译过程的输出是二进制存档 App 运行时无需再生成 GPU 代码 只需加载离线构建的二进制存档 即可加速创建 PSO
离线编译通过减少运行时的 CPU 占用率让你的 App 受益 这是 Metal 作为底层 API 的核心价值 此外 通过这种方式可以 从两个方面显著改善 App 的体验 首次启动和新关卡的加载 会大大加快 从而可能带来更好的参与度和互动 由于运行时编译而导致的卡顿 或帧速率下降 最终也可以消除 无需再为预热帧提供内存或 CPU 成本 接下来我将更详细地探讨这些优势
这里是传统的 App 运行时的 二进制生成 在这个例子中 你的 App 在加载屏幕后 花费大约三分之二的时间编译 GPU 二进制 然后你才能开始与它进行交互 但是通过离线编译 你的运行时 着色器生成将移动到 APP 构建时间 PSO 的创建只需要一小部分时间 你可以更快地与 App 交互 而不是在加载屏幕上空转
离线编译也有助于减少卡顿 使用传统的运行时二进制生成 为了避免在加载时 创建过多的管线状态 你可能会按需即时创建一些状态 发生这种情况时 你可能会遇到 由于编译而暂时中断命令编码 导致的掉帧 离线编译消除了这些讨厌的气泡 因为你可以在 App 构建时 编译更多的着色器 接下来 我将分享一个 新的开发工作流 帮助你利用离线编译的优势
在以下工作流中 你将学习如何使用新的工具链功能 离线构建 GPU 二进制 我将向你们展示如何生成新的 管线脚本输入文件 然后 如何调用工具链生成 GPU 二进制 管线脚本文件是一个 或多个 API 管线状态描述符的 JSON 格式描述 可以通过多种方式生成 例如 在你最喜欢的 JSON 编辑器中编写它们 或者通过处理在开发和测试期间 序列化的二进制存档获取 这里有一段 Metal 代码 它生成了一个具有 状态和函数的渲染管线描述符 及其等效的 JSON 描述 首先 将 API metal 库文件 指定为库路径属性 然后你的 API 渲染描述符函数名称 作为渲染管线属性 最后 其他管线状态 如 raster_sample_count 或像素格式 也被记录为脚本属性 在 Metal 的开发文档中 查找更多 JSON 结构定义详细信息
你可能还想开启 JSON 脚本生成 可以使用 Metal 运行时 只需在运行时生成二进制存档 并在开发和测试过程中将它们序列化 现在我将向你们展示 如何使用 Metal API 完成此任务
你们可以在运行时通过创建 带有状态和函数的管线描述符 开始收集过程 将其添加到生成 GPU 二进制的存档中 并将其序列化到文件系统以导入 你的 App 包 再从中加载 Metal 3 运行时将你的管线描述符 与 GPU 二进制一起存储 现在我将向你们展示如何提取它们 metal-source 允许从现有存档中 提取 JSON 管线脚本 这对于将二进制生成从运行时 迁移到 App 构建时非常方便 只需调用 metal-source 时 使用 flatbuffers 和 输出目录选项 就可以产生管线脚本文件 你可以对其进行编辑 以生成额外的二进制 现在 我将向你展示如何调用工具链 在 Xcode 项目构建阶段 生成 GPU 二进制很容易 只需象从终端调用 metal 一样 提供源代码 管线脚本 和输出文件 输出的 metal 库 现在就包含 GPU 二进制 并且可以部署在 任何工具链支持的设备上 如果你有一个 Metal 库而不是源码 也可以将其传递给工具链 使用 Metal 转换工具 从现有的 Metal 库 生成二进制也同样简单 只需像在终端中调用 metal-tt 一样 提供源码 管线脚本和输出文件 得到的 Metal 库现在包含所有工具链 支持设备的 GPU 二进制 现在你知道如何离线创建二进制 我将回顾如何加载它们 只需在创建存档描述符时 提供二进制的 URL 并使用它来实例化存档 就是这样 有关 Metal 的 二进制存档 API 的更多信息 请查阅我们过去几年的演讲 最后 是关于 Metal 如何处理 这些通过离线生成的 GPU 二进制的兼容性的一点说明 为确保离线生成的二进制 与未来的操作系统版本 和产品前向兼容 Metal 会在操作系统更新期间 或 App 安装时优雅地升级 你的二进制存档 它会在后台异步执行此操作 我迫不及待地想 让你们利用离线编译的优势 来消除运行时卡顿 并减少首次启动和新关卡加载时间 这种提升对其他人来说是切实可见的 并增强了他们的整体使用体验 现在 让我们交给 Eylon Eylon: 谢谢 Galo 接下来 我将介绍新的编译选项 用于优化大小 Metal 编译器积极优化代码 以提高运行时性能 一些优化会扩大 GPU 程序的大小 这可能会带来意想不到的成本 例如 函数内联是一种避免 函数调用消耗的优化 它通过将被调用函数的函数体 内联到调用点来实现 这个示例内核看起来没有很多代码 但内联之后 它将包含函数 f 和 g 的副本 可能还包含从 f 和 g 调用的函数 比如辅助函数和非原始库函数
另一种优化是循环展开 它会内联循环体的额外副本 以暴露迭代之间的并行性 并避免分支消耗 编译器有可能只展开循环的两次迭代 也可能展开具有 固定边界的循环的所有迭代 当通过象这样的优化创建一个 非常大的程序时 编译器也必须花费 更多的时间来编译它 在某些情况下 你可能希望避免这些成本 Xcode 14 推出了一种新的 Metal 优化模式 尺寸优化 当编译器应用性能优化时 此模式限制了尺寸扩展转换 例如内联和循环展开 这样做的好处是 可以使 GPU 的二进制代码更小 编译时间更短 特别在默认优化 代价过大的情况下 在对大小进行优化时 可能会造成较低的运行时性能 这是否真的发生取决于程序 所以你们需要尝试两种优化模式 并进行比较 优化大小可能不会提高所有 着色器的大小和编译时间 它最有可能对具有深度调用路径 和循环的大型程序有益 内联和展开在这些程序里很常见 当遇到默认优化导致的编译时间 异常长时 该选项值得尝试 无论在项目构建期 还是程序运行时编译 该选项都可用 在这种情况下 对大小进行优化会产生不同的效果 Cycles 是 Blender 3D 设计环境的 产品渲染器的开源项目 最近进行了更新以支持 Metal Apple 最近加入了 Blender 开发基金 我们所了解的是 Blender 的 路径跟踪算法 使用了大型计算着色器 有许多辅助函数和循环 它的编译时间加起来可以达到几分钟 事实证明 这些正是可以 从 Metal 3 的 新尺寸优化选项中受益的着色器
在 Apple 芯片 GPU 上渲染这些场景时 启用优化尺寸会改善 Blender 的设置时间 包括编译着色器管线 最多快 1.4 倍 它提供了加速 对渲染时间影响很小甚至没有影响 有些渲染速度下降了 4% 有些根本没有下降 因此 有可能运行时性能会降低 但在某些情况下 对大小进行优化 也可能提高运行时性能 这是个例子 这些是在英特尔 GPU 上 启用尺寸优化后 相同场景的渲染时间加速 好处不仅在于更快的编译速度 还在于更快的渲染速度 最高可达 1.6 倍 怎么样 因为较小的 GPU 程序可以避免一些 大尺寸带来的运行时损失 它可以受益于更少的指令缓存缺失 或者需要更少的寄存器 这意味着更少溢出到内存 甚至更多的并行线程 请记住 这些结果并不是所有 着色器和场景的典型结果 性能下降是有可能的 对于你的项目 需要评估对编译时间 和运行性能的实际影响 你们可以在三种不同的环境中 从 Metal 源代码编译时启用优化大小 在 Xcode 14 用户界面中 在构建设置中指定尺寸优化 在 Metal Compiler - Build Options 下 找到 Optimization Level 设置 “Default” 级别优化了性能 正如 Metal 过去所做的那样 “Size” 级别可以优化大小
通过命令行编译 Metal 源码时 使用选项 -Os 指定尺寸优化 第一个示例指定单个编译 和链接命令的选项 第二个示例有两个编译命令 并指定其中一个选项 使其只对一些着色器启用 该选项不需要传递给 link 命令 或任何后续命令 你们可以选择 是否和本演讲前面介绍的 生成 GPU 二进制的命令一起 使用尺寸优化
最后 在运行时使用 Metal Framework API 编译 Metal 源代码时 例如通过 newLibraryWithSource 指定为尺寸优化 只需在 MTLCompileOptions 对象中 使用属性 optimizationLevel 优化级别可以是 “default” 或 “size” 我希望你们的项目能够 从 Metal 编译器中的 这种新优化模式中受益 Galo:总结一下 我介绍了离线编译 这是一种完全在 App 构建时 生成 GPU 二进制的新工作流 以减少应用内卡顿 首次启动 和新关卡加载时间 Eylon:然后我提出了尺寸优化 一种在源代码编译时新的 Metal 优化模式 以减少程序大小和编译时间 Galo: 我们希望这些改进 能够帮助你的 App 或游戏提供更好的用户体验 Eylon: 让设置和加载时间更短 卡顿更少 还有新的工作流程 感谢运行时更低的编译消耗 齐: 感谢收看
-
-
4:47 - Using a JSON editor: render pipeline descriptor
// An existing Obj-C render pipeline descriptor NSError *error = nil; id<MTLDevice> device = MTLCreateSystemDefaultDevice(); id<MTLLibrary> library = [device newLibraryWithFile:@"default.metallib" error:&error]; MTLRenderPipelineDescriptor *desc = [MTLRenderPipelineDescriptor new]; desc.vertexFunction = [library newFunctionWithName:@"vert_main"]; desc.fragmentFunction = [library newFunctionWithName:@"frag_main"]; desc.rasterSampleCount = 2; desc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; desc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float;
-
4:47 - Using a JSON editor: pipelines script
{ "//comment": "Its equivalent new JSON script", "libraries": { "paths": [ { "path": "default.metallib" } ] }, "pipelines": { "render_pipelines": [ { "vertex_function": "vert_main", "fragment_function": "frag_main", "raster_sample_count": 2, "color_attachments": [ { "pixel_format": "BGRA8Unorm" }, ], "depth_attachment_pixel_format": "Depth32Float" } ] } }
-
5:33 - Harvesting sample
// Create pipeline descriptor MTLRenderPipelineDescriptor *pipeline_desc = [MTLRenderPipelineDescriptor new]; pipeline_desc.vertexFunction = [library newFunctionWithName:@"vert_main"]; pipeline_desc.fragmentFunction = [library newFunctionWithName:@"frag_main"]; pipeline_desc.rasterSampleCount = 2; pipeline_desc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; pipeline_desc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float; // Add pipeline descriptor to new archive MTLBinaryArchiveDescriptor* archive_desc = [MTLBinaryArchiveDescriptor new]; id<MTLBinaryArchive> archive = [device newBinaryArchiveWithDescriptor:archive_desc error:&error]; bool success = [archive addRenderPipelineFunctionsWithDescriptor:pipeline_desc error:&error]; // Serialize archive to file system NSURL *url = [NSURL fileURLWithPath:@"harvested-binaryArchive.metallib"]; success = [archive serializeToURL:url error:&error];
-
6:01 - Extracting a JSON script
metal-source -flatbuffers=json harvested-binaryArchive.metallib -o /tmp/descriptors.mtlp-json
-
6:24 - Generate a GPU binary from source
metal shaders.metal -N descriptors.mtlp-json -o archive.metallib
-
6:48 - Generate a GPU binary from Metal library
metal-tt shaders.metallib descriptors.mtlp-json -o archive.metallib
-
7:07 - Load GPU binaries via the runtime API
MTLBinaryArchiveDescriptor *desc = [MTLBinaryArchiveDescriptor new]; desc.url = [NSURL fileURLWithPath:@"archive.metallib"]; NSError *error = nil; id<MTLDevice> device = MTLCreateSystemDefaultDevice(); id<MTLBinaryArchive> binaryArchive = [device newBinaryArchiveWithDescriptor:desc error:&error];
-
12:11 - Enable optimize for size in command lines
xcrun metal -Os large_shader.metal # or xcrun metal -c -Os large_shader.metal xcrun metal -c more_shaders.metal xcrun metal large_shader.air more_shaders.air
-
12:44 - Enable optimize for size with Metal framework
MTLCompileOptions* options = [MTLCompileOptions new]; options.optimizationLevel = MTLLibraryOptimizationLevelSize; NSString* source = @"..."; NSError* error = nil; id<MTLLibrary> lib = [device newLibraryWithSource:source options:options error:&error];
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。