함수를 호출하는 오버헤드에 따른 성능 차이를 알고 싶어서 Vector3의 Normalize 방법 별 성능 테스트를 해보았다.

 

이전에도 Vector3.Normalize()와 Vector3.normalized에 대한 차이는 해봤는데 그것과 더불어 Vector3.Normalize() 내부에 있는 함수를 꺼내와서 직접 사용하면 얼마나 차이가 있을지 비교해봤다.

 

using System;
using UnityEngine;

public partial class Tester : MonoBehaviour
{
    private void OverheadTest()
    {
        Vector3 vector3 = new Vector3(2, 1, 1);
        Vector3 vector3Temp = new Vector3();

        DoTest("Vector3.normalized Test", repeatTime => {
            for (int n = 0; n < repeatTime; ++n)
            {
                vector3Temp = vector3.normalized;
            }
        });

        DoTest("Vector3.Normalize() Test", repeatTime => {
            for (int n = 0; n < repeatTime; ++n)
            {
                vector3Temp = Vector3.Normalize(vector3);
            }
        });

        DoTest("Vector3.Normalize() disassemble Test", repeatTime => {
            for (int n = 0; n < repeatTime; ++n)
            {
                float num = (float)Math.Sqrt(vector3.x * vector3.x + vector3.y * vector3.y + vector3.z * vector3.z);
                if (num > 1E-05f)
                {
                    vector3Temp = vector3 / num;
                }
                else
                {
                    vector3Temp = Vector3.zero;
                }
            }
        });
    }
}

테스트 코드는 위와 같다.

 

Vector3.normalized는 내부적으로 Vector3.Normalized()를 호출하기 때문에 함수 호출이 1번 더 발생된다.

 

Vector3.Normalize()는 위와 같이 작동하는데, 이 부분을 빼와서 돌렸다.

Math.Sqrt는 "Math.Sqrt() translates to a single floating point machine code instruction." 이라고 하여 이대로 사용하는 게 빠를 것 같아서 이대로 두었다.

 

10억번씩 5회 돌려봤고, Vector3.normalized를 사용하는 경우 143612ms, Vector3.Normalized()를 사용하는 경우 120142ms, Vector3.Normalized() 내부를 꺼내와서 사용한 경우 78946ms가 소요되었다.

 

Vector3.Normalized()를 사용하면 Vector3.normalized를 사용하는 것보다 16.3%정도 빠르다.

Vector3.Normalized() 내부를 꺼내와서 사용하면 Vector3.normalized를 사용하는 것보다 45%나 빠르다.

 

차이가 생각보다 큰데, 테스트삼아 스크립트에 특별한 기능을 하지 않는 함수 참조를 0회~10회 하도록 만들어서 비교해봤지만, 20억 번씩을 돌려봐도 별 차이가 없어서 약간 의아했다. Vector3의 경우 외부에서 참조해오는 것이라 차이가 많이 생기는 것일 수도 있을 것 같다.

 

뭐, 아무튼... Vector3.Normalized()를 사용하는 것이 약간은 더 좋을 것 같다. 내부에서 꺼내와서 사용하는 것은 성능상으로는 좋을지 몰라도 일반적인 상황에서 딱히 좋은 방법은 아닐 것이라고 생각한다.

 

 

객체지향이란 무엇일까 최근에 생각하고 있는데, 성능을 일부 포기하고 가독성과 재사용성을 높여 생산성을 끌어올리는 방법이라고 생각하게 되었다. 조금 더 나아가자면 가독성을 일부 포기하면서까지 재사용성을 극단적으로 높이면서 생산성을 향상시키는 방법이라고 생각한다.

 

때문에 절차지향 프로그래밍에 비해서 속도가 얼마나 느릴까... 하는 생각으로 비교를 해봤는데, Vector3를 호출하는 경우는 생각보다 차이가 심하고, 같은 스크립트 내에서 참조하는 경우에는 전혀 차이가 없어서 조금 알쏭달쏭한 것 같다.

 

당연히 오버헤드에 대한 영향은 무조건 있겠지만, 언어 내부적으로 최적화를 통해서 비슷한 위치에서는 오버헤드에 따른 성능 저하가 거의 없고, Vector3와 같이 외부에서 호출하는 경우에는 그 영향이 크게 나타나는 것이 아닐까 싶다.

 

MethodImplOptions.AggressiveInlining에 대해서도 찾아봤는데 컴파일러가 해당 함수의 코드 전체를 호출한 코드 위치로 복사하여 대체하기 때문에 오버헤드를 줄여주는 키워드라고 한다. 아마 1번 실행할 때는 크게 차이가 없을 것 같고, 반복문 등에서 의미가 크게 나타날 것 같다.

 

.NET Framework: 2016. C# - JIT 컴파일러의 인라인 메서드 처리 유무 (sysnet.pe.kr)

 

.NET Framework: 2016. C# - JIT 컴파일러의 인라인 메서드 처리 유무

.NET Framework: 2016. C# - JIT 컴파일러의 인라인 메서드 처리 유무 [링크 복사], [링크+제목 복사] 조회: 3151 글쓴 사람 정성태 (techsharer at outlook.com) 홈페이지 첨부 파일 부모글 보이기/감추기 (연관된

www.sysnet.pe.kr

 

그리고 찾아보니 이런 글도 있었는데, JIT 컴파일러에서는 자체적으로 인라인을 하는 것에 따른 비용과 효과를 판단해서 인라인을 해주기도 하고, 안 해주기도 한다는 것 같다. 음... 내가 모르는, 성능 향상을 위한 노력들이 많구나 하는 것을 다시금 느끼게 되었다.

 

근데 그렇다면 인라인하는 처음 1번을 제외하고는 함수를 인라인한 테스트들과 같은 실행시간을 가져야할 것 같은데... 음... 결과를 보면 그렇지만도 않은 모양이다. 아마 개선을 해서 이 정도의 시간이 소요되는 것이 아닐까... 라고 생각해본다.

+ Recent posts