Unity3D) Burst에 대해

이찬구·2023년 3월 15일
0

Unity3D

목록 보기
1/3

스프린트를 마감기한보다 일찍 끝내서 케케묵은 버그들도 수정해보고, 코드 리팩토링도 해보던 도중 기존 구현되어 있는 코드에 Burst를 한 번 써볼 수 있을까 해서 시도해봤는데 결국 포기했다..ㅋㅋ

그래도 블로그도 시작하는 겸, 한 번 시도해봤던 내용을 기록으로 남겨보는 것도 의미가 있겠다 싶어서 정리해보았다.

  • 최대한 많은 내용을 담으려고 하다보니 보기에 따라 TMI로 느껴질 수 있으니, 아는 내용은 가볍게 보시면 될 것 같습니다.

Burst Compiler란?

"유니티에서 성능 향상을 위해 제공하는 컴파일러"

유니티에서 일반적인 소스코드를 컴파일 하는 방식은 중간 언어(IL)로 변환하고 각 플랫폼에 맞게 기계어로 변환하는 과정을 거칩니다. (유니티에서 지원하는 컴파일 과정과 Mono, IL2CPP는 추후 다뤄보겠습니다)

Burst는 LLVM(Low Level Virtual Machine)을 활용한 컴파일러로, 컴파일 과정에서 최적화를 통해 퍼포먼스를 높일 수 있습니다.

위 사진이 컴파일 과정인데, IL2CPP와 .Net Assembly로 변환되는 것까진 동일하다고 볼 수 있으나, IR 변환 과정부터 IL2CPP와 다르다고 보시면 되겠습니다.

유니티에서 Package Manager를 통해 Burst 패키지를 설치하면 Burst Inspector를 열 수 있는데, 요 부분에서 코드의 변환 과정이랑 에러가 있다면 에러 로그까지 확인할 수 있습니다. (디버그 창에서도 나오긴 하더라고요)

추가로, 위에 설명된 IL 과정에서의 최적화 외에도 SIMD(Single Instruction Multiple Data)를 이용한 최적화도 포함되어 있습니다. SIMD는 여러개의 동일한 연산을 동시에 처리해서 속도의 이득을 얻는 방법인데, 데이터 구조를 벡터화 했을 때 다중 벡터에 대해 동시 연산이 가능한 SIMD의 특정 명령어를 통해 데이터를 병렬적으로 처리하는 데 최적화하기 용이해지는 부분이 있습니다.

사용 방법

요렇게 빠르면 기본 컴파일러로 Burst를 지원해주면 되는 게 아닌가..? 라고 생각할 수 있지만
성능을 위해 코드에서 일부분에 적용되는 구현한 모든 C# 코드가 Burst를 적용할 수 있는 건 아닙니다. Burst는 크게 2가지 제약사항이 있는데
Job에서만 사용 가능하다는 점과 사용할 수 있는 자료형이 제한적이다라는 점입니다.

1. 사용 가능한 자료형이 제한?

Burst는 일단 Managed Type 자료형(메모리 관리를 Mono에서 하는 경우)에 대해 사용이 불가능합니다. 그래서 일반적인 객체 타입에 대해 사용이 불가능합니다. 메모리 관리를 수동으로 하는 unsafe class는 사용 가능하긴 한데, GC 대상이 아니므로 메모리 관리에 신경써야합니다.

또한 .Net에서 래핑되어 Managed되는 특정 타입(string) 또한 사용이 불가능하며, char 타입은 구조체에 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]를 지정해줘야 사용 가능합니다.

	void Update()
    {
        IJobBurstTest test = new IJobBurstTest();
        test.a = new NativeArray<char>(1, Allocator.TempJob);
        test.a[0] = 'A';
        JobHandle handler = test.Schedule();
        handler.Complete();

        if (handler.IsCompleted)
            Debug.Log(test.a[0]);

        test.a.Dispose();
    }

    [BurstCompile]
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct IJobBurstTest : IJob
    {
        public NativeArray<char> a;

        public void Execute()
        {
            a[0] = 'B';
        }
    }

제한되는 자료형에 대해 더 알고 싶으신 분은
https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/csharp-type-support.html
를 참고해주시면 될 것 같습니다.
(특히 위에서 설명한 SIMD를 활용 가능한 자료형 및 방법은 도큐먼트에 잘 정리되어 있습니다)

2. Job에서만 사용 가능?

Job은 기본적으로 멀티 쓰레드 환경에서의 병렬 처리 작업을 위해 사용되고, Burst는 병렬적인 작업에 최적화 된 친구다 보니 Burst는 Job에서만 사용 가능하도록 강제되어 있습니다.

병렬적인 작업(Ex. 맵에 있는 모든 캐릭터들이 다음 프레임에 이동할 위치를 계산)을 메인 스레드에서 돌릴 때, Job만 사용할 때, Burst랑 결합해서 사용할 때의 성능 차이는 아래 사진에서 보듯이 매우 크기 때문에 그만큼 시너지가 좋다라고 볼 수 있을 것 같습니다.

여기서 등장하는 Entity Component System(ECS)는 DOTS(Data-Oriented Technology Stack, 데이터 지향 기술 스택)는 개발 방식에 대한 이야기인데, 객체 지향적 코드에 비해 캐시 적중률을 높여 성능 이득을 얻는 방식이라고 생각하시면 되겠습니다. 이 부분도 기회가 생기면 정리해볼 수 있겠네요.

결론

Burst는 매 프레임마다 특정 요소에 대해 반복적인 코드를 돌릴 때 사용하기 매우 유용하지만, 사용 제약사항이 많은 만큼 가능하다면 초기 설계 과정에서 이를 고려하는 게 중요하다는 점을 느낄 수 있었습니다.

0개의 댓글