'쿼드트리'에 해당되는 글 1건
- 2010.06.25 단기간에 많은 처리를 하는 선행조건을 가지는 함수의 분리
단기간에 많은 처리를 하는 선행조건을 가지는 함수의 분리
2010. 6. 25. 14:03 in C/C++/패턴 및 리팩토링

선행학습으로 인라인함수(Efficient C++)과 80-20의 법칙(Efficient C++, More Effective C++, 기타)을 읽어보시면 이해가 빠르십니다.
어떤 함수가 처리되는데 특정한 조건이 필요한 경우를 보자. 그런데 그 함수가 단시간에 자주 호출된다면, 명령어 하나라도 최적화할 필요가 있다.
파란색 - CQuadTreeNode의 소스
연두색 - CQuadTree의 소스
예를 들어 쿼트트리내 오브젝트들을 가져오는 경우 해당 노드에 다음과 같은 함수를 호출할 수 있다.
VOID CQuadTreeNode::CheckInFrustumAndNotifyToNode( CFrustum* pFrustum );
위 함수는 노드내 오브젝트와 절두체의 충돌판정 후 절두체와 충돌한다면 해당 오브젝트의 렌더링 콜백함수를 호출하는 함수로써 다음과 같은 형식을 가지고 있다. 하지만 선행조건이 걸려있다. m_iObjectCount <= 0, 오브젝트의 개수가 0보다 같거나 작다면 더이상 진행하지 않고 함수를 끝낸다.
CQuadTreeNode
VOID CQuadTreeNode::CheckInFrustumAndNotifyToNode( CFrustum* pFrustum )
{
if( m_iObjectCount <= 0 )
{
return;
}
for( SCENENODELIST::iterator it = m_sceneNodeList.begin() ;
it != m_sceneNodeList.end() ;
it++ )
{
if( pFrustum->IsAABBInFrustum(*(*it)->GetWorldBoundAABB()) )
(*it)->AddedRenderEntryArrayCallBack();
}
}
{
if( m_iObjectCount <= 0 )
{
return;
}
for( SCENENODELIST::iterator it = m_sceneNodeList.begin() ;
it != m_sceneNodeList.end() ;
it++ )
{
if( pFrustum->IsAABBInFrustum(*(*it)->GetWorldBoundAABB()) )
(*it)->AddedRenderEntryArrayCallBack();
}
}
이 함수는 쿼드트리클래스의 CQuadTree::CheckInFrustumAndNotifyToNode함수에서 다음과 같은 형식으로 호출될 수 있다.
CQuadTree
VOID CQuadTree::CheckInFrustumAndNotifyToNode( CFrustum* pFrustum )
{
// 쿼드트리의 영역 계산
...
for 쿼드트리의 영역 순회
{
CQuadTreeNode* node = GetNode( .. );
node->CheckInFrustumAndNotifyToNode( pFrustum );
}
}
{
// 쿼드트리의 영역 계산
...
for 쿼드트리의 영역 순회
{
CQuadTreeNode* node = GetNode( .. );
node->CheckInFrustumAndNotifyToNode( pFrustum );
}
}
렌더링 쿼드트리의 특성상 아주 작은 시간에도 매우 자주 호출됨에 따라 단 하나의 명령어도 간과해서는 안된다. 즉, CQuadTreeNode::CheckInFrustumAndNotifyToNode 함수의 함수호출 명령어조차도 최소화 시켜야하는데 이 얘기는 inline으로 선언해야한단 얘기다. 하지만 inline으로 하기에는 소스의 길이가 다소 길다.(싱글톤메소드라고 해서 예외사항은 있다.) 만약 위 소스는 절대 inline이 될 수 없다 가정하면,
오브젝트의 개수 부분만을 인라인화 해서 아예 이 함수가 호출되지 않게끔 방지할 순 있다. 다음을 보자. 수정된 부분은 굵은글씨로 표시해두었다.
CQuadTreeNode
inline BOOL CQuadTreeNode::IsExistNode()
{
return m_iObjectCount>0 ? TRUE:FALSE;
}
VOID CQuadTreeNode::CheckInFrustumAndNotifyToNode( CFrustum* pFrustum )
{
// if검사부분을 아예 없애버렸다.
// 이 경우는 다소 위험한 방법이긴한데 릴리즈시 약간의 오버헤드를 없앨 수 있다.
// 만약, CQuadTree::CheckInFrustumAndNotifyToNode함수에서만 호출된다면,
// 아래 코드는 99%안전한 코드다.
// 특정 위치의 한곳에서만 호출되는 것을 싱글톤 메소드라고 하는데 이 경우는 FORCEINLINE 매크로로
// 강제 인라인화 할 수 있다.(VS에서는 지나치게 긴 메소드를 인라인화에서 제외시킨다.)
assert( m_iObjectCount > 0 && "오브젝트가 하나도 없는데 호출됐네요." );
for( SCENENODELIST::iterator it = m_sceneNodeList.begin() ;
it != m_sceneNodeList.end() ;
it++ )
{
if( (*it)->IsInFrustum( pFrustum ) )
(*it)->AddedRenderEntryArrayCallBack();
}
{
return m_iObjectCount>0 ? TRUE:FALSE;
}
VOID CQuadTreeNode::CheckInFrustumAndNotifyToNode( CFrustum* pFrustum )
{
// if검사부분을 아예 없애버렸다.
// 이 경우는 다소 위험한 방법이긴한데 릴리즈시 약간의 오버헤드를 없앨 수 있다.
// 만약, CQuadTree::CheckInFrustumAndNotifyToNode함수에서만 호출된다면,
// 아래 코드는 99%안전한 코드다.
// 특정 위치의 한곳에서만 호출되는 것을 싱글톤 메소드라고 하는데 이 경우는 FORCEINLINE 매크로로
// 강제 인라인화 할 수 있다.(VS에서는 지나치게 긴 메소드를 인라인화에서 제외시킨다.)
assert( m_iObjectCount > 0 && "오브젝트가 하나도 없는데 호출됐네요." );
for( SCENENODELIST::iterator it = m_sceneNodeList.begin() ;
it != m_sceneNodeList.end() ;
it++ )
{
if( (*it)->IsInFrustum( pFrustum ) )
(*it)->AddedRenderEntryArrayCallBack();
}
그리고 쿼드트리에서도 약간의 수정이 필요하다. 수정된 부분은 굵은글씨로 표시해두었다.
CQuadTree
VOID CQuadTree::CheckInFrustumAndNotifyToNode( CFrustum* pFrustum )
{
// 쿼드트리의 영역 계산
...
for 쿼드트리의 영역 순회
{
CQuadTreeNode* node = GetNode( .. );
if( node->IsExistNode() )
node->CheckInFrustumAndNotifyToNode( pFrustum );
}
}
{
// 쿼드트리의 영역 계산
...
for 쿼드트리의 영역 순회
{
CQuadTreeNode* node = GetNode( .. );
if( node->IsExistNode() )
node->CheckInFrustumAndNotifyToNode( pFrustum );
}
}
성능이 비약적으로 상승한다.
'C/C++ > 패턴 및 리팩토링' 카테고리의 다른 글
파일간 #include 설계에 대한 생각 (0) | 2010.07.07 |
---|