2016-11-04 3 views
2

모두 : 두 개의 코드가 있습니다. 첫 번째는 다음과 같습니다왜이 C++ 코드가 더 빠르지 않습니까?

#include <iostream> 

using namespace std; 

static constexpr long long n = 1000000000; 

int main() { 
    int sum = 0; 
    int* a = new int[n]; 
    int* b = new int[n]; 

    for (long long i=0; i<n; i++) { 
    a[i] = static_cast<int>(i); 
    } 

    for (long long i=0; i<n; i++) { 
    sum *= a[i]; 
    sum += a[i]; 
    } 

    for (long long i=0; i<n; i++) { 
    b[i] = static_cast<int>(i); 
    } 

    for (long long i=0; i<n; i++) { 
    sum *= b[i]; 
    sum += b[i]; 
    } 

    cout<<sum<<endl; 
} 

두 번째는 다음과 같습니다

#include <iostream> 

using namespace std; 

constexpr long long n = 1000000000; 

int main() { 
    int* a = new int[n]; 
    int* b = new int[n]; 
    int sum = 0; 

    for (long long i=0; i<n; i++) { 
    a[i] = static_cast<int>(i); 
    b[i] = static_cast<int>(i); 
    } 

    for (long long i=0; i<n; i++) { 
    sum *= a[i]; 
    sum += a[i]; 
    sum *= b[i]; 
    sum += b[i]; 
    } 

    cout<<sum<<endl; 
} 

내가 첫 번째 프로그램이 두 번째보다 훨씬 빨리해야한다고 생각, 더 캐시가 친절하기 때문이다. 그러나 진실은 두 번째가 쓰레기가 더 빠르다는 것입니다. 내 서버에서, 첫 번째 서버는 23 초가 걸리고, 두 번째 서버는 20 초가 걸립니다.

+5

그래도 1000000000 루프를 두 번 반복하지 않고 두 번 실행하는 것이 빠릅니다. 이유가 궁금합니다. 내가 틀렸다면 삽으로 치지 만, 나는 이것이 자명하다 고 생각한다. – Steeve

+3

생성하는 정수 오버 플로우의 방대한 양 때문에, 프로그램은 어쨌든 완전히 정의되지 않은 동작을합니다. –

+5

정보가 충분하지 않습니다. 어떤 컴파일러 플래그를 사용하고 있습니까? 모든 정적 캐스팅은 무엇입니까? 그럼에도 불구하고, 이것은 현재 가장 높은 투표 된 C++ 질문의 클론 일 수 있습니다 : http://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted -array –

답변

3

느린 것으로 예상되는 버전에서도 액세스 패턴이 여전히 너무 단순하기 때문에 캐시가 유리한 장점이 없습니다.

두 개 (또는 그 이상)의 직선 입력 스트림은 최신 CPU가 감지하고 필요에 앞서 L1로 스트리밍 할 수있는 기능입니다.

또한 여러 SDRAM 뱅크를 동시에 유용하게 사용할 수 있습니다. 리눅스를 사용한다면 페이지가 무작위로 매핑되기 때문에 그다지 제어 할 수는 없지만 mmap()을 사용하여 메모리를 할당하고 MAP_HUGETLB 인수를 사용하여 메모리를 할당 해보고 다른 오프셋을 시도해 볼 수 있습니다. 할당의 시작.

캐시 친숙한 순서로 계산을 정렬하는 이점을 보려면 2 차원 배열에서 다른 액세스 패턴을 실험해야합니다.

+0

네, 맞습니다. 나는 2 차원 어레이 방식을 시도했고 더 빨랐어 요. –

-1

첫 번째 코드는 루프를 사용하여 작업을 완료합니다.

for (long long i=0; i<n; i++) { 
    a[i] = static_cast<int>(i); 
    } 

    for (long long i=0; i<n; i++) { 
    sum *= a[i]; 
    sum += a[i]; 
    } 

    for (long long i=0; i<n; i++) { 
    b[i] = static_cast<int>(i); 
    } 

    for (long long i=0; i<n; i++) { 
    sum *= b[i]; 
    sum += b[i]; 
    } 

두 번째에는 두 개의 루프 만 사용하여 작업을 완료합니다.

for (long long i=0; i<n; i++) { 
    a[i] = static_cast<int>(i); 
    b[i] = static_cast<int>(i); 
    } 

    for (long long i=0; i<n; i++) { 
    sum *= a[i]; 
    sum += a[i]; 
    sum *= b[i]; 
    sum += b[i]; 
    } 

제공된 반복 코드는 두 번째 코드에서 훨씬 적습니다.

+0

그렇다면 주어진 테스트 결과를 제외하면 캐시의 친숙성보다 반복 횟수가 항상 중요하다고 생각하는 이유는 무엇입니까? 이것은 질문의 기본 아이디어였습니다. – stefaanv

+0

잘 OP는 첫 번째 캐시가 더 빠를 것이라고 생각했는데 그 이유는 캐시가 더 캐시가되어 캐시 미스가 적어 루프가 빨라지고 반복 작업이 줄어들었기 때문입니다. – Hayt

+0

더 많은 루프가 있더라도 연산의 수는 거의 동일하며 유일한 차이는 'i'의 증가와 'n'과의 비교 수입니다. 나는 컴파일러가 모든 종류의 최적화를 수행 할 수있는 컴파일 타임에 알려진 'n'이기 때문에 퍼포먼스의 차이를 정당화 할만큼 충분하지 않다고 생각한다. – alessandrolenzi

2

캐시가 사용자의 예에서 큰 역할을하지 않습니다. 캐쉬보다 큰 배열 렁에 대한 선형 액세스는 캐쉬가 아닌 메모리 대역폭에 의해 항상 제한 될 것입니다. 그들은 단순히 프리 페치로 채울 충분한 시간이 없습니다.

당신이하고있는 것을 단서로 얻고 간단히 결과를 출력하기 위해 4/2 루프를 하나 또는 그 똑똑한 것으로 최적화하는 컴파일러의 영리함을 시험해보십시오.

관련 문제