C 语言版 ORT 入门
目录
构建版本
产物 | 描述 | 支持的平台 |
---|---|---|
Microsoft.ML.OnnxRuntime | CPU (发布版) | Windows, Linux, Mac, X64, X86 (仅限 Windows), ARM64 (仅限 Windows)…更多详细信息: 兼容性 |
Microsoft.ML.OnnxRuntime.Gpu | GPU - CUDA (发布版) | Windows, Linux, Mac, X64…更多详细信息: 兼容性 |
Microsoft.ML.OnnxRuntime.DirectML | GPU - DirectML (发布版) | Windows 10 1709+ |
onnxruntime | CPU, GPU (开发版), CPU (设备端训练) | 与发布版相同 |
Microsoft.ML.OnnxRuntime.Training | CPU 设备端训练 (发布版) | 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 张量转换为可用作模型输入的指针。
- 设置每个会话的线程池大小。
- 设置每个会话的图优化级别。
- 动态加载自定义操作符。 说明
- 能够从字节数组加载模型。请参见
onnxruntime_c_api.h
中的OrtCreateSessionFromArray
。 - 全局/共享线程池: 默认情况下,每个会话都会创建自己的线程池集合。在同一进程中需要创建多个会话(用于推断不同的模型)的情况下,每个会话都会创建多个线程池。为了解决这种低效率问题,我们引入了一项名为全局/共享线程池的新功能。其基本思想是在多个会话之间共享一组全局线程池。此功能的典型用法如下:
- 填充
ThreadingOptions
。使用 0 值让 ORT 选择默认值。 - 使用
CreateEnvWithGlobalThreadPools()
创建环境 - 创建会话并对会话选项对象调用
DisablePerSessionThreads()
- 照常调用
Run()
- 填充
- 在会话之间共享分配器
- 描述: 此功能允许同一进程中的多个会话使用相同的分配器。
- 场景: 在同一进程中您有多个会话,并且发现内存使用率很高。其中一个原因如下。每个会话都会创建自己的 CPU 分配器,默认情况下它是基于竞技场(arena)的。 ORT 实现了一个简化版的竞技场分配器,该分配器基于 Doug Lea 的最佳匹配(best-first)合并算法。每个分配器都存在于其自己的会话中。它在初始化时分配一大块内存,然后根据分配/解除分配需求对这块初始区域进行分块、合并和扩展。随着时间的推移,竞技场最终会为每个会话留下未使用的内存块。此外,竞技场分配的内存从不返回给系统;一旦分配,它就始终保持分配状态。当使用多个会话(每个会话都有自己的竞技场)时,所有这些因素累加起来,从而增加了进程的整体内存消耗。因此,在会话之间共享竞技场分配器变得很重要。
- 用法:
- 使用
CreateAndRegisterAllocator
API 创建并向 env 注册一个共享分配器。除非会话选择通过将session_state.use_env_allocators
设置为“0”来覆盖此设置,否则使用相同 env 实例的所有会话都将重用此分配器。 - 对于每个希望使用 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 类中找到。有关CreateArenaCfgV2
的示例用法,请参见 https://github.com/microsoft/onnxruntime/blob/main/onnxruntime/test/shared_lib/test_inference.cc 中的测试ConfigureCudaArenaAndDemonstrateMemoryArenaShrinkage
。max_mem
: 这是竞技场分配的最大内存量。如果任何现有区域都无法满足某个内存块的需求,竞技场会根据可用内存(max_mem - 已分配量)再分配一个区域来扩展自身。如果可用内存少于请求的扩展量,则返回错误。arena_extend_strategy
: 目前只能取 2 个值:kSameAsRequested 或 kNextPowerOfTwo。顾名思义,kNextPowerOfTwo(默认值)按 2 的幂次扩展竞技场,而 kSameAsRequested 每次按与分配请求相同的大小进行扩展。kSameAsRequested 适用于更高级的配置,在这些配置中您提前知道预期的内存使用情况。initial_chunk_size_bytes
: 此配置仅在竞技场扩展策略为 kNextPowerOfTwo 时才相关。这是竞技场在首次分配时(如果首次内存请求大于此值,则分配大小将大于此提供值)分配的区域的(可能)大小。内存块从该区域分配给请求。如果日志显示竞技场的扩展量远超预期,则最好为此选择一个足够大的初始大小。initial_growth_chunk_size_bytes
: 请在阅读本节之前阅读“内存竞技场收缩”一节。此配置仅在竞技场扩展策略为 kNextPowerOfTwo 时才相关。目前,此值是竞技场收缩后首次分配的(可能)大小(竞技场收缩后,如果内存请求大于此值,将导致更高的分配大小)。竞技场(可能)的首次分配由initial_chunk_size_bytes
定义,随后的分配可能是initial_chunk_size_bytes * 2
、initial_chunk_size_bytes * 4
,依此类推。如果竞技场收缩(即)解除分配任何这些内存区域,我们希望“重置”收缩后首次分配的大小。这是当前的定义。将来,此配置可能用于控制竞技场的其他“以增长为中心”的行为(即)竞技场在首次分配(初始块)之后的第二次分配(竞技场增长)可能由该参数定义。max_dead_bytes_per_chunk
: 这控制了是否拆分一个内存块以满足分配请求。目前,如果内存块大小与请求大小之间的差值小于此值,则不会拆分该内存块。这有可能通过在整个过程中保留内存块的一部分未被使用(因此称为死字节)而浪费内存,从而增加内存使用量(直到此内存块返回到竞技场)。
- 使用
- 内存竞技场收缩
- 描述: 默认情况下,内存竞技场不会收缩(将未使用的内存返回给系统)。此功能允许用户以某种节奏“收缩”竞技场。目前,唯一支持的节奏是在每次 Run() 结束时(即)如果使用此功能,则在每次 Run() 结束时扫描竞技场内存以可能释放未使用的内存。这通过 RunOption 实现。
- 场景: 您有一个动态形状模型,可能偶尔会处理需要大量内存分配的请求。由于竞技场默认不释放任何内存,因此作为处理此请求的一部分,竞技场的这种“增长”会永远保持下去。这是次优的,因为大多数其他请求可能不需要那么多内存(即)为了处理一两个异常值而分配了过多的内存。如果这最能描述 ORT 的使用场景,那么使用此收缩功能是一个选择。此功能仅适用于相关内存分配器本身是基于竞技场的分配器。
- 用法: 有关示例,请参见 https://github.com/microsoft/onnxruntime/blob/main/onnxruntime/test/shared_lib/test_inference.cc 中的测试
ConfigureCudaArenaAndDemonstrateMemoryArenaShrinkage
。为了最佳地使用此功能,需要根据用例适当地配置要收缩的内存竞技场。请参阅上面如何使用OrtArenaCfg
实例配置竞技场分配器。此功能根据两种可用的竞技场扩展策略(kSameAsRequested 和 kNextPowerOfTwo)略有不同,如下所述:kNextPowerOfTwo
: 如果这是选定的配置,则除了初始分配之外的所有内存分配都将在收缩时考虑解除分配。其思想是,用户设置足够高的initial_chunk_size_bytes
来处理大多数模型请求,而无需分配更多内存(即)此初始内存足以满足任何平均请求。作为处理异常请求的一部分而分配的任何后续分配是唯一的解除分配候选者。kSameAsRequested
: 如果这是选定的配置,则所有内存分配都将在收缩时考虑解除分配。这是因为,目前initial_chunk_size_bytes
与此策略不相关。
- 从非竞技场内存中为初始化器分配内存(适用于高级用户)
- 描述: 如果初始化器内容存储的设备所对应的分配器是基于竞技场的分配器,并且希望防止因分配这些初始化器的内存而引起的任何(潜在的)过度竞技场增长,那么此功能提供了这种能力。
- 场景: 您有一个相当简单的模型,它在 Run() 期间本身不需要分配大量内存,但该模型具有相对大量的初始化器,以至于如果使用竞技场分配这些初始化器的内存,则整个竞技场已分配内存中的未使用的内存可能会远远超过模型在 Run() 期间实际所需的内存。在这种情况下,并且如果模型要部署在内存受限的环境中,使用此功能将很有意义,以防止竞技场中因初始化器内存分配而引起的任何过度增长。使用此功能意味着竞技场仅用于分配模型操作符所需的内存。似乎为竞技场设置足够高的初始块大小 (
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 隐私政策的更多信息,请参阅隐私页面。
示例
请参见 Candy 风格迁移