Ssssong += Dev

[C#/유니티] (최적화) Foreach 루프 본문

카테고리 없음

[C#/유니티] (최적화) Foreach 루프

ssong_dev 2022. 11. 14. 18:22

유니티 5.5버전 이전까지 Foreach문을 사용하면 가비지가 생기는 현상이 있었다.

하지만 이후 버전에서는 개선되었는데, 이 과정이 어땠는지 적힌 좋은 글이 있다.

 

 

https://m.cafe.naver.com/ca-fe/web/cafes/indiedev/articles/13188?useCafeId=false&or=m.search.naver.com&query=foreach%2B%EA%B5%AC%EC%A1%B0%2B%EB%AC%B8%EC%A0%9C&buid=09ddb1ed-e3f0-43b1-a235-5f194b615103&art=ZXh0ZXJuYWwtc2VydmljZS1uYXZlci1ldGMtZm9yLWNvbW1lbnQ.eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjYWZlVHlwZSI6IkNBRkVfSUQiLCJhcnRpY2xlSWQiOjEzMTg4LCJpc3N1ZWRBdCI6MTYxNDI5NzMzMTczMiwiY2FmZUlkIjoyODE4MzkzMX0.kB0HsxGiUsEG6bu-qN_PZm9Z8PFnvtfHLiIe7vk11Vg 

 

(중급) 유니티의 Foreach는 여전히 악의 근원일까요?

대한민국 모임의 시작, 네이버 카페

cafe.naver.com

 

 

List와 Dictionary와 같은 IEnumerable을 상속받는 컬렉션은  foreach문으로 탐색할 경우 C# 컴파일러에서

다음과 같이 동작한다.

 

using (List<int>.Enumerator enumerator = list.GetEnumerator())
{
	while(enumerator.MoveNext())
    {
    	int current = enumerator.get_Current();
    }
}

 

여기서 Enumerator는 IDisposable을 상속받는 struct 타입이다.

public struct Enumerator : IEnumerator<T>, IEnumerator, IDisposable
{
	public T Current { get; }

	public void Dispose();
	public bool MoveNext();
}

 

어? using...? IDisposable...? 그렇다면 using 종료 후 Dispose()가 실행된다는 것이다.

참조한 글에 따르면 저 코드는 이런 의미와 같다고 한다.

 

Enumerator enumerator = list.GetEnumerator();
try
{
	while(enumerator.MoveNext())
    {
    	int current = enumerator.Current;
    }
}
finally
{
	var disposable = (System.IDisposable)enumerator;
    disposable.Dispose();
}

이 때, var disposable = (System.IDisposable)enumerator; 때문에

struct, 즉 Value-Type인 enumerator가 박싱되는 것 때문에 생긴 가비지였다고 한다.

(인터페이스 타입으로 캐스팅 시 죄다 Reference-type으로 처리되는 것 때문)

 

 

 

그런데 업데이트된 컴파일러에서는 

constrained라는 instruction을 통해 (IL 언어라 잘 모르겠다...) enumerator의 포인터를 넘겨버리는 것으로

Value-Type이고 Reference-Type이고 자시고 간에 callvirt를 하는 데 문제없게 만들었다. <<여기 좀 모르겠지만...

따라서 박싱이 일어나지 않게 되었고

 

최신 컴파일러를 사용하는 유니티에서는 foreach 문 사용 시에 더이상 내부적으로 가비지를 만들지 않게 되었다.