C 版本 ORT 入门
目录
构建版本
构建产物 | 描述 | 支持的平台 |
---|---|---|
Microsoft.ML.OnnxRuntime | CPU (Release) | Windows、Linux、Mac、X64、X86 (仅限 Windows)、ARM64 (仅限 Windows)……更多详情:兼容性 |
Microsoft.ML.OnnxRuntime.Gpu | GPU - CUDA (Release) | Windows、Linux、Mac、X64……更多详情:兼容性 |
Microsoft.ML.OnnxRuntime.DirectML | GPU - DirectML (Release) | Windows 10 1709+ |
onnxruntime | CPU, GPU (Dev), CPU (设备端训练) | 与 Release 版本相同 |
CPU 设备端训练 (Release) | .zip 和 .tgz 文件也作为资产包含在每个 Github 版本中。 | Windows、Linux、Mac、X64、X86 (仅限 Windows)、ARM64 (仅限 Windows)……更多详情:兼容性 |
.zip 和 .tgz 文件 也作为资源包含在每个 Github 发布 中。
API 参考
- 包含 onnxruntime_c_api.h。
- 调用 OrtCreateEnv
- 创建会话:OrtCreateSession(env, model_uri, nullptr,…)
- 可选地添加更多执行提供者 (例如,对于 CUDA 使用 OrtSessionOptionsAppendExecutionProvider_CUDA)
- 创建张量 1) OrtCreateMemoryInfo 2) OrtCreateTensorWithDataAsOrtValue
- OrtRun
功能特性
- 从磁盘上的模型文件和一组 SessionOptions 创建 InferenceSession。
- 注册自定义日志记录器。
- 注册自定义分配器。
- 注册预定义提供者并设置优先级顺序。ONNXRuntime 拥有一组预定义的执行提供者,如 CUDA、DNNL。用户可以将提供者注册到他们的 InferenceSession。注册的顺序也表明了偏好顺序。
- 使用输入运行模型。这些输入必须位于 CPU 内存中,而非 GPU 内存。如果模型有多个输出,用户可以指定他们想要的输出。
- 将编码为 protobuf 格式的内存中的 ONNX 张量转换为可用作模型输入的指针。
- 设置每个会话的线程池大小。
- 设置每个会话的图优化级别。
- 动态加载自定义算子。说明
- 从字节数组加载模型的能力。参见
OrtCreateSessionFromArray
在 onnxruntime_c_api.h 中。 - 全局/共享线程池:默认情况下,每个会话创建自己的线程池集。在同一进程中需要创建多个会话(以推理不同的模型)的情况下,最终会产生每个会话创建的多个线程池。为了解决这种低效率问题,我们引入了一项名为全局/共享线程池的新功能。其基本思想是在多个会话之间共享一组全局线程池。此功能的典型用法如下
- 填充
ThreadingOptions
。使用值 0 让 ORT 选择默认值。 - 使用
CreateEnvWithGlobalThreadPools()
创建 env - 创建会话并在会话选项对象上调用
DisablePerSessionThreads()
- 像往常一样调用
Run()
- 填充
- 在会话之间共享分配器
- 描述:此功能允许同一进程中的多个会话使用相同的分配器。
- 场景:您在同一进程中有多个会话,并且内存使用量很高。其中一个原因如下。每个会话都创建自己的 CPU 分配器,默认情况下这是基于 arena 的。ORT 实现了一个基于 Doug Lea 的“最佳首次合并算法”的简化版 arena 分配器。每个分配器都驻留在自己的会话中。它在初始化时分配一块大的内存区域,然后根据分配/解除分配的需求对此初始区域进行分块、合并和扩展。随着时间的推移,arena 最终会在每个会话中留下未使用的内存块。此外,arena 分配的内存永远不会返回给系统;一旦分配,它就会一直保持已分配状态。当使用多个会话(每个会话都有自己的 arena)时,所有这些因素累加起来,从而增加了进程的总体内存消耗。因此,在会话之间共享 arena 分配器变得很重要。
- 用法:
- 使用
CreateAndRegisterAllocator
API 创建并向 env 注册一个共享分配器。然后,使用相同 env 实例的所有会话都将重用此分配器,除非某个会话选择通过将session_state.use_env_allocators
设置为“0”来覆盖此设置。 - 对于每个希望使用 env 注册的分配器的会话,将其
session.use_env_allocators
设置为“1”。 - 请参阅 https://github.com/microsoft/onnxruntime/blob/main/onnxruntime/test/shared_lib/test_inference.cc 中的
TestSharedAllocatorUsingCreateAndRegisterAllocator
测试以获取示例。 - 配置 OrtArenaCfg(从 ORT 1.8 版本开始使用 API
CreateArenaCfgV2
创建OrtArenaCfg
实例,之前版本使用现已弃用的CreateArenaCfg
创建实例)CreateArenaCfgV2
接收以下键列表及其对应的取值集(每个键一个值)。这些配置的默认值可以在 BFCArena 类中找到。请参阅 https://github.com/microsoft/onnxruntime/blob/main/onnxruntime/test/shared_lib/test_inference.cc 中的ConfigureCudaArenaAndDemonstrateMemoryArenaShrinkage
测试,以获取CreateArenaCfgV2
的使用示例。max_mem
:这是 arena 分配的最大内存量。如果现有任何区域无法服务某个块,则 arena 会根据可用内存(max_mem - 已分配量)分配更多区域来扩展自身。如果可用内存小于请求的扩展量,则会返回错误。arena_extend_strategy
:当前只能取两个值:kSameAsRequested 或 kNextPowerOfTwo。顾名思义,kNextPowerOfTwo(默认值)以 2 的幂次扩展 arena,而 kSameAsRequested 每次都以与分配请求相同的大小进行扩展。kSameAsRequested 适用于事先知道预期内存使用量的更高级配置。initial_chunk_size_bytes
:此配置仅在 arena 扩展策略为 kNextPowerOfTwo 时相关。这是 arena 在首次分配时(如果首次内存请求大于此值,则分配大小将大于此提供值)可能分配的区域大小。从此区域将块移交给分配请求。如果日志显示 arena 的扩展量远超预期,最好为此选择一个足够大的初始大小。initial_growth_chunk_size_bytes
:在阅读本节之前,请先阅读“内存 arena 收缩”部分。此配置仅在 arena 扩展策略为 kNextPowerOfTwo 时相关。目前,此值是 arena 收缩后首次分配(如果在 arena 收缩后内存请求大于此值,则分配大小会更大)的可能大小。arena 的首次(可能)分配由initial_chunk_size_bytes
定义,随后的可能分配是initial_chunk_size_bytes * 2
、initial_chunk_size_bytes * 4
等等。如果 arena 收缩(即解除分配任何这些内存区域),我们希望“重置”收缩后首次分配的大小。这是当前的定义。将来,此配置可能用于控制 arena 的其他“增长导向”行为(即 arena 在首次分配(初始块)之后的第二次分配(arena 增长)可能由此参数定义)。max_dead_bytes_per_chunk
:这控制了是否将块拆分以服务分配请求。目前,如果块大小与请求大小之间的差异小于此值,则不拆分该块。这可能会导致内存浪费,因为它在整个过程中会保留一部分未使用的块(因此称为“死字节”),从而增加内存使用量(直到此块返回到 arena)。
- 使用
- 内存 arena 收缩
- 描述:默认情况下,内存 arena 不会收缩(将未使用的内存返回给系统)。此功能允许用户以某种节奏“收缩” arena。目前,唯一支持的节奏是在每次 Run() 结束时(即,如果使用此功能,则在每次 Run() 结束时扫描 arena 内存以可能释放未使用的内存)。这通过 RunOption 实现。
- 场景:您有一个动态形状模型,可能偶尔会服务于需要分配大量内存的请求。由于默认情况下 arena 不会释放任何内存,因此 arena 在服务此请求时发生的“增长”会一直保留。这是次优的,因为大多数其他请求可能不需要这么多内存(即,仅为了处理一个或两个异常值而最终分配了太多内存)。如果这最能描述 ORT 的使用场景,那么使用此收缩功能是一个选项。此功能仅适用于相关内存分配器一开始就是基于 arena 的分配器的情况。
- 用法:请参阅 https://github.com/microsoft/onnxruntime/blob/main/onnxruntime/test/shared_lib/test_inference.cc 中的
ConfigureCudaArenaAndDemonstrateMemoryArenaShrinkage
测试以获取示例。要最佳地使用此功能,需要根据用例适当配置将要收缩的内存 arena。请参阅上面如何使用OrtArenaCfg
实例配置 arena 分配器。此功能根据两种可用的 arena 扩展策略(kSameAsRequested 和 kNextPowerOfTwo)略有不同,如下所述kNextPowerOfTwo
:如果选择此配置,则收缩时会考虑除了初始分配之外的所有内存分配进行解除分配。其想法是用户设置足够高的initial_chunk_size_bytes
以处理大多数模型请求而无需分配更多内存(即,此初始内存足以服务任何平均请求)。作为服务异常值请求一部分而分配的任何后续分配是唯一可以解除分配的候选者。kSameAsRequested
如果选择此配置,则收缩时会考虑所有内存分配进行解除分配。这是因为,目前对于此策略,initial_chunk_size_bytes
不相关。
- 从非 arena 内存分配初始化器内存(适用于高级用户)
- 描述:如果与存储初始化器内容的设备相关的分配器是基于 arena 的分配器,并且希望防止因分配这些初始化器的内存而导致的(潜在的)过度 arena 增长,则此功能提供了这种能力。
- 场景:您有一个相当简单的模型,在 Run() 本身期间不需要分配太多内存,但模型具有相对大量的初始化器,因此如果使用 arena 为其分配内存,则整个 arena 分配的内存中的未使用的内存可能会远远超过模型在 Run() 期间实际所需的内存。在这种情况下,如果模型要部署到内存受限的环境中,使用此功能将很有意义,以防止因在 arena 中分配初始化器内存而导致的 arena 过度增长。使用此功能意味着 arena 仅用于分配模型算子所需的内存。看起来,为 arena 设置足够高的初始块大小 (
initial_chunk_size_bytes
) 来考虑初始化器和 Run() 期间预期的任何所需内存,可以避免出现问题的情况。这样做的问题是,某些 EP 在内部使用每个线程的分配器,并且配置中设置的任何高初始块大小都将应用于每个分配器,并且由于最终只有一个每个线程的分配器分配初始化器的内存,因此会导致内存浪费。 - 用法:请参阅 https://github.com/microsoft/onnxruntime/blob/main/onnxruntime/test/shared_lib/test_inference.cc 中的
AllocateInitializersFromNonArenaMemory
测试以获取示例。
- 在会话之间共享初始化器及其 ORT 预处理版本
- 描述:此功能允许用户在多个会话之间共享同一个初始化器实例(及其 ORT“预处理”版本)。
- 场景:您有多个模型使用了相同的初始化器集,除了模型的最后几层不同,并且您在同一进程中加载这些模型。当每个模型(会话)创建同一初始化器的独立实例时,会导致过度和浪费的内存使用,因为在这种情况下它是相同的初始化器。您希望优化内存使用,同时具有灵活分配初始化器(甚至可能将它们存储在共享内存中)的能力。
- 用法:在调用
CreateSession
之前,使用AddInitializer
API 将预分配的初始化器添加到会话选项中。使用相同的会话选项实例创建多个会话,从而允许在会话之间共享初始化器。请参阅 C API 示例用法 (TestSharingOfInitializerAndItsPrepackedVersion) 和 C# API 示例用法 (TestSharingOfInitializerAndItsPrepackedVersion)。 - 在某些 ORT 算子实现中,模型加载时会预处理初始化器(称为“预打包”的过程),以促进在某些平台上的最佳算子推理。默认情况下,这些预处理版本的初始化器按每个会话进行维护(即,它们不在会话之间共享)。要在会话之间启用共享,请创建容器(使用
CreatePrepackedWeightsContainer
)并在会话创建时传递,以便在会话之间共享共享初始化器的预打包版本,并且这些版本不会在内存中重复。上面在 C 和 C# 中引用的相同测试也展示了此功能的示例用法。注意:任何希望实现预打包的内核开发人员必须编写一个测试,该测试触发使用该内核可能预打包的所有权重的预打包,并且必须测试这些预打包权重在会话之间的共享。请参阅内核测试 (SharedPrepackedWeights)。
部署
Windows 10
您的安装程序应将 onnxruntime.dll 放在与您的应用程序相同的文件夹中。您的应用程序可以使用加载时动态链接或运行时动态链接来绑定到 dll。
动态链接库搜索顺序
这是一篇关于 Windows 如何查找支持 dll 的重要文章:动态链接库搜索顺序。
在某些情况下,应用程序不直接使用 onnxruntime,而是调用使用 onnxruntime 的 DLL。构建这些使用 onnxruntime 的 DLL 的人需要注意文件夹结构。不要修改系统 %path% 变量来添加您的文件夹。这可能与机器上也在使用 onnxruntime 的其他软件冲突。相反,将您的 DLL 和 onnxruntime DLL 放在同一文件夹中,并使用运行时动态链接显式绑定到该副本。您可以使用此示例在 GetModulePath() 中使用的代码来查找加载您的 dll 的文件夹。
遥测
要在官方 Windows 构建中开启/关闭遥测收集,请使用 C API 中的 Enable/DisableTelemetryEvents()。有关遥测收集和 Microsoft 隐私政策的更多信息,请参阅隐私页面。