메시 및 이미지 처리, 물리 엔진 및 레이트 레이싱과 같은 성능이 중요한 분야에서 작동하는 연결 목록에 대해 내가 찾은 가장 유용한 사례 중 하나는 연결 목록을 사용하여 실제로 참조 위치를 개선하고 힙 할당을 줄이며 때로는 메모리 사용을 간단한 대안.
이제는 링크드리스트가 그 반대를 자주하는 것으로 악명이 높기 때문에 모든 것을 할 수 있다는 완전한 모순처럼 보일 수 있지만, 각리스트 노드는 우리가 허용 할 수있는 고정 된 크기와 정렬 요구 사항을 가지고 있다는 점에서 고유 한 속성을 가지고 있습니다. 그것들은 연속적으로 저장되고 가변 크기의 것은 불가능한 방식으로 일정한 시간에 제거됩니다.
결과적으로 백만 개의 중첩 된 가변 길이 하위 시퀀스를 포함하는 가변 길이 시퀀스를 저장하는 것과 유사한 것을 수행하려는 경우를 살펴 보겠습니다. 구체적인 예는 100 만 개의 다각형 (일부 삼각형, 일부 사각형, 일부 오각형, 일부 육각형 등)을 저장하는 색인화 된 메시이며, 때로는 메시의 어느 곳에서나 다각형이 제거되고 때로는 기존 다각형에 정점을 삽입하기 위해 다각형이 다시 작성되거나 하나를 제거하십시오. 이 경우 백만 개의 tiny를 저장하면 std::vectors
모든 단일 벡터에 대한 힙 할당과 잠재적으로 폭발적인 메모리 사용에 직면하게됩니다. 백만 명의 작은 사람 SmallVectors
은 일반적인 경우만큼이 문제를 겪지 않을 수 있지만 별도로 힙 할당되지 않은 사전 할당 된 버퍼는 여전히 폭발적인 메모리 사용을 유발할 수 있습니다.
여기서 문제는 백만 개의 std::vector
인스턴스가 백만 개의 가변 길이 사물을 저장하려고한다는 것입니다. 가변 길이 사물은 힙의 다른 곳에 콘텐츠를 저장하지 않으면 연속적으로 저장되고 일정 시간 (적어도 매우 복잡한 할당자가없는 간단한 방식으로)에서 제거 될 수 없기 때문에 힙 할당을 원하는 경향이 있습니다.
대신 다음과 같이하면됩니다.
struct FaceVertex
{
// Points to next vertex in polygon or -1
// if we're at the end of the polygon.
int next;
...
};
struct Polygon
{
// Points to first vertex in polygon.
int first_vertex;
...
};
struct Mesh
{
// Stores all the face vertices for all polygons.
std::vector<FaceVertex> fvs;
// Stores all the polygons.
std::vector<Polygon> polys;
};
... 그런 다음 힙 할당 및 캐시 누락 수를 크게 줄였습니다. 액세스하는 모든 단일 다각형에 대해 힙 할당 및 잠재적으로 강제적 인 캐시 미스를 요구하는 대신, 이제 전체 메시에 저장된 두 벡터 중 하나가 용량을 초과 할 때만 힙 할당이 필요합니다 (상각 된 비용). 한 정점에서 다음 정점으로 이동하는 걸음으로 인해 캐시 미스가 발생할 수 있지만 노드가 연속적으로 저장되고 인접한 정점이있을 가능성이 있기 때문에 모든 단일 다각형이 별도의 동적 배열을 저장하는 것보다 적은 경우가 많습니다. 제거하기 전에 액세스 할 수 있습니다 (특히 많은 다각형이 한 번에 모두 정점을 추가하므로 다각형 정점의 사자가 완벽하게 연속되도록합니다).
다음은 또 다른 예입니다.
... 그리드 셀을 사용하여 매 프레임마다 이동하는 1,600 만 개의 입자에 대해 입자-입자 충돌을 가속화합니다. 이 입자 그리드 예제에서 연결 목록을 사용하면 3 개의 인덱스 만 변경하여 한 그리드 셀에서 다른 그리드 셀로 입자를 이동할 수 있습니다. 벡터에서 지우고 다른 벡터로 푸시하는 것은 훨씬 더 비싸고 더 많은 힙 할당을 도입 할 수 있습니다. 연결 목록은 또한 셀의 메모리를 32 비트로 줄입니다. 구현에 따라 벡터는 빈 벡터에 대해 32 바이트를 취할 수있는 지점에 동적 배열을 미리 할당 할 수 있습니다. 약 백만 개의 그리드 셀이 있다면 그것은 상당한 차이입니다.
... 그리고 이것은 내가 요즘 가장 유용한 연결 목록을 찾는 곳이며, 특히 32 비트 인덱스가 64 비트 컴퓨터에서 링크의 메모리 요구 사항을 절반으로 줄이기 때문에 "인덱싱 된 연결 목록"다양성이 유용하다는 것을 알게되었습니다. 노드는 배열에 연속적으로 저장됩니다.
종종 나는 그것들을 색인 된 자유 목록과 결합하여 언제 어디서나 일정한 시간 제거 및 삽입을 허용합니다.
이 경우 next
인덱스는 노드가 제거 된 경우 다음 사용 가능한 인덱스를 가리키고 노드가 제거되지 않은 경우 다음 사용 된 인덱스를 가리 킵니다.
그리고 이것이 제가 요즘 연결 목록에 대해 찾은 최고의 사용 사례입니다. 예를 들어 각각 4 개의 요소를 평균하는 백만 개의 가변 길이 하위 시퀀스를 저장하려고 할 때 (하지만 때로는 요소가 제거되고 이러한 하위 시퀀스 중 하나에 추가되는 경우도 있음) 연결 목록을 사용하면 4 백만 개를 저장할 수 있습니다. 각각 개별적으로 힙 할당되는 1 백만 개의 컨테이너 대신 연결된 목록 노드 : 하나의 거대 벡터, 즉 백만 개의 작은 벡터가 아닙니다.