2017-01-01 1 views
1

그래서 저는 C++에서 메모리 풀 클래스를 구현하는 데 시간을 썼습니다. 도중에 약간의 사소한 문제를 제외하고는, 상당히 잘 진행되었습니다. 그러나 처음에 메모리 풀을 사용하여 1000 개의 청크를 할당 한 다음 새로운을 사용하여 비교 한 결과, 메모리 풀을 사용할 때 실제로는 거의 3 배나 성능이 떨어졌습니다 (나노초 단위). 내 할당 방법은 다음과 같습니다 : 나는 풀의 첫 번째 덩어리에서 시작하여 내가 무료 덩어리를 찾을 때까지 풀의 연결리스트를 통해 검색을 수행하거나, 풀의 끝에 도달하고메모리 풀에서 다음 사용 가능한 청크 찾기

template <class T> T* MemPool<T>::allocate() 
{ 
    Chunk<T>* tempChunk = _startChunk; 

    while (tempChunk->_free == false) 
    { 
     if (tempChunk->_nextChunk == NULL) 
      throw std::runtime_error("No available chunks"); 

     tempChunk = tempChunk->_nextChunk; 
    } 

    tempChunk->_free = false; 
    return &tempChunk->object; 
} 

. 이제는 풀이 클수록 검색에 O (n) 시간의 복잡도가있는만큼 더 오래 걸립니다. 여기서 n은 풀의 청크 수입니다.

할당을 향상시키는 방법에 대해 의견이있는 사람이 있는지 궁금합니다. 나의 초기 생각은 단지 하나 대신 두개의 링크 된리스트를 사용하는 것이었다. 여기에는 하나의 프리 덩어리와 다른 할당 된 덩어리가있다. 새로운 청크가 할당 될 때, 먼저 언급 된 첫 번째 링크 된 목록의 첫 번째 요소를 가져 와서 할당 된 연결된 목록으로 옮깁니다. 내가 볼 수있는 한, 할당 할 때 검색을 수행 할 필요가 없으며 올바른 청크를 찾기 위해 검색을 요구하는 할당 해제 만 남겨 둡니다.

이 방법으로 메모리로 직접 작업하는 것은 처음이기 때문에 어떤 생각이라도 환영합니다. 감사!

+0

청크가 모두 같은 크기입니까? –

답변

0

직접 만든 연결 목록을 사용하는 대신 std::list을 사용하는 것이 더 효과적 일 수 있습니다 (특히 사용자 지정 할당기와 함께 사용하는 경우). 오류가 발생하기 쉬우 며 최적화가 더 잘됩니다.

두 개의 목록을 사용하면 많은 것을 단순화 할 수 있습니다. 청크가 자유롭거나없는 경우 목록 자체에서 추적 할 필요가 없습니다. 청크가있는 목록으로 지정되기 때문에 필요하지 않습니다. 청크가 어떻게 든 두 목록에 나타나지 않도록하는 것이 모두 필요합니다.

귀하의 현재 구현은 할당 및 할당 해제시 링크 된 목록을 걸어야한다는 것을 의미합니다.

청크가 고정 크기 인 경우 할당은 사용 가능한 첫 번째 청크를 할당 된 목록으로 이동하여 간단히 구현됩니다. 검색 할 필요가 없습니다. 청크를 할당 해제하려면 할당 된 목록에서 해당 번호를 찾아야합니다. 즉, T*을 목록의 항목에 매핑해야합니다 (예 : 검색 수행). 할당 취소 작업을 수행하면 하나의 목록에서 다른 목록으로의 진입.

청크가 가변 크기 인 경우 조금 더 작업해야합니다. 할당 할 때 할당 할 때 적어도 요청 된 크기 인 청크를 찾아야합니다. 할당 (할당량을 늘리는 것보다 큰 청크를 할당하는 것)은 성능 측면에서 할당 및 할당 해제를보다 효율적으로 수행 할 수 있지만 풀에서 할당 할 수있는 청크가 더 적음을 의미합니다. 양자 택일로, 큰 덩어리 (자유 명부에서) 2에서 끊고, 할당 된 부분을 대표하는 두 명부 및 할당되지 않는 남겨 두는 부분에 1 개의 입장을 두십시오. 이렇게하면 할당을 해제 할 때 메모리에 인접한 청크를 병합하는 것이 바람직합니다 (효과적으로 풀의 여유 메모리 조각 모음을 구현합니다).

풀을 여러 스레드에서 사용할 수 있는지 결정하고 적절한 동기화를 사용해야합니다.

+0

언급 할 또 다른 요점은 조각 모음을 할 때 할당 된 메모리 포인터가 유효하지 않으므로 포인터를 재배치해야한다는 것입니다. 해결책은 스마트 포인터 또는 핸들을 사용하는 것일 수도 있습니다. –

+0

풀에서 해제 된 (사용되지 않은) 공간의 조각 모음만을 언급했습니다. 당신이 말한 것은 할당 된 메모리를 조각 모음하는 것이 사실 일 것입니다. – Peter

+0

위대한,이 말이되었다. 감사! – MulattoKid

0

고정 된 수의 크기 저장소를 사용하고 각 저장소를 링크 된 목록으로 만듭니다.

예를 들어, 빈은 단순히 시스템 페이지 크기 (일반적으로 4KiB)의 정수 배이고 1MiB 청크를 사용한다고 가정 해 보겠습니다. 1MiB/4KiB = 256 개의 저장소가 있습니다. 빈 공간이 청크에서 n 페이지 영역을 사용할 수 있도록 만들려면 빈 n에 추가하십시오. n 페이지 영역을 할당 할 때 n에서 256까지의 저장소를 살펴보고 사용 가능한 첫 번째 청크를 선택하십시오.

성능을 최대화하려면 비트를 비트 맵과 연결 한 다음 비트 n-1에서 비트 255까지 스캔하여 첫 번째 세트 비트를 찾습니다 (__builtin_clz 및 _BitScanForward와 같은 컴파일러 내장 함수를 사용하여 선행 또는 후행 0 수 계산). 빈의 수 때문에 여전히 O (1)은 아니지만 꽤 가깝습니다.

메모리 오버 헤드가 염려되는 경우 각 덩어리에 대해 번만 추가하면 각각 개가됩니다. 즉, 청크에 사용할 수있는 1 페이지 영역이 128 개 (최대 조각화) 인 경우에도 빈 1은 여전히 ​​청크에 한 번만 연결되어 128 번 다시 사용됩니다.

이렇게하려면 각 청크 안에이 영역을 연결해야합니다. 즉, 각 청크에도 크기 저장 빈의 목록을 저장해야합니다. 그러나 최대 256 개의 유효한 데이터 만 있기 때문에 더 많은 메모리를 사용할 수 있습니다 목록은 전체 포인터를 저장할 필요가 있지만 각 청크 내부에는 오프셋이 있습니다.

각 청크 내부의 여유 공간을 조각화하지 않으려면 목록의 빈에서 청크를 신속하게 제거해야합니다. 이중 링크 목록 사용을 의미합니다. 분명히 추가 메모리 오버 헤드가 추가되지만 전체 목록에서주기적인 여유 공간 조각 모음을 수행하는 것이 더 나을 수도 있습니다.

관련 문제