게임엔진/유니티

Unity에서의 Null Comparison

tsyang 2021. 4. 11. 14:59

Comparison to 'null' is expensive


Rider 에디터에서 유니티 오브젝트의 Null 비교 '=='연산자를 통해 수행하려 하면 다음과 같은 안내가 나온다.

 

Comparison to 'null' is expensive

 

단순히 null을 비교하는데 왜 이런 안내가 뜰까??

 

이유는 UnityEngine.Object가 비교 연산자를 오버라이드 했기 때문이다. UnityEngine.Object의 비교 연산자는 lhs나 rhs중 둘 중 하나만 null일 때, 나머지 하나가 '살아있는' 오브젝트인지 검사한다. 이 과정은 나름 시간이 소요되기에 라이더에서 저런 안내를 해주는 것이다.

 

그러면 UnityEngine.Object가 살아있음을 보장할 수 있다면 null비교를 더 빠르게 수행할 수 있지 않을까? 여러 방법이 있겠지만 대충 세 가지 방법을 생각해 볼 수 있다.

 

  1. System.Object로 캐스팅하여 사용하는 방법.
  2. ReferenceEquals를 이용하는 방법.
  3. 패턴 매칭을 이용하는 방법.
GameObject obj = new GameObject();
GameManager gameMgr = obj.GetComponent<GameManager>();

if (gameMgr == null) {} //내부적으로 obj가 살아있는 객체인지 확인하므로 오래 걸린다.

//obj가 살아있는 UnityObject임이 확실하다면 다음과 같이 사용하여 성능 향상을 꾀할 수 있다.
if((object)gameMgr == null){}
if(System.Object.ReferenceEquals(gameMgr,null)){}
if(gameMgr is null){}

 


is null


 

1,2는 별도로 설명이 필요 없다. 3번의 경우는 어떨까? 'is null'과 '== null'은 어떤 차이가 있을까?

 

is null은 C#7.0부터 사용 가능한 패턴 매칭이라는 기능 중 하나이다. 

(docs.microsoft.com/ko-kr/dotnet/csharp/pattern-matching)

 

'is null'과 '== null'의 차이는 컴파일러가 만든 IL코드를 보면 알 수 있다.

 

== null의 경우 lhs, rhs를 로드하고 해당 타입의 op_Equality(==연산자 메소드)를 호출한다. 보통은 System.Object의 ==연산자 메소드가 호출되겠지만 타입이 해당 연산자를 오버라이드 한 경우 오버라이드한 메서드를 호출하기 때문에 추가적인 연산 작업이 필요하게 된다.

 

is null의 경우 lhs, rhs(null)를 로드한 뒤 바로 ceq로 두 값을 비교한다. 그렇기 때문에 ==null보다 빠를 수 밖에 없다.

 

참고로 널 병합 연산자(nillish coalescing operator, "obj ?? new Object();" 와 같은 코드) 역시 내부적으로 패턴 매칭과 같은 방법을 사용한다고 하니, == 연산자가 오버라이드 되어 있는 경우 주의가 필요하다.

결론


UnityEngine.Object는 ==, =! 연산자를 오버라이드 하여 엔진오브젝트가 살아있는지 확인하기 때문에 null비교가 꽤 무거운 작업이 된다. 따라서, 오브젝트가 살아있는 것이 확실하다면 object 캐스팅이나 ReferenceEquals와 같은 메서드를 활용하여 성능 향상을 꾀할 수 있다. 또한 패턴 매칭이 사용 가능한 환경(C#7.0)이라면 패턴 매칭을 활용하는 것이 좋은 선택지가 될 것이다.

 

 

참고 : www.gullberg.tk/blog/is-null-versus-null-in-c/