2014-02-14 4 views
1

은 내가 forward_list으로 목록을 변경 std::forward_listchar가있는 forward_list를 사용하는 것이 왜 오래 오래 사용하는 것보다 훨씬 최적화되어 있습니까?

#include <iostream> 
#include <list> 
#include <forward_list> 
#include <windows.h> 

int main() 
{ 
    LARGE_INTEGER start_; 
    LARGE_INTEGER end_; 
    LARGE_INTEGER freq; 

    QueryPerformanceFrequency(&freq); 
    std::list<long long> list; 

    QueryPerformanceCounter(&start_); 
    for(long long i=0;i<50000000;i++) 
     list.push_front(i); 
    QueryPerformanceCounter(&end_); 


    std::cout<< (end_.QuadPart - start_.QuadPart)/(freq.QuadPart/1000) <<"\n"; 
    cin.get(); 
} 

std::list을 비교하는 코드 아래 사용. 모두 결과 시간과 크기를 측정 :

//long long 
//    size time 
//forward list 1157.3 2360 
//list   1157.3 2450 

그리고이 코드에 대해 동일한 작업을 수행합니다

int main() 
{ 
    LARGE_INTEGER start_; 
    LARGE_INTEGER end_; 
    LARGE_INTEGER freq; 

    QueryPerformanceFrequency(&freq); 
    std::list<char> list; 

    QueryPerformanceCounter(&start_); 
    for(long long i=0;i<50000000;i++) 
     list.push_front('a'); 
    QueryPerformanceCounter(&end_); 


    std::cout<< (end_.QuadPart - start_.QuadPart)/(freq.QuadPart/1000)<<"\n"; 
    std::cin.get(); 
} 

결과 :

//char 
//    size time 
//forward list 773 2185 
//list   1157 2400 

문제는 왜 std::forward_list 파크를 사용하고 있습니다 char는 std::list과 비교해 오랫동안 사용하는 것보다 훨씬 낫습니다.

속도는 거의 같지만 용기의 크기는 얼마입니까?

//long long 
//    size(mb) time 
//forward list 1157.3  2360 
//list   1157.3  2450 


//char 
//    size(mb) time 
//forward list 773  2185 
//list   1157  2400 
+3

무료 차트 포르노 : https://docs.google.com/spreadsheets/d/1F8lmLjFtIrDJb_FUauP1L_7m6bsiLFJSv2hyLV3tI3o/edit?usp=sharing (내 컴퓨터에서 수집 한 결과). 당신은 아직도 이것들 사이에 중요한 차이가 있다고 생각합니까? –

답변

3

forward_list은 일반적으로 단순한 단일 연결 목록으로 구현됩니다. 목록 노드에는 목록에서 다음 노드를 지정하는 단일 포인터와 실제 사용자 데이터를 보유하는 데이터 필드가 있습니다. forward_list<T> 단일 노드의 크기는 가정 sizeof(void*) + sizeof(T) 것 (1) 모든 데이터 포인터의 사이즈가 동일하고, (2) Tvoid*보다 엄격한 정렬하지 않는다.

list은 이중 연결 목록으로 구현됩니다. 따라서 단일 노드는 데이터 필드와 다음 및 이전 목록 노드 모두에 대한 포인터를 갖습니다. 위와 동일한 가정 하에서 list<T> 노드의 크기는 따라서 2 * sizeof(void*) + sizeof(T)입니다. 메모리 할당 자들이 크기가 가장 기본적인 정렬의 배수 블록 메모리를 할당하는

것이 일반적이다. 할당의 입도와 추적 오버 헤드의 양 사이에는 절충점이 있습니다. 기본 정렬의 배수로 블록을 할당하는 것은 좋은 균형점이며 과도하게 정렬되지 않은 모든 유형에 대해 할당이 항상 올바르게 정렬되도록하는 보너스가 추가됩니다.

프로그램이 32 비트 구현 - sizeof(void*) == 4 -에서 실행되고 있고 시스템 할당자가 8 바이트의 블록 크기 인 sizeof(double)을 사용한다고 가정 해 봅시다. 이러한 가정은 MS Visual C++에서 구현 된 일반적인 32 비트 Windows C++에 해당합니다. 이러한 가정으로, 다양한 객체의 크기가있다 :

  • forward_list<char> 노드 : sizeof(void*) + sizeof(char) == 5, 메모리 할당이 8 바이트 할당 올림 것이다. 메모리 할당은 16 바이트 할당 올림 것이다 sizeof(void*) + sizeof(long long) == 12 : 노드
  • forward_list<long long>.
  • list<char> node : 2 * sizeof(void*) + sizeof(char) == 9이 메모리 할당자는 16 바이트 할당으로 반올림됩니다.
  • list<long long> 노드 : 2 * sizeof(void*) + sizeof(long long) == 16, 메모리 할당 부 (16) 이후 둥근 이미 제

영업의 종류 정도의 배수되지 않으며 forward_list<char>는 요소 list<char> 모든 당 8 바이트를 할당 list<long long>forward_list<long long>은 요소 당 16 바이트를 할당합니다. 이것은 메모리 사용 관측에 대한 하나의 가능한 설명입니다.

6

첫 번째로 주목해야 할 점은이 방법이 "훨씬 뛰어나다"는 것입니다. 차이는 전적으로 중요하지 않습니다. 그 너머

, 나는 그것이 8 바이트를 복사하는 것은 하나를 복사하는 것보다 약간 더 걸릴 것입니다 것을 매우 분명하다 생각; 게다가 레지스터/메모리에서 읽은 값이 귀하의 상수 인 'a'에 필요하지 않으며 그 값은 실행 파일에 하드 코딩 될 수 있습니다.

+0

'a'대신 i % 255를 시도했습니다. 그것은 같은 결과를 가지고 있습니다 ... – Omid

+0

@omid : 이것이 실질적이라고 생각하는 이유를 잘 모릅니다. –