STUDY 36개
Post
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://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/operators/is
'STUDY > 유니티' 카테고리의 다른 글
[C#] null을 비교하기위한 여정2, is 와 FakeNull (0) | 2023.01.21 |
---|---|
[C#] null을 비교하기위한 여정1, ==와 Equals() (0) | 2023.01.18 |
[Unity] 메지카복셀에서 내가 만든 캐릭터 유니티로 불러오기 (0) | 2022.09.10 |
[Unity] C# 문자열보간 (0) | 2022.08.02 |
[UniRx] UniRx에 대해서 알아보기 (0) | 2022.08.02 |
Post
이전에 null 비교를 위한 연산으로 ==와 Equals를 보던 중 같은 기능을 수행하는 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체크 자주 쓰이는것도 아니라면..굳이?
'STUDY > 유니티' 카테고리의 다른 글
[C#] null을 비교하기위한 여정3, ==과 is 그리고 Equals(,) 속도 (1) | 2023.01.22 |
---|---|
[C#] null을 비교하기위한 여정1, ==와 Equals() (0) | 2023.01.18 |
[Unity] 메지카복셀에서 내가 만든 캐릭터 유니티로 불러오기 (0) | 2022.09.10 |
[Unity] C# 문자열보간 (0) | 2022.08.02 |
[UniRx] UniRx에 대해서 알아보기 (0) | 2022.08.02 |
Post
if (instance == null)
{
instance = this;
}
else if (instance != this)
Destroy(gameObject);
싱글톤 코드를 작성하면서 들었던 의문이 있었습니다.
==대신에 Equals()를 써서 null인지를 체크할 수는 없을까?
Equals로 인한 비교는 총 두가지 방법이 있습니다.
매개변수를 1개 쓰는것과 2개쓰는것
신기하게도
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함수가
동작하는 시간을 재는 코드입니다.
예상과 동일하게 객체의 값을 참조하고 난 다음에 값을 비교하는것보다
두개의 객체를 한번에 올려놓고 비교하는것이 훨씬 더 빠릅니다.
물론 횟수자체를 많이하여 극단적이긴하지만
같은 기능을 수행한다면 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)순으로
속도가 빠르네요.
이와 관련해서는 또 자료를 찾아본 뒤 글을 적도록하겠습니다.
'STUDY > 유니티' 카테고리의 다른 글
[C#] null을 비교하기위한 여정3, ==과 is 그리고 Equals(,) 속도 (1) | 2023.01.22 |
---|---|
[C#] null을 비교하기위한 여정2, is 와 FakeNull (0) | 2023.01.21 |
[Unity] 메지카복셀에서 내가 만든 캐릭터 유니티로 불러오기 (0) | 2022.09.10 |
[Unity] C# 문자열보간 (0) | 2022.08.02 |
[UniRx] UniRx에 대해서 알아보기 (0) | 2022.08.02 |
Post
항목13에서 본 shared_ptr(auto_ptr은 이후에 삭제되었다고한다)은 힙이 아닌 다른 자원에는 맞지않다라는 견해가 일반적이다.
하지만 모든 자원이 힙에서 생기지는 않는다. 그래서 자원 관리 클래스를 우리가 스스로 만들어야할 필요성이 있다.
class Lock{
public:
explicit Lock(Mutex *pm)
:mutexPtr(pm)
{ lock(mutexPtr);}
~Lock() {unlock(mutexPtr);}
private:
Mutex *mutexPtr;
};
잠금을 관리하는 클래스를 하나 만들고 , RAII법칙을 따라한다고하자.
Lock m11(&m);
Lock m12(m11);
하지만 여기서 복사를 하게 된다면 어떻게 될까?
복사할때 이루어지는 동작과 관련해서 선택지를 고를 수 있다.
1. 복사를 금지한다.
복사하면 안되는 RAII클래스(위와 같은 Lock)의 경우에는 복사가 아예 안되도록 막아놓아야한다.
이 경우에는 복사함수를 private 멤버로 만들면된다.
2. 관리하고 있는 자원에 대해 참조 카운팅을 수행한다.
해당 자원을 참조화는 객체의 개수에 대한 카운트를 증가시키는 식으로 RAII 객체의 복사 동작을 만들어야한다.
shared_ptr이 이러한 방식을 사용하고있다.
하지만, shared_ptr의 경우에는 참조카운트가 0이되면 삭제가 되기때문에 Lock을 해제만 하고싶지 삭제를 하고싶지않은 사람에게는 다소 목적이 안맞을 수 있다.
class Lock {
public:
explict Lock(Mutex *pm)
:mutexPtr(pm, unlock) //삭제자로 umlock함수 사용
{
lock(mutexPtr.get());
}
private:
std::tr1::shared_ptr<Mutex> mutexPtr; //원시포인터 대신, shared_ptr사용
};
이런경우에는 shared_ptr에 '삭제자'(deleter)라는 것이 있다. 삭제자란 shared_ptr의 참조 카운트가 0이되면 호출되는 함수 혹은 함수객체를 일컫는 말이다. 이 삭제자를 shared_ptr 생성자의 두번째 매개변수에다가 선택하여 넣어줄 수가 있다.
이러면 소멸자를 선언할 필요 없이 비정적데이터 멤버(mutexPtr)의 소멸자가 자동으로 호출되는데, mutexPtr의 소멸자는 shared_ptr의 삭제자가 자동으로 호출되어진다!
3. 관리하고 있는 자원을 진짜로 복사한다.
자원관리 객체를 복사하면 그 객체의 자원까지 복사되어야하기때문에 깊은 복사를 수행해야한다.
4. 관리하고 있는 자원의 소유권을 옮긴다.
'STUDY > Effective C++' 카테고리의 다른 글
항목 13: 자원관리에는 객체가 그만! (2) | 2022.09.22 |
---|---|
항목 12: 객체의 모든 부분을 빠짐없이 복사하자 (0) | 2022.09.08 |
항목 11: operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 (0) | 2022.08.31 |
항목 10 : 대입 연산자는 *this의 참조자를 반환하게 하자 (0) | 2022.08.24 |
항목 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 (0) | 2022.08.17 |