首页 > 其他分享 >unity下零GC反射实现思路

unity下零GC反射实现思路

时间:2025-01-11 17:57:46浏览次数:3  
标签:反射 创建 unity GC IL 使用 类型

C#反射GC怎么产生的?

C# 的反射 API 确实在某些情况下会导致频繁的垃圾回收(GC),尤其是在使用 MethodInfo.Invoke 等方法时。反射 API 的设计使得值类型在传递时需要进行装箱(boxing),这会导致额外的内存分配,从而增加 GC 的压力。

反射导致 GC 的原因

  1. 装箱和拆箱

    • 当值类型(如 intstruct 等)被传递到 object 数组中时,它们会被装箱,创建一个新的对象在堆上。这会导致额外的内存分配和 GC 压力。
    • 返回值如果是值类型,也会发生拆箱(unboxing),这同样会导致额外的内存分配。
  2. 临时对象的创建

    • 反射 API 在调用时可能会创建许多临时对象,这些对象在使用后会被 GC 回收,增加了 GC 的频率。
  3. 分代 GC

    • C# 使用分代 GC,临时对象通常会被分配在新生代中。虽然新生代的 GC 性能较好,但频繁的分配和回收仍然会导致性能问题,尤其是在游戏等对性能要求较高的场景中。

解决方案

如果你希望减少反射带来的 GC 压力,可以考虑以下几种方法:

  1. 避免使用反射

    • 尽量使用静态类型和编译时绑定,而不是动态类型和反射。通过接口、委托或泛型等方式来实现灵活性,而不是依赖反射。
  2. 使用表达式树

    • 使用 System.Linq.Expressions 命名空间中的表达式树来动态生成方法调用。这种方式可以在运行时生成 IL 代码,避免装箱和拆箱,从而减少 GC 压力。
    var param = Expression.Parameter(typeof(MyType), "x");
    var body = Expression.Property(param, "MyProperty");
    var lambda = Expression.Lambda<Func<MyType, MyPropertyType>>(body, param).Compile();
    
  3. 缓存反射结果

    • 如果必须使用反射,可以缓存 MethodInfoPropertyInfo 等反射结果,避免重复获取。
  4. 使用 IL 代码生成

    • 使用 System.Reflection.Emit 来动态生成 IL 代码,这样可以在运行时创建类型和方法,避免反射带来的性能损失。
  5. 使用 Unity 的 Job System 和 Burst Compiler

    • 在 Unity 中,可以使用 Job System 和 Burst Compiler 来处理性能敏感的代码,减少 GC 的影响。
  6. 对象池

    • 对于频繁创建和销毁的对象,可以使用对象池来重用对象,减少 GC 的压力。

总结

C# 的反射 API 设计确实会导致 GC 的频繁触发,尤其是在处理值类型时。通过避免反射、使用表达式树、缓存反射结果、使用 IL 代码生成等方法,可以有效减少 GC 的影响,提高性能。在 Unity 等对性能要求较高的场景中,合理的内存管理和对象重用策略是非常重要的。

使用 IL 代码生成

使用 System.Reflection.Emit 来动态生成 IL 代码是一种强大的技术,可以在运行时创建类型和方法,从而避免反射带来的性能损失。以下是一个简单的示例,展示如何使用 System.Reflection.Emit 创建一个动态类型并调用其方法。

示例:动态生成一个简单的类

在这个示例中,我们将创建一个动态类型 DynamicType,它有一个方法 SayHello,该方法返回一个字符串。

步骤 1: 创建动态程序集和模块

首先,我们需要创建一个动态程序集和模块。

using System;
using System.Reflection;
using System.Reflection.Emit;

class Program
{
    static void Main()
    {
        // 创建一个动态程序集
        AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
        AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);

        // 创建一个动态模块
        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");

        // 创建一个公共类
        TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicType", TypeAttributes.Public);

        // 创建一个公共方法 SayHello
        MethodBuilder methodBuilder = typeBuilder.DefineMethod("SayHello", MethodAttributes.Public, typeof(string), null);

        // 获取 IL 生成器
        ILGenerator ilGenerator = methodBuilder.GetILGenerator();

        // 生成 IL 代码
        ilGenerator.Emit(OpCodes.Ldstr, "Hello, World!"); // 加载字符串 "Hello, World!"
        ilGenerator.Emit(OpCodes.Ret); // 返回

        // 创建类型
        Type dynamicType = typeBuilder.CreateType();

        // 创建实例并调用方法
        object instance = Activator.CreateInstance(dynamicType);
        string result = (string)dynamicType.GetMethod("SayHello").Invoke(instance, null);

        // 输出结果
        Console.WriteLine(result); // 输出: Hello, World!
    }
}

代码解析

  1. 创建动态程序集

    • 使用 AssemblyBuilder.DefineDynamicAssembly 创建一个动态程序集。
  2. 创建动态模块

    • 使用 DefineDynamicModule 创建一个模块。
  3. 定义类型

    • 使用 DefineType 创建一个新的类型 DynamicType
  4. 定义方法

    • 使用 DefineMethod 创建一个公共方法 SayHello,返回类型为 string
  5. 生成 IL 代码

    • 使用 ILGenerator 来生成 IL 代码。在这个例子中,我们加载一个字符串常量并返回它。
  6. 创建类型

    • 使用 CreateType 方法完成类型的定义。
  7. 实例化类型并调用方法

    • 使用 Activator.CreateInstance 创建类型的实例,并通过反射调用 SayHello 方法。

优势

  • 性能:与反射相比,使用 IL 代码生成可以显著提高性能,尤其是在需要频繁调用的方法时。
  • 灵活性:可以在运行时根据需要动态生成类型和方法,适用于许多动态编程场景。

注意事项

  • 复杂性:使用 System.Reflection.Emit 可能会增加代码的复杂性,尤其是在生成复杂类型时。
  • 调试:动态生成的代码可能会使调试变得更加困难,因为它们在编译时并不存在。

总结

使用 System.Reflection.Emit 动态生成 IL 代码是一种强大的技术,可以在运行时创建类型和方法,避免反射带来的性能损失。通过合理使用这一技术,可以在需要动态行为的场景中提高应用程序的性能。

IL2CPP 的零 GC 实现思路

  1. 使用栈分配

    • 在 IL2CPP 中,方法调用的参数可以通过 alloca 在栈上分配。这种方式的好处是栈上的内存分配和释放非常快速,且不会产生堆上的垃圾对象,从而减少了 GC 的压力。
  2. 参数列表的处理

    • 当调用一个方法时,IL2CPP 会将参数列表转换为一个指针数组。这个数组可以在栈上分配,避免了在堆上分配内存。
    • 对于引用类型的参数,IL2CPP 直接将对象的指针放入参数数组中。
    • 对于值类型的参数,IL2CPP 会在栈上分配足够的空间,然后将值类型的内容复制到这个栈空间中,并将指向该空间的指针放入参数数组中。
  3. 返回值的处理

    • 如果方法的返回值是值类型,IL2CPP 会在栈上分配空间来存储返回值。调用方法时,会将返回值的指针传递给调用的函数,函数在执行完毕后会将结果写入这个栈空间。
    • 这种方式避免了值类型的装箱(boxing),从而减少了内存分配和 GC 的发生。

优势

  • 性能:通过在栈上分配内存,IL2CPP 可以显著提高方法调用的性能,尤其是在频繁调用的情况下。
  • 减少 GC 压力:由于大部分内存分配发生在栈上,而不是堆上,因此减少了 GC 的频率和压力。
  • 简化内存管理:栈上的内存管理是自动的,函数返回时栈空间会自动释放,避免了手动管理内存的复杂性。

注意事项

  • 栈空间限制:栈的大小是有限的,因此在处理大量参数或深度递归时,可能会导致栈溢出(stack overflow)。在这种情况下,仍然需要考虑使用堆分配。
  • 跨线程调用:在多线程环境中,栈分配的方式可能会受到限制,因为每个线程都有自己的栈空间。

总结

IL2CPP 的实现通过在栈上分配参数和返回值的内存,能够有效地减少垃圾回收的发生,从而提高性能。这种方法在处理频繁调用的场景中尤其有效,适合于游戏开发等对性能要求较高的应用场景。通过合理的内存管理和优化,IL2CPP 能够提供更好的运行时性能和用户体验。

标签:反射,创建,unity,GC,IL,使用,类型
From: https://blog.csdn.net/qq_33060405/article/details/144991429

相关文章

  • Unity Android 调用 so 卡死问题 (so 编译踩坑)
    问题描述把Unity工程编译到Android上运行后,出现了一个必现的界面卡死bug。表现为:每次轮到自己出大招时,必现界面卡死,但程序不会crash。在Unity编辑器和iOS下都无法重现。分析日志发现,卡死前最后调用了算法库中的一个函数。算法库是c++写的,用cmake组织,编译成......
  • Windows下Dll在Unity中使用的一般方式
    Windows下Dll在Unity中使用的一般方式Unity中虽然已经有广泛的库和插件,但是相较于C++的库生态而言,还是有一定的差距;因此本篇博文记录Windows下将C++函数打包成动态链接库在Unity中使用的一般方法。环境VisualStudio2019,Unity2022,Windows11,OpenCV说明Unity中有针对Wind......
  • Unity URP Shader Graph 实现复古电视机效果
    想到一出实现一出的复古电视机效果实现。复古电视机效果显示展示:使用素材一张纹理需要放映的图片,一张遮罩贴图,一个电视机模型。UV使用Spherize模拟电视机球状显示屏。扫描线A效果扫描线B效果像素化/随机UV偏移屏幕做旧效果边缘变暗效果屏幕黑边效果自制一张合适......
  • 在LangChain中使用Hazy Research生态系统
    在AI技术不断发展的今天,HazyResearch提供了一套强大的工具生态系统,能够帮助开发者在语言模型和数据处理中获得更高的效率。今天,我们将探讨如何在LangChain中集成和使用HazyResearch的生态系统,实现安装和配置,并了解如何利用具体的HazyResearch封装器来提升应用的功能。技......
  • 使用Fireworks AI与LangChain集成实现高级AI对话功能
    技术背景介绍在AI时代,能够灵活使用各种预训练模型来处理不同的文本生成任务越来越重要。FireworksAI提供了一个强大的AI推理平台,专注于运行和自定义AI模型。与LangChain集成后,开发者可以更便捷地利用多种模型进行对话生成和自定义任务。核心原理解析FireworksAI通过其......
  • 【Django】自定义中间件的注册和使用,利用反射规范代码
    在Django中创建和使用自定义中间件1.创建自定义中间件在你的Django应用目录下创建一个新的文件,通常命名为middleware.py。在这个文件中,你可以定义自己的中间件类。示例中间件#middleware.pyclassCustomMiddleware:def__init__(self,get_response):......
  • 如何通过LangChain使用KoboldAI的API进行AI辅助写作
    KoboldAI是一个浏览器端的前端工具,允许通过本地和远程的多种AI模型实现AI辅助写作。它提供了一个公共和本地API,可以与LangChain集成使用。本文将深入介绍如何通过LangChain使用KoboldAIAPI进行AI辅助写作。技术背景介绍KoboldAI提供了一个强大的界面,支持多种AI模型的集......
  • 使用LangChain集成ForefrontAI进行LLM调用
    在本篇文章中,我们将深入探讨如何在LangChain中集成使用ForefrontAI。本文将分为两部分:安装与设置,以及如何使用ForefrontAI的封装器。技术背景介绍LangChain是一个用于构建LLM(大型语言模型)应用程序的强大框架。ForefrontAI作为其中一个支持的LLM提供商,为开发者提供了灵活的......
  • 复旦附中T517829 GCD变换
    原题链接:T517829GCD变换这道题很唐氏,但是我不会(在cjy1024的指点下,这道题我会了。结论:每一次让\(x=x\cdot\gcd\{x,\frac{m}{x}\}\)。我们为了让他们尽量次数少,所以我们希望乘上\(\frac{m}{x}\),但如果gcd不满足的话,那么我们就乘上\(\frac{m}{x}\)的因数即可。误解情况......
  • [LangChain 视频学习资源一览]
    如果你对LangChain感兴趣,并计划通过YouTube视频自学,那么这里整理了一份截至2024年5月16日的LangChain官方和相关教程资源清单。这些视频内容丰富、深度适中,涵盖从入门到进阶的多个主题,是学习构建基于大语言模型(LLM)应用的绝佳资料。官方LangChainYouTube......