
PuerTS Unity il2cpp 优化特性全解析原理、开启方式与三种胶水代码生成模式【免费下载链接】puertsPUER(普洱) Typescript. Lets write your game in UE or Unity with TypeScript.项目地址: https://gitcode.com/GitHub_Trending/pu/puerts导读本文围绕 PuerTS普洱在 Unity 平台提供的il2cpp 优化特性xIl2cpp 模式展开系统讲解其绕过 PInvoke、直接通过 il2cpp 接口访问 C#的性能优化思路、不同版本下的默认开关与宏配置、三种胶水代码Wrapper生成模式的选择策略以及 iOS 构建时的常见问题排查。读完本文你将能够在自己的 Unity 工程中正确开启该特性、按性能/包体诉求选择对应的生成菜单并独立解决 iOS 构建中的典型报错。适用版本PuerTS 2.0.0 以上版本。一、il2cpp 优化的核心原理绕开 PInvoke 的跨语言通道在默认的 PInvoke 通道下TypeScript/JavaScript 调用 C# 方法时需要经过一整套跨语言桥接参数在脚本侧与 C# 侧之间进行编解码、类型登记、对象池管理等每一次调用都有可观的固定开销。而 il2cpp 优化的思路非常直接——绕过 PInvoke直接通过 il2cpp 的接口访问 C#从而大幅削减跨语言的调用消耗。从源码结构可以印证这一设计PuerTS 在运行时将 C# 侧实现按条件编译分为两套互斥的实现集合二者的开关由同一组宏控制优化实现位于 unity/upms/core/Runtime/Src/IL2Cpp/其中 NativeAPI.cs、ScriptEnv.cs、ObjectPool.cs、TypeRegister.cs、JSType/ScriptObject.cs 均以#if !PUERTS_DISABLE_IL2CPP_OPTIMIZATION ENABLE_IL2CPP编译传统 PInvoke 实现位于 unity/upms/core/Runtime/Src/PInvoke/以#if PUERTS_DISABLE_IL2CPP_OPTIMIZATION || !ENABLE_IL2CPP编译两者正好互补。进一步观察 NativeAPI.cs 可以看到优化模式下的关键差异大量入口不再走[DllImport]的 PInvoke 声明而是改用[MethodImpl(MethodImplOptions.InternalCall)]直接声明为 IL2CPP 内部调用方法配合InitialPuerts、InitialPapiEnvRef等 C 侧注册的回调完成 C# 与脚本引擎之间的直连。也就是说跨语言调用被编译期内联进 IL2CPP 生成的 C 代码中运行时不再需要经过 PInvoke 的 marshaling 与查找。结论il2cpp 优化本质上是一次架构级降耗——把高频的跨语言调用从解释式桥接变成静态编译产物中的直接函数调用这也是性能大幅提升的根本来源。完整的实测数据可参考 性能表现文档。二、如何开启与关闭版本差异与 Define Symbols 配置该特性的默认状态在不同版本区间有差异开启/关闭统一通过 UnityPlayer Settings中的Scripting Define Symbols配置宏完成。请对照你使用的版本按下表操作版本区间默认状态关闭方法手动开启方法2.1.1 及以下全平台关闭—添加PUERTS_IL2CPP_OPTIMIZATION2.2.0 ~ 2.2.1Windows / macOS / Linux / Android 默认开启添加PUERTS_DISABLE_IL2CPP_OPTIMIZATIONiOS / WebGL 下添加PUERTS_IL2CPP_OPTIMIZATION2.2.2 及以上Windows / macOS / Linux / Android / WebGL 默认开启添加PUERTS_DISABLE_IL2CPP_OPTIMIZATIONiOS 下添加PUERTS_IL2CPP_OPTIMIZATION操作路径Edit Project Settings Player Other Settings Scripting Define Symbols按平台如 iOS、Android分别配置后重新编译。需要特别说明的是PUERTS_DISABLE_IL2CPP_OPTIMIZATION是一个显式关闭宏只要它存在无论ENABLE_IL2CPP是否成立PInvoke 实现都会被启用见上文条件编译宏的反向判断。因此在默认开启的平台上如果你希望回退到传统 PInvoke 通道排查问题只需添加PUERTS_DISABLE_IL2CPP_OPTIMIZATION这一个宏即可无需删除任何代码。从源码看宏的生效还依赖平台宏ENABLE_IL2CPP只有在 IL2CPP 脚本后端而非 Mono下构建时优化路径才会真正参与编译。这意味着在编辑器Editor或 Mono 模式下即使不配置任何宏代码也始终走 PInvoke 实现该特性只在发布构建的 IL2CPP 产物中起作用。三、使用步骤三种胶水代码生成模式开启特性后还需要为 IL2CPP 构建生成对应的 C 胶水代码。Unity 菜单入口统一位于Tools/PuerTS/Generate il2cpp/三个菜单项分别对应三种不同的生成策略其行为可在 UnityMenu.cs 中找到一一对应的实现3.1 Static Wrapper Mode追求更高性能菜单路径Tools/PuerTS/Generate il2cpp/Static Wrapper Mode这是生成全量胶水代码的模式源码实现为GenV2()调用CSharpFileExporter.GenAll(saveTo, false, false)。它会扫描当前AppDomain中的全部公共类型过滤掉泛型类型定义与大值类型为构造函数、方法、字段逐一生成静态包装器Wrapper。正如性能数据所示这一模式下的跨语言调用如puer X S列普遍明显快于不生成包装器的模式适合对性能有极致要求的场景。生成的产物包括由 CSharpFileExporter.GenCPPWrap 输出到 il2cpp 插件目录PuertsIl2cppWrapper.cpp包装器声明与查找表PuertsIl2cppWrapperDefN.cpp按每文件 1000 个包装器拆分实现的包装器定义N 为序号PuertsIl2cppFieldWrapper.cpp字段访问包装器PuertsIl2cppBridge.cpp委托/回调桥接PuertsValueType.h值类型struct的布局信息。3.2 Reflection Mode追求更小代码量菜单路径Tools/PuerTS/Generate il2cpp/Reflection Mode这是仅生成基于反射的胶水代码的模式源码实现为GenV2WithoutWrapper()调用GenAll(saveTo, false, true)。生成器会把包装器集合置空genWrapperCtor/Method/Field全部清空仅保留 Bridge 与值类型信息运行时通过反射完成对 C# 成员的调用。其优点是生成的 C 代码量显著减小、构建产物更精简代价是跨语言调用性能弱于全量包装器模式。3.3 Minimal Bridge, Reflection Mode进一步精简产物菜单路径Tools/PuerTS/Generate il2cpp/Minimal Bridge, Reflection Mode该模式在反射模式基础上再进一步源码实现为GenMinimumWrappersAndBridge()调用GenAll(saveTo, true, true)。从 CSharpFileExporter.cs 的onlyConfigure true分支可以看出它不再扫描全量类型而是只收集标注了Puerts.BindingAttribute的配置类型及其委托、UsingAction/UsingFunc泛型委托调用点将生成的 Bridge 数量压缩到最小。适合希望把生成产物尤其是 Bridge 代码压到极限、且能接受运行时反射开销的场景。3.4 生成产物输出位置三种模式生成的文件统一写入 il2cpp 插件目录具体路径由 PathHelper.GetIl2cppPluginPath() 决定默认代码输出目录/Plugins/puerts_il2cpp/定义了CPP_OUTPUT_TO_NATIVE_SRCAssets/core/upm/Plugins/puerts_il2cpp/定义了PUERTS_CPP_OUTPUT_TO_UPMPackages/com.tencent.puerts.core/Plugins/puerts_il2cpp/。同时生成器还会输出ExtensionMethodInfos_Gen.cs扩展方法信息与link.xml用于避免 IL2CPP 裁剪掉反射所需的类型/成员见 CSharpFileExporter.GenExtensionMethodInfos / GenLinkXml。3.5 模式选择建议需求导向推荐模式备注极致性能、不在乎代码量Static Wrapper Mode跨语言调用最快兼顾体积与可用性Reflection Mode代码量小、性能可接受产物/ Bridge 最精简Minimal Bridge, Reflection Mode需配合[Puerts.Binding]配置使用四、性能表现参考官方在性能表现文档中给出了基于社区基准项目基于 xLua 基准改造的对比数据。数据列定义如下Puer S不使用xIl2cpp 模式但生成了StaticWrapperPuer X R使用xIl2cpp 模式但没有生成xIl2cpp StaticWrapperPuer X S使用xIl2cpp 模式且生成了xIl2cpp StaticWrapper。以安卓Vivo Neo6SEvoid Payload(int, int, float)生成 Wrapper20 万次调用为例xLua 耗时 37.4mspuer S38.0ms而puer X S仅 16.0msQuaternion Payload(Transform, Vector3)场景下puer S152.0ms /puer X S47.0ms优化幅度显著。整体上极限配置xIl2cpp 生成 Wrapper在跨语言调用场景下相比未优化状态有大幅收益跨语言性能约为 xLua 的 2 倍iOS 上两者接近持平无参或基本类型参数时略慢对象参数时略快。脚本自身执行性能方面由于 Lua 5.3 无 JIT安卓上 PuerTSV8 引擎明显占优。需要说明以上为官方文档给出的实测数据实际表现会受机型、Unity/IL2CPP 版本、测试基准变动等因素影响官方也注明数据曾随基准修正而更新建议以自身工程为准进行基准测试。五、iOS 构建 FAQ5.1 报错hash_map头文件找不到现象iOS 构建Xcode 工程编译时报hash_map头文件缺失。原因与解决Unity 构建时一部分头文件不会自动打包进产物 Xcode 工程在 Unity 2021 及以下版本较常见。可在本机你的Unity.app/Contents/il2cpp/external/目录下找到缺失的内容将其复制到iosbuild目录/Libraries/external/即可。external目录中包含 il2cpp 依赖的第三方头文件如 EASTL 等PuerTS 生成的 C 胶水代码编译时需要这些头文件参与。5.2 报错ReentrantLock is ambiguous现象Unity 2022 版本构建 iOS 时常见。原因il2cpp 的il2cpp-config.h中baselib相关命名空间展开后与 PuerTS 代码中的ReentrantLock产生二义性。解决修改il2cpp-config.h路径形如/Applications/Unity/Hub/Editor/你的Unity版本/PlaybackEngines/iOSSupport/il2cpp/libil2cpp/il2cpp-config.h按实际安装路径调整。在#pragma once之后加入宏定义#pragma once #define BASELIB_INLINE_NAMESPACE il2cpp_baselib //this line fix ReentrantLock is ambigious #include string.h修改后重新构建 iOS 工程即可。该问题的详细分析可参考 PuerTS 仓库对应 issue原文档中引用的 issue 编号为 Tencent/puerts#1428。六、总结PuerTS 的 il2cpp 优化特性通过绕过 PInvoke、直连 il2cpp 接口从根本上压缩了跨语言调用开销并以三档生成模式提供了性能—代码量之间的灵活权衡追求性能选Static Wrapper Mode追求精简选Reflection Mode极限精简选Minimal Bridge, Reflection Mode。在 2.2.2 及以上版本中主流桌面/移动平台Windows、macOS、Linux、Android、WebGL已默认开启iOS 需手动添加PUERTS_IL2CPP_OPTIMIZATION回退旧通道则统一使用PUERTS_DISABLE_IL2CPP_OPTIMIZATION。配合本文的 iOS 构建排错指引即可在生产工程中稳定落地这一优化。【免费下载链接】puertsPUER(普洱) Typescript. Lets write your game in UE or Unity with TypeScript.项目地址: https://gitcode.com/GitHub_Trending/pu/puerts创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考