Post

[ STUDY/유니티 ] 2023. 1. 22. 15:57

null 체크를 하기위해서 다양한 방법들을 알아보았습니다.

그러던 중 신기한 것을 발견하였죠.

 

바로 같은역할을 하는 친구들이 모두 속도가 다르단 것입니다!

is, Equals ,==

(ReferenceEquals의 경우에는 is와 속도가 거의 같아서 is로 대체하였습니다.)

 

테스트코드

테스트 코드

 void Start()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        Test test1= TestClass1;

        for (int i=0;i<100000000;i++)
        {
            if ( test1 == null);
        }
        sw.Stop();

        UnityEngine.Debug.Log("Object == null : " + sw.Elapsed);
    }

일단 앞선 게시물에서 is가 ==보다는 왜 빠른지 알았습니다.

근데 Equals(object,object).. 이 친구는 ==보다는 빠른데, is보다는 느립니다..

어떤 원리일까요? 좀 더 안전하게 속도를 빨리 할 수 있는걸까요?

 


 

일단 FakeNull을 체크할 수 있는 지 확인해봅시다.

IEnumerator Start()
    {
        GameObject test = new GameObject();
        yield return null;


        Destroy(test);
        yield return null;

        Debug.Log("UnityEngine Null check : " + NullCheckUnityEngine(test));
        Debug.Log("System Null check : " + NullCheckSystem(test));
    }

    string NullCheckUnityEngine(Object obj)
    {
        if (obj == null)
            return "true";
        else
            return "false";            
    }
    string NullCheckSystem(object obj)
    {
        if (obj == null)
            return "true";
        else
            return "false";
    }

일단, ==은 fake null체크를 진행합니다. 그 결과 true, false로 나오죠.

 

 

Equals(,)의 경우에는 어떨까요?

IEnumerator Start()
    {
        GameObject test = new GameObject();
        yield return null;


        Destroy(test);
        yield return null;

        Debug.Log("UnityEngine Null check : " + NullCheckUnityEngine(test));
        Debug.Log("System Null check : " + NullCheckSystem(test));
    }

    string NullCheckUnityEngine(Object obj)
    {
        if (Equals(obj, null))
            return "true";
        else
            return "false";            
    }
    string NullCheckSystem(object obj)
    {
        if (Equals(obj, null))
            return "true";
        else
            return "false";
    }

이런.. fake null까지 체크하진 않군요.

 

실제 찾아보니 ==의 경우에는 UnityEngine.Object와 정의가 연결되어 있고

Equals나 ReferenceEquals 등 fakenull 을 체크하지 않는 친구들은 System.Object에 정의되어있습니다.

 

Equals(object, null)같은 경우는 fakenull을 체크하지 않아 연산에서 좀 더 시간이 빠르단걸 알게되었습니다.

그럼 왜 is나 ReferenceEquals보다는 느린걸까요??

 


둘다 시스템 상 null만 체크하는건 동일하지만

 Reference.Equals(object, object)의 경우에는 두 개체의 참조만을 비교합니다.

참조만을 비교하니 서로 가리키는 것이 무엇인지만 파악하면 되기에 속도가 빠른것같습니다.

 

object is null의 경우에는 상수 또는 Null만을 비교함으로써

두가지의 경우만 있기에 좀 더 속도가 빠르게 진행되는것같습니다.

 

Equals(object,null) 의 경우에는 두개의 값 모두 null인지를 먼저 체크 한 다음에 값의 비교를 진행합니다

null인지도 체크하고, 그 다음에 값이 같은지까지 체크하니 좀 더 속도가 걸리는것같습니다.

 

그래서 결론적으로 속도차이는

object.Reference.Equals(object,null) = object is null  >  Equals(object,null)  >  object == null

이렇게 결론이 나게됩니다.

 

 

자세한 함수의 코드는 잘 모르지만 나와있는 문서의 정보라던가 사람들의 이야기를 들어보고 정리한 결과,

해당 함수가 어떤 기능을 수행하는지를 알게되어 속도가 왜 차이가 나는지에 대해서도 어느정도 이해가 되었습니다. 

 

하지만 무조건 제 말이 옳은건 아닙니다.

저도 단지 원리를 보고 파악하는것이기때문에 피드백이 있다면 언제든지 환영입니다!

 

이걸로 null에 대한 여정 끝!  

 

 

참고자료

https://stackoverflow.com/questions/34895925/difference-between-object-equalsobject-object-and-object-referenceequalsobjec

 

difference between Object.Equals(object,object) and Object.ReferenceEquals(object,object) in c#

I have asked by an Interviewer in an Interview that "difference between Object.Equals(object,object) and Object.ReferenceEquals(object,object)". I have tried in code snippet but the result is same.

stackoverflow.com

https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/operators/is

 

'is' 연산자 - 식과 형식 또는 상수 패턴 일치

패턴에 대한 식과 일치하는 C# 'is' 연산자에 대해 알아봅니다. 식이 패턴과 일치하면 'is' 연산자는 true를 반환합니다.

learn.microsoft.com

 

Post

[ STUDY/유니티 ] 2023. 1. 21. 21:58

이전에 null 비교를 위한 연산으로 ==와 Equals를 보던 중 같은 기능을 수행하는  is라는 연산자를 발견하였습니다.

그럼 is란 무엇인가?

여기서 is란?

연산자로써 C#7 이전에는 객체의 타입을 체크하는 용도로 쓰였지만,

C#7 이후부터는 패턴으로써 사용할 수 있게되었습니다. 

 

패턴은 크게 상수패턴/ 타입패턴 / var 패턴으로 나뉘어져있고

여기서 이번 게시물과 밀접한 관련이 있는 패턴은 상수패턴입니다.

 

상수패턴은 특정 상수값을 사용해 패턴을 체크함으로써 ==과 같은 기능을 합니다.

(instance is null 과 instance==null은 같게 결과가 나옴.. 처럼 보이지만 내막이 있는데...)

 

하지만 기능이 같다고 아예 같다란 것은 아닙니다.

제가 본 글들은 is가 ==보다 속도면에서 우수하다고 하는데

그 이유를 제가 자세히 몰라  왜 그런걸까 한번 알아봤습니다.


먼저, 유니티에는 두가지 오브젝트가 있습니다.

 

하나는 System.Object이고, 다른 하나는 UnityEngine.Object입니다.

이 두개가 나뉘는 이유는 유니티 엔진은 C++로 만들어져있습니다.

다만, 유니티에서 .NET API를 노출하여 우리는 C++대신에 C#을 이용해 개발을 할 수 있는거죠.

 

System.Object(=object)를 통해 시스템상 오브젝트(C#)를 확인하고,

UnityEngine.Object(=Object)를 통해 유니티 엔진상 오브젝트(C++)을 확인할 수 있습니다.

 

이 오브젝트들은 평소에는 신경을 쓰지 않고 개발을해도 크게 문제는 안되지만

Destroy를 할 경우 문제가 발생하게 됩니다. 

 

C++에서는 메모리를 동적할당/해제가 가능하지만, C#에서는 메모리를 사용자가 할당하고 해제할 수 없죠.
그런 상황에서 Destroy를 했을 경우에 속에 C++로 이루어진 시스템상 오브젝트는 NULL 이 되지만
C#으로 겉모습을 유지하고있는 오브젝트는 GC가 호출되어 메모리를 정리할때까지 NULL이 아니게됩니다.
유니티 엔징 상(C++)에서는  NULL이지만 시스템 상(C#)으론 NULL이 아닌 이 현상을 Fake Null이라고 합니다.


이런 Fake Null 현상을 잡아내기 위해

UnityEngine.Object 클래스에서는 같음연산자(==)를 오버로딩하여

네이티브 객체가 존재하는지 여부까지 판단하여 비교 후 결과를 돌려줍니다. 

 

하지만  is의 경우에는 (ReferenceEquals함수도 마찬가지)

실재여부 판단까지 하지않고 값만 판단하기 위해서
원시 오브젝트 자체로 비교하여 null check 비교과정을 생략하여 속도를 향상시킵니다.

 


그럼 속도가 빠른 is가 마냥 좋은걸까요?

그건 아닙니다.

 오브젝트가 Destroy가 되는 상황이 아예 없을 경우에는 is 나 ReferenceEquals가 속도면에서 좋을 수도 있지만
 그런 보장된 상황이 아니라면 좀 더 안정적으로 추가검증을 해주는 == 로 체크하는것이 좋을 것입니다.

 

무조건 빠르다고 쓰는것이 아닌

적재적소에 맞게 사용하면되는것이 결론!

 

 

 


 근데.. 솔직히..NULL체크 자주 쓰이는것도 아니라면..굳이?

 

 

 

 

 

 

Post

[ STUDY/유니티 ] 2023. 1. 18. 05:08
 if (instance == null)
        {
            instance = this;
        }
        else if (instance != this)
            Destroy(gameObject);

싱글톤 코드를 작성하면서  들었던 의문이 있었습니다.

==대신에 Equals()를 써서 null인지를 체크할 수는 없을까?

 

Equals로 인한 비교는 총 두가지 방법이 있습니다.

매개변수를 1개 쓰는것과 2개쓰는것 

 

instance.Equals(null) 을 사용했을때 뜨는 에러

신기하게도 

instance.Equals(null)를 쓰면 에러가 뜨지만

Equals(instance,null)을 쓰면 에러가 뜨지 않았습니다.

 

이유는 일단 instance는 null인 상태입니다.

그리고 그 null인 instance를 참조할려고하니 NullReferenceException 오류가 뜨는건 당연했죠.

 

하지만 instance.Equals(null)은 Null인 오브젝트의 값을 확인한다음,

매개변수의 값을 비교해야하기때문에 NULL값을 참조할려고해서 바로 에러가 뜨는거였고

Equals(instance,null)같은 경우는 마이크로소프트 .NET7 설명서에 따르면

매개변수 두개 다 NULL일경우 TRUE를 반환하여 오류가 발생하지 않는것이었습니다.

 

그렇다면 매개변수를 하나만 쓰는 Equals보다 매개변수 두개를 쓰는 Equals는 

NULL값 참조라는 검증이 빠지기때문에 좀 더 빠르지 않을까? 란 생각이 들었습니다.

 

바로 검증해보죠

 void Start()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        Test test1= TestClass1;
        Test test2= TestClass1;

        for (int i=0;i<100000000;i++)
        {
            if (Equals(test1, test2));
        }
        sw.Stop();

        UnityEngine.Debug.Log("Equals(object,object) : " + sw.Elapsed);
    }
void Start()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        Test test1= TestClass1;
        Test test2= TestClass1;

        for (int i=0;i<100000000;i++)
        {
            if (test1.Equals(test2));
        }
        sw.Stop();

        UnityEngine.Debug.Log("Object.Equals(object) : " + sw.Elapsed);
    }

코드는 매개변수가 두개인 Equals함수와 매개변수가 하나인 Equals함수가

동작하는 시간을 재는 코드입니다.

위 : 매개변수2개  Equals, 아래:매개변수1개 Equals

예상과 동일하게 객체의 값을 참조하고 난 다음에 값을 비교하는것보다

두개의 객체를 한번에 올려놓고 비교하는것이 훨씬 더 빠릅니다.

 

물론 횟수자체를 많이하여 극단적이긴하지만

같은 기능을 수행한다면 Equals(object,object)가 훨씬 빠르단걸 알게되었습니다.

하지만,  Null값을 참조한다는 경고를 하지 않는단건 분명 수행도중에 null이 되었을 경우

나도 모르게 그냥 넘어갈 수 있다는 점입니다. 이 점만 유의한다면 속도향상에 좋을것같습니다.

.

.

.

.

실제 Equals 코드가 어떻게 구현되어있고 다른지를 보지 못해서 아쉽지만,

== 대신 Equals(object,object)를 사용하여 null체크를 할 수 있고,

Equals(object,object)가 Object.Equals(object)보다 속도가 빠르단걸 알 수 있게되었습니다.

 

그리고 마지막에 ==을 이용해 똑같이 검증을 해본 결과, Equals(object,object)보다 속도가 느린것으로 나왔습니다.

최종적으로 Equals(object,object)> object==object > object.Equals(object)순으로

속도가 빠르네요.

 

이와 관련해서는 또 자료를 찾아본 뒤 글을 적도록하겠습니다.

 

Post

[ STUDY/유니티 ] 2022. 9. 10. 18:00

메지카복셀프로그램에서 오른쪽 하단에 Export를 눌러줍니다

 

 

그런다음에 obj를 눌러줍니다

 

obj파일을 저장한 경로로 가면 이렇게 3개의 파일들이 존재합니다.

 

이 친구들(3파일 모두)을 유니티 프로젝트 폴더안, Assets밑에 원하는경로에다가 넣어줍니다. 

 

씬에다가 넣어줍니다. 그럼 캐릭터가 나옵니다!

 

저는 position에서 y=-0.3 정도를 하니까 바닥에 착 달라붙더라구여 참고참고~.~

 

▲ top