Unity 검색

업데이트 가이드: Unity Burst에서 Neon 내장 함수를 사용하는 방법

2021년 7월 30일 게임 | 8 분 소요
Header image showing someone working in Unity
Header image showing someone working in Unity
다루는 주제
공유

Is this article helpful for you?

Thank you for your feedback!

Example chart of neon intrinsic multiply accumulate
그림 1: Neon 내장 함수 Multiply Accumulate의 예

Unity는 최근 Arm의 Neon 내장 함수를 추가한 Burst 1.5를 출시했습니다. Neon 내장 함수를 사용하면 정확한 벡터 명령어를 지정하여 Arm CPU에서 워크로드를 처리하는데 가장 효율적인 코드를 얻을 수 있습니다. 보통은 C/C++로 작성되어 있지만 Unity는 C#으로 도입했습니다.

사용 목적에 가장 적합한 명령어를 파악하는 데 도움을 주기 위해 Arm은 Unity에서 Neon 내장 함수를 사용하는 방법에 관한 가이드사용 가능한 코드가 포함된 Unity 프로젝트를 만들었습니다. 이 가이드는 Neon을 자동으로 사용하도록 Burst 코드를 구성하는 데 도움을 주며, 내장 코드를 직접 작성하지 않아도 성능을 크게 향상할 수 있습니다. Neon 내장 함수를 최대한 활용하는 몇 가지 베스트 프랙티스를 살펴 보겠습니다.

자동 벡터화

Burst 컴파일러를 사용하면 성능을 최대한 확보하기 위해 직접 내장 함수를 추가할 필요가 없습니다. 물론 다양한 방법으로 Burst를 지원해 성능을 추가로 향상할 수도 있습니다. 예를 들어 데이터와 루프 구조를 조정하여 자동 벡터화를 실현할 수 있으며, 이는 큰 폭의 성능 향상으로 이어집니다.

컴파일러에서 네 개의 명령을 하나의 Neon SIMD 명령으로 변환하려면 break 문이 없는 짧고 간결한 루프에 인라인 함수를 사용하도록 합니다. 또한 Burst 함수로 전달되는 모든 포인터에 [NoAlias] 속성을 사용하면 물리 충돌에 대한 활용 사례에서 제시된 대로 최대 4배까지 속도가 빨라집니다.  

Demo scene of neon intrinsics
그림 2: 데모 씬

개발자 이야기

이 샘플 활용 사례에서는 물리 충돌에 집중하며, 따라서 위를 보면 간단한 캡슐과 직육면체 그래픽스가 표시되어 있습니다. 여기에서는 캐릭터와 벽 간의 충돌에 관한 AABB(Axis-Aligned Bounding Boxes, 축 정렬 바운딩 박스) 및 캐릭터 간 충돌에 관한 반경 기반 바운딩 박스의 두 가지 충돌 유형을 최적화했습니다.

컴파일러를 능가하는 내장 함수를 직접 만드는 것은 어렵지만 이 활용 사례에서는 다양한 접근 방식을 제시합니다. 성능 향상은 단일 목표가 아니라 일련의 과정입니다. 측정과 최적화를 반복하면서 프로파일링을 통해 루틴에 걸리는 시간을 확인한 다음, 코드를 수정하고 다시 시간을 측정합니다. Profile Analyzer를 사용하거나 고유의 타이밍을 적용하여 이를 수행할 수 있습니다.

이제 코드 수정 작업을 살펴보겠습니다. 이번 활용 사례에서는 Burst 잡에서 Burst 정적 함수로 변경했으며, 덕분에 타이밍을 더 쉽게 달성할 수 있었습니다. 성능 타이밍으로 인해 복잡도가 커지기는 하지만, 최종 게임 잡에서 비동기성은 훌륭한 기능입니다. 실제 게임에서는 ProfilerMarker, ProfilerRecorder, ProfileAnalyzer를 사용하여 잡 내에서 타이밍을 측정합니다. 하지만 여기에서는 Burst 정적 함수로의 이동이 실제로 자동 벡터화에 필요한 변경 사항을 시행하는 데 도움이 되었습니다. Burst 정적 함수를 통해 구조체의 NativeArray를 사용하도록 잡이 설정되면 기본적인 타입에 보다 간단하게 포인터를 사용할 수 있게 됩니다. 이렇게 하면 데이터가 더 쉽게 벡터화할 수 있는 단위로 분해됩니다. [NoAlias] 속성이 포인터에 추가되고 나면 포인터를 사용했던 데이터에 중복이 있는지 컴파일러에 알려줍니다. 이번 활용 사례에서는 일반 Burst의 성능이 너무 강력하여, 이를 능가하기 위해 매우 뛰어난 Neon 코딩이 필요했습니다. Neon을 완전히 활용하기 위해 두 가지 충돌 유형 각각에 적절한 데이터 및 논리 구조가 필요했습니다.

벡터화는 적절한 Neon 명령어를 사용하여 한 번에 동일한 작업을 완료할 수 있도록 네 개 또는 여덟 개 오브젝트를 동시에 비교할 수 있는 경우에 가장 효과적으로 작동합니다. 이 업데이트 가이드에서는 최대 성능을 유지하는 사례를 살펴봅니다.

AABB 비교를 사용하는 벽 충돌 사례의 코드 살펴보기

기본 코드:

Burst 정적 함수로 호출된 코드:

Neon 내장 함수 명령어의 Burst 정적 함수를 통한 코드:

chart showing neon is 10X faster in AABB collisions and 3.5X faster in arm neon

전체 가이드 확인

전체 프로세스와 실제 사례를 확인하고, 프로젝트에 적용하는 방법을 알아보려면 가이드를 읽어보고 작성해둔 코드도 살펴보세요. 최적화 방법 중 하나만 사용하더라도 Burst를 사용하지 않은 잘 짜여진 코드에 비해 버스트 코드를 6배 더 빠르게, 직접 작성한 Neon 코드는 10배 더 빠르게 만들 수 있습니다.

Neon에 관한 자세한 정보는 Arm의 Neon 마이크로사이트를 참고하세요. 대부분 C/C++ 내장 함수를 대상으로 하지만 적용되는 원리는 같습니다. 또한 Unity에서 사용할 수 있는 Neon 내장 함수 목록과 Arm의 Neon 내장 함수 검색 엔진도 꼭 살펴보세요.

 

이 포스팅은 Arm의 디벨로퍼 애드보킷인 Ben Clark와 함께 작성했습니다.

2021년 7월 30일 게임 | 8 분 소요

Is this article helpful for you?

Thank you for your feedback!

다루는 주제