[Unity 소프트 광고 2019] UniEnumExtension에서 500배속 열거형 처리[Mono. Cecil]

오늘은 제 생일이라 24살 학생으로는 처음 투고합니다.
이 글은 유니티 말랑말랑 여름 이벤트 달력 2019 19일째 되는 글입니다.
며칠 전 17일 보도는 @nanaki_pg 씨의 Oculus 가속도 센서를 사용하여 VR 공간에서 이동!
전날 18일 보도는 @mao_ 씨의 "!
다음 날인 20일 보도는 @kingyo222 씨의'Unity: UI Elements로 샘플 몇 개 썼어요!'입니다!
다음 날인 21일 보도는 @yKimisaki 씨의'UIELEMENTS도 UniRx를 사용하고 싶어요.'입니다!

입문


UniEnumExtensionBoothGitHub에 공개된 편집기 확장 자산입니다.
"UniEnumExtension"을 가져오면 매거진 ToString은 원본 소스 코드를 바꾸지 않고 약 500배에서 100배 이상의 속도를 낼 수 있습니다.

버전 정보 등

  • UniEnumExtension version 0.1.1
  • Unity 2018.4209.2에서 동작 확인
  • 배포 방법


    Git 설치



    먼저 새 프로젝트를 만듭니다.

    Packages 폴더는 초기에 Analystic Library와 Package Manager UI 등을 포함합니다.
    그리고 명령 프롬프트와 터미널 등을 시작합니다.
    대상 항목 아래의 포장 폴더로 이동하십시오.
    git clone https://github.com/pCYSl5EDgo/UniEnumExtension
    의 명령.
    아래의 표시일 것이다.

    유니티 편집기로 돌아가 봅시다.새로운 UniEnumExtension 및 MonoCecil이 늘었을 거예요.

    설치는 여기서 끝냅니다.간단하네!

    Booth에서 구입 시


    다운로드한 zip 파일을 현장에서 압축을 풀다.
    UniEnumExtension.유니티 패키지 파일이 나타날 수 있으므로 드래그 앤 드롭 등을 통해 프로젝트로 가져오십시오.
    라이센스 - jp 즉 개발자는 전원을 편집기로 확장하고 컴퓨터마다 하나의 사용과 양도와 재발포 금지 등을 규정했다.


    Assets/Plugins/UniEnumExtensionInstaller는 GitHub에서 UniEnumExtension만 설치하는 설치 프로그램입니다.
    Packages/UniEnumExtension이 확인되면 삭제할 수 있습니다.

    사용 방법


    아무것도 하지 않아도 열거형의 성능이 좋아진다.
    ...이러면 해설이 외롭기 때문에 좀 더 구체적으로.메뉴에서 [윈도우] > [UniEnumExtension]을 클릭하면 다음 창이 열립니다.

    ProcessRewriteToString All Assemblies가 선택되었지만 이 옵션을 선택하면 모든 Player 구축의 asmdef를 처리합니다.
    우리 체크를 떼어 봅시다.

    UniEnumExtension은 컴파일 시간을 처리할 수량에 비례하여 증가시킵니다.열거 유형이 없는 구성 요소를 제외하면 처리 시간이 단축됩니다.
    수동으로 측정한 부품당 0.05~0.2초 정도의 처리 시간이 걸렸습니다.

    왜 이 자산이 필요합니까?


    Enums.NET.
  • ToString 등도 반사되어 캐시가 유효하거나 유효하지 않으며 방법에 따라 다르다
  • System.Enum의 접근 방식은 거의 일반적이지 않으며 쓸모없는 typeof() 가 필요합니다.
  • 두 번째 파라미터는object형을 요구하기 때문에 BoxingAn 권투
  • 가 자주 발생한다
  • 반환 값의 유형은 T[]가 아닌 Array이므로 역할 할당 필수
  • 그럼, Enums.NET는 EnumsNET 네임스페이스와 Enums 정적 클래스를 정의하여 이러한 문제를 해결합니다.
    그 결과 ToString의 매거 속도는 표준에 비해 45배 향상되었다.
    이것은 아주 좋은 일이다.
    UniEnumExtension의 속도는 표준보다 300-500배 빠릅니다.
    모노야.Cecil의 정적 IL 해석을 통해 후면 컴파일할 때 권투하는 방법 호출과 가상 방법 호출 등을 상수로 바꾸거나 신속하고 분배가 적은 비가상 방법 호출로 바꾸어 실현한다.
    또한 완전히 다른 기능이지만 Burst Job에서 foreach를 사용할 수 있습니다!

    어떤 상황에서 고속화를 진행합니까

  • 처리 대상 구성 요소에 포함된 열거 유형의 모든 ToString 호출1^
  • Enum.상수 (특정 유형 이름), 상수 (문자열 유형 변수) 는 상수 포함 또는 빠른 함수 호출 2^ 로 바뀝니다.
  • 특히 두 번째 파라미터가 상수일 때 진위값의 상수 삽입으로 대체
  • Enum.가져오기 값(특정 유형 이름)이 빠른 그룹 생성 및 초기화로 바뀝니다
  • HasFlag(Enum)에 나타나는 두 번의 권투와 가상 방법 호출은 0번의 권투와 구체적인 방법 호출 또는 상수 삽입으로 교체됩니다
  • 처리 대상 구성 요소에 포함된 열거형에 IEquatable<열거형>을 설치하여 System을 구현합니다.Collections.Dictionary의 TKey로 지정할 때 동작이 빨라질 수 있음
  • 고급 테마


    Packages/UniEnumExtension/BuildPlayer/EnumExtensionPostBuildPlayerScriptDll.cs 및 Packages/UniEnumExtension/UI/Programcs를 보십시오.
    EnumExtensionPostBuildPlayerScriptDll.cs는 Mono 구축 또는 IL2 CPP 구축 시 후기 프로세스 IL 편집을 수행합니다.
    실현UnityEditor.Build.IPostBuildPlayerScriptDLLs의 유형은 구축할 때 자신도 모르게 실례화되어 리셋이라고 불린다.콜백은 구축에 사용되는 모든 C# 파일을 컴파일하고 DLL을 제공할 때입니다.
    public void OnPostBuildPlayerScriptDLLs(BuildReport report)
    {
        step[0] = BeginBuildStep.Invoke(report, uniEnumExtension);
        try
        {
            Implement(report);
        }
        finally
        {
            EndBuildStep.Invoke(report, step);
        }
    }
    
    리셋에는 특별한 필요가 없지만 UnityEditor.Build.Reporting.BuildReport 의 internal API Begin/EndBaildStep을 사용합니다.
    공식 참고 내에 설명이 없기 때문에 구체적인 역할은 알 수 없지만 구축 시간과 오류 처리 시 정보량이 늘어날 것으로 보인다.
    private void Implement(BuildReport report)
    {
        string[] guidArray = AssetDatabase.FindAssets("t:" + nameof(ProgramStatus));
        ProgramStatus programStatus = AssetDatabase.LoadAssetAtPath<ProgramStatus>(AssetDatabase.GUIDToAssetPath(guidArray[0]));
        programStatus.Initialize();
        IEnumerable<string> targetNames = programStatus.Enables.Zip(programStatus.OutputPaths, (enable, outputPath) => (enable, Path.GetFileName(outputPath))).ToArray();
        IEnumerable<string> assemblyPaths = report.files.Where(buildFile =>
        {
            if (buildFile.role != "ManagedLibrary")
            {
                return false;
            }
            if (string.IsNullOrWhiteSpace(buildFile.path)) return false;
            string buildName = Path.GetFileName(buildFile.path);
            return targetNames.All(pair => pair.Item2 != buildName) || targetNames.First(pair => pair.Item2 == buildName).Item1;
        }).Select(buildFile => buildFile.path);
        string directoryName = Path.GetDirectoryName(report.files[0].path);
        Debug.Log(directoryName);
        using (var extender = new EnumExtender(searchDirectory: new string[1] { directoryName }))
        {
            extender.Extend(assemblyPaths);
        }
    }
    
    단일 프로필의 스크립트 대상을 읽습니다.
    그런 다음 구축 보고서의 파일 속성에서 DLL 또는 PDB 파일 목록을 얻을 수 있습니다.
    이 중 role이 ManagedLibrary이고 설정되고 처리된 DLL만 IENumerableassembly Paths로 검색합니다.
    EnumExtender 인스턴스에 Extend 메서드를 전달하면 해당 구성 요소에 대한 IL 개작이 수행됩니다.
    EnumExtender 구조 함수는 어셈블리 참조를 해석하는 데 사용할 디렉토리 이름을 제공합니다.
    어셈블리 참조 해결?이런 사람은 보세요Mono.Cecil 시작.

    고급 API


    using(EnumExtender extender = new EnumExtender(string[] searchDirectory))
    extender.Extend(IEnumerable assemblyPaths);
    문서 레지스트리에 항목을 추가합니다.
    assembly Paths에서 경로를 지정하면 여러 가지 일을 잘 처리할 수 있습니다.

    하위 API


    EnumExtender에는 구조 함수가 하나 더 있습니다.
    public EnumExtender(IModuleProcessor[] moduleProcessorCollection, ITypeProcessor[] typeProcessorCollection, IMethodProcessor[] methodProcessorCollection, string[] searchDirectories)
    UniEnumExtension.IModuleProcessor, UniEnumExtension.ITypeProcessor, UniEnumExtension.IMethodProcessor는 모듈(구성 요소), 유형 및 방법을 처리하는 인터페이스입니다.
    구조 함수에서 IL 처리를 등록하는 거죠.
    상기 3개의 인터페이스는 공개적이기 때문에 후면 컴파일할 때 고리를 걸어 EnumExtender를 실행하는 것이 좋습니다.
    EnumExtender를 비활성화해야 합니다.IL 쓰기를 완료할 수 없습니다.
    EnumExtender는 UniEnumExtension입니다.IExtender를 구현했기 때문에 그 단계부터 교체하는 것도 좋습니다.
    public interface IExtender : IDisposable
    {
        void Extend(IEnumerable<string> assemblyPaths);
    }
    public interface IModuleProcessor : IProcessor
    {
        void Process(ModuleDefinition moduleDefinition);
    }
    public interface ITypeProcessor : IProcessor
    {
        void Process(ModuleDefinition systemModuleDefinition, TypeDefinition typeDefinition);
    }
    public interface IMethodProcessor : IProcessor
    {
        bool ShouldProcess(TypeDefinition typeDefinition);
        void Process(ModuleDefinition systemModuleDefinition, MethodDefinition methodDefinition);
    }
    

    감상


    Mono.나는 Cecil이 뒤에서 컴파일할 때 constexpr을 진행할 수 있다는 것을 느꼈다.
    readonly 필드를 실현하고 IL 수준에서도 어떤 형식으로 표현할 수 있다면 정말constexpr입니다.
    추가 기능 요구 사항이 있는 경우 제 트위터에 DM이 있어요. 또는 GitHub으로 Issue 만들기.

    좋은 웹페이지 즐겨찾기