menu
 

关于 Wwise 2024.1 中新增的默认内存分配器 AkMemoryArena

新版本 / Wwise 技巧和工具

在本文中,我们将详细介绍 Wwise 2024.1 中新增的默认内存分配器 AkMemoryArena。相较于之前的 Wwise 版本,新的内存分配器可大幅提升 Wwise 对内存资源的利用效率。不过,各位仍可对内存系统做细致的研究和配置,并在此基础上对很多项目做进一步的优化。

AkMemoryArena 概述

在 Wwise 2024.1 中,我们将引入 AkMemoryArena 作为新的默认内存分配器。事实上,之前的内存分配器都只是基于现成解决方案做了一些调整,而这次是我们第一次从头开始构建一个完整的内存分配器。这样就可以从一开始就满足 Wwise 的所有需要。

AkMemoryArena 具有以下功能:

动态映射和取消映射内存跨度

现在仍然可以在需要的时候映射新的内存跨度,并在释放所有相关内存之后取消映射这些跨度。也就是说,我们并不严格要求按照硬性限制来预设内存池的大小。根据我们的经验,对大多数开发人员来说,不预设内存限制的话操作起来会更加灵活。 

提升 CPU 和内存碎片处理性能

AkMemoryArena 结合使用了不同的分配算法以期在各种情形下实现整体 CPU 性能和内存利用效率之间的平衡:

  • 基于内存块的分配:适用于 256 字节以内的小规模分配
    • 即分配区的 Small-Block Allocator (SBA) 部分
  • 基于空闲列表的分配:采用 Good-fit 分配策略,适用于中等规模的分配
    • 即分配区的 Two-level segregated fit (TLSF) 部分
  • 独立的内存跨度:适用于大规模的分配

藉此,开发人员可根据需要从整体上控制内存碎片。另外,所有分配算法都能提供稳定的性能。这样在大部分情况下都可将 CPU 开销控制在较低水平。

而且,AkMemoryArena 没有任何线程本地缓存,而是依靠便捷的锁定机制来处理线程之间的同步。通过在所有可用线程之间共享内存分配器的状态,确保即使在多线程场景下也能准确预测预留内存。

不过没有线程本地缓存还是有一定的缺点的:在多个线程上频繁分配内存可能会影响性能。为了降低分配内存的频率,尤其是在多个内核之间渲染音频时,Wwise 还使用了一些专用的内存分配器来处理短时内存分配。比如,TempAlloc 和 BookmarkAlloc。它们会利用自身的线程本地状态,基本上不需要做任何跨线程同步。

除内存分配本身的 CPU 开销外,我们还发现使用 2 MiB 左右的 Huge Page 来映射内存会更好一些。通过使用 Huge Page,可减少 CPU 上的转译后备缓冲区缺失。跟使用 4 KiB 或 16 KiB 的较小内存页相比,这样通常可将 CPU 的整体性能提升约 10%。

简化游戏引擎集成

为了简化游戏引擎集成并设法降低分配内存时出错的几率或存在的不确定性,我们已将 AkMemoryArena 用来获取和释放内存跨度的回调做得尽可能简单。

每个 AkMemoryArena 都需要一组用户提供的回调来管理内存:一个用于分配内存跨度,一个用于释放内存跨度。

这些回调的实现基本上没什么要求:

  • 对映射和取消映射内存的方式没有特别的要求 
  • 对内存对齐没有任何要求 
  • 释放跨度时唯一提供的数据跟之前跨度分配调用返回的数据完全一样 – 包括用户为分配提供的地址,及指向任意 userData 的指针。

比如,只需将函数的部分参数传给 std::malloc() 和 std::free() 就可有效实现这些函数,而无需额外执行任何其他逻辑。事实上,在 Windows 和 POSIX 平台上,Wwise 声音引擎中默认的这些挂钩就是如此!

根据不同项目的需要灵活配置

很多项目和团队在内存利用上都有不同的做法。鉴于游戏开发内容的性质,有的时候可能需要做出某些决定。比如:

  • 某个团队可能倾向于在项目启动时针对各个系统制定详细的预算,并分配整个应用程序所需的全部内存以确保能精准预测内存用量
  • 另一团队可能倾向于逐一映射和取消映射内存,以此确保其他系统只能在必要的时候使用内存

为此,我们提供了丰富的配置选项,以满足各种不同用例的需要。

比如,虽然 AkMemoryArena 并不要求在初始化时预留全部内存,但是可以通过将初始跨度设得很大来实现同样的配置效果。甚至还可在内存分配回调中将此初始跨度作为软性限制,确保在第一次调用该回调之后只要再调用就会发出警告。 

通过性能分析器监控内存碎片

在将 AkMemoryArena 集成到 Wwise 中的时候,我们顺便搭建了相应的系统以便对分配区做详细的监控和分析。我们希望用户可以据此为自己的游戏配置 AkMemoryArena。而且,这样也方便我们准确地识别问题并提供相应的支持和帮助。 

降低内存用量

除了这些功能,我们还发现 AkMemoryArena 预留和使用的内存总量基本上都比以前低很多。

对 AkMemoryArena 实施性能分析

为 AkMemoryArena 新增的 Profiler 针对各个分配区的跨度详细提供了以下统计信息和数据:

  • 每次调用分配回调 fnMemAllocSpan 时返回的 Address 和 UserData
    • 注意,主视图中的每一行都代表对 fnMemAllocSpan 的一次调用
  • 跨度的大小
  • 分配了多少个跨度,还有几个是可用的
  • 内存碎片分布图,表明分配的跨度区域

同时,还提供了各个 AkMemoryArena 的汇总统计数据。除了所用内存总量和预留内存总量等简单的累计数据,Profiler 还列出了分配区中的 Largest Free 可用空间。这个统计数字表示分配区最多支持分配多大的内存。也就是说,在达到该值之后便要申请获取新的跨度。只要将该值与总计可用内存进行比较,就能大致判断出分配区的碎片化程度。

Wwise-Advanced-Profiler-Memory-Arenas

所有这些都只要很小的运行时成本就可以实现,而且计算时不用获取完整的应用程序历史记录。也就是说,即使游戏已经运行了好几个小时,设计工具也能轻松连接游戏并评估内存布局的状态。

注意,为了降低追踪这些数据的 CPU 开销,其只能粗略提供 AkMemoryArena 的状态。虽然可能无法像很多专业的内存分析工具那样详细提供每项分配的信息,但我们相信这些数据应该足以让用户轻松评估内存利用上是否存在问题。如果发现问题,还可据此做进一步的检查,来确定如何进一步配置分配区和内存策略。

集成到游戏引擎中

对于使用自研游戏引擎(而非我们预构的 Wwise 与 Unity 和 Unreal 的集成)的用户,不妨重新考虑一下如何集成和配置内存系统。

首先要指出的是,各项内存分配操作的现有回调仍然可用,其行为也未做修改。需要的话,也可以使用自己的内存分配器来分配 Wwise 所需的各项内存。 

own-Memory-Allocator

不过,如果使用这些回调,就无法使用 AkMemoryArena 以及为其新增的 Profiler。所以,即使觉得有必要确保兼顾每一项内存分配,也不妨考虑一下直接使用 AkMemoryArena,因为其提供的工具足以满足各种情形的需要。而且,依靠 AkMemoryArena 处理大部分内存分配,还可减轻其他全局内存系统(如有)的压力,并简化对 Wwise 内存用量追踪工具的开发。

值得注意的是,即便使用了 AkMemoryArena,仍可记录各项内存分配的相关元数据。对此,可在 AkMemSettings 中使用 Debug 内存挂钩:

AkMemSettings-debug-2

无论 Wwise 是使用内置内存分配器,还是使用单独的内存分配回调,这些内存挂钩都可以有效地发挥作用。除此之外,甚至还可使用这些回调来对内存碎片做深入的分析。

就像前面说的,我们已经设法把 AkMemoryArena 的初始设置和集成做得尽可能简单。下面举例展示了如何实现回调以分配和释放内存。

AkMemoryArenas-example-2

因为 AkMemoryArena 是单独配置的,所以甚至可为各个 AkMemoryArena 使用不同的回调。比如,Primary 或 Media 分配区所用分配的生命周期和大小存在一定差异。在这种情况下,我们可以为其使用不同的底层内存分配器。

若要禁止使用某些内存分配区,还可直接将回调设置为 nullptr:

Disable-memory-arenas-nullptr

在 Release 配置下,可据此来默认禁用 Profiler 分配区 (AkMemoryMgrArena_Profiler),或在不使用设备专用内存做音频处理的平台上禁用 Device 分配区 (AkMemoryMgrArena_Device)。

同样,在一般的游戏中 Wwise 可能根本不会为 Media 分配区分配内存(具体取决于如何设置 Wwise 与游戏引擎的其他集成)。比方说,工程只使用 AK::SoundEngine::SetMedia 和 AK::SoundEngine::LoadBankMemoryView 等 API,而不使用 AK::SoundEngine::LoadBank 或 AK::SoundEngine::LoadBankMemoryCopy。注意,在设计工具在性能分析过程中将新的媒体传给声音引擎时,仍会执行某些 Media 分配。所以,只有在将 Wwise 的目标配置设为 Release 时才应选用此选项。

根据游戏引擎的集成情况,也可考虑在声音引擎以外使用 Memory Arena。比如,使用 AK::MemoryMgr::Malloc 自行创建内存分配,然后将 SoundBank 数据加载到分配中,再将内存提供给 AK::SoundEngine::LoadBankMemoryView。这样仍可由 AkMemoryArena 管理内存,而且允许充分利用 Profiler 及其他系统,同时通过代码来控制内存的分配和释放。

进一步配置和调整

AkMemSettings 中的 memoryArenaSettings 数组用于配置 Memory Arena 的其他参数。声音引擎中会将此数组配置为合理的默认值,以此确保系统在一般情况下都能很好地运行。但要知道每款游戏在内容和内存上都有不同的需求,所以要对游戏内容做适当优化以提高内存利用效率。比如,在对模拟游戏的内容测试时,我们发现只要对 AkMemoryArena 设置做一些简单的调整,就能将总体 Memory Reserved 降低 5-10%。

下面是一些简单的建议,各位不妨参考一下:

  • 设置 AkMemoryArenaSettings::uTlsfInitSize 以使其符合典型的内存用量或目标内存预算。我们发现较大的 Base 或初始跨度通常可提供最佳的内存碎片处理性能,而无需分配很多单独的内存跨度。而且,还有助于在项目启动时明确整个系统的总计预留内存并制定其他方面的内存预算。
  • AkMemoryArenaSettings::uSbaInitSize 设为方便在 Profiler 中识别的水印。SBA 有自己的 Base 跨度。这有助于降低相关分配的内存用量(每项分配约 16 字节)。通过将其设为较高的值,可将小规模的分配归入 SBA Base Span。这样不仅能降低 Memory Reserved,还能降低 Memory Used。
  • AkMemoryArenaSettings::uAllocSizeHuge 设为较低的值以减少 TLSF 跨度中的碎片。数值越小就会有越多的分配归入独立的 Huge 跨度,而不是归入 TLSF 跨度。注意,这在很大程度上取决于如何为 fnMemAllocSpan 设置 Memory Arena 挂钩集成。因为这也会导致对 fnMemAllocSpan 的更多调用,而且要假定跨度以外的碎片不会构成问题。

有关诸如此类的其他建议,请参阅 Wwise SDK 文档中的 AkMemoryArena 的配置和调整

大卫•克鲁克斯

软件工程师

Audiokinetic

大卫•克鲁克斯

软件工程师

Audiokinetic

David Crooks是Wwise核心团队的一名软件工程师。他拥有超过10年的经验,在开发、调试和发布多个大型视频游戏项目方面,特别专注于低层系统设计和引擎架构,以帮助开发者为他们的玩家创造最佳的体验。

评论

留下回复

您的电子邮件地址将不会被公布。

更多文章

简化 Wwise 音频插件构建管线

就音频插件开发来说,Wwise 与数字音频工作站 (DAW) 有很大的不同。Wwise...

23.3.2020 - 作者:乔尔·罗比哈德(Joel Robichard)

在Wwise中进行大批量音频管理的思路分享

在我的日常音频工作中,经常会遇到需要做大批量语音文件导入、配置的情况,由此产生的批量化处理需求大致可分为三种:...

11.5.2020 - 作者:葛鑫

在 Wwise 中设定 Audio Object

未来遥不可及,往日似水流年。亦如费里斯•布勒 (Ferris Bueller) 所说:“生活快速向前,时光转瞬即逝。如不偶尔驻足停留,就可能会错失良辰。”距离 Wwise 2021...

24.8.2021 - 作者:戴米安·卡斯特鲍尔(Damian Kastbauer)

对白 | 基于Wwise与Unreal Engine的语音设计

22.12.2021 - 作者:杰克•盖米林 (Jake Gamelin)

关于如何在团队工作环境中使用 WAAPI 和 Python

在本文中,我想说说自己很长一段时间以来是如何使用 WAAPI 的。这当中用到了 Python、命令扩展 (Command Add-on) 和一个小的辅助程序 (Helper) 库。藉此,能以比...

11.5.2022 - 作者:尤金•乔尔内 (Eugene Cherny)

Wwise Spatial Audio 2023.1 新增功能 | Reverb Zone

Reverb Zone 简介 在 Wwise 23.1 中,我们为 Wwise Spatial Audio 增添了一个名为 Reverb Zone 的工具。Reverb Zone 本质上来说是...

10.1.2024 - 作者:托马斯•汉森 (Thomas Hansen)

更多文章

简化 Wwise 音频插件构建管线

就音频插件开发来说,Wwise 与数字音频工作站 (DAW) 有很大的不同。Wwise...

在Wwise中进行大批量音频管理的思路分享

在我的日常音频工作中,经常会遇到需要做大批量语音文件导入、配置的情况,由此产生的批量化处理需求大致可分为三种:...

在 Wwise 中设定 Audio Object

未来遥不可及,往日似水流年。亦如费里斯•布勒 (Ferris Bueller) 所说:“生活快速向前,时光转瞬即逝。如不偶尔驻足停留,就可能会错失良辰。”距离 Wwise 2021...