2012-01-18 3 views
1

메모리에 대한 액세스와 관련하여 매우 이상한 성능 문제가 있습니다. 코드 조각은 다음과 같습니다왜 메모리 액세스가 너무 느린가요?

#include <vector> 
using namespace std; 

vector<int> arrx(M,-1); 
vector< vector<int> > arr(N,arrx); 

... 
for(i=0;i<N;i++){ 
    for(j=0;j<M;j++){ 
    //>>>>>>>>>>>> Part 1 <<<<<<<<<<<<<< 
    // Simple arithmetic operations 
    int n1 = 1 + 2; // does not matter what (actually more complicated) 
    // Integer assignment, without access to array 
    int n2 = n1; 

    //>>>>>>>>>>>> Part 2 <<<<<<<<<<<<<< 
    // This turns out to be most expensive part 
    arr[i][j] = n1; 
    } 
} 

N과 M - 10000 정도 - 1000의 순서의 일부 상수이다. 이 코드 (릴리스 버전)를 컴파일 할 때 파트 2에 주석이 달린 경우 약 15 클럭 걸립니다. 이 부분에서 실행 시간은 100+ 클럭까지 올라서 거의 10 배 더 느려집니다. 나는 할당 연산이 단순한 산술 연산보다 훨씬 저렴할 것으로 기대했다. 배열을 사용하지 않는다면 실제로 적용됩니다. 그러나이 배열을 사용하면 훨씬 많은 비용이들 것입니다. 2 차원 대신 1 차원 배열을 시도했습니다. 동일한 결과 (2D의 경우 분명히 느립니다). 벡터 < 벡터 < int>> 대신 int ** 또는 int *를 사용하거나 벡터 < int>를 사용합니다. 다시 결과가 동일합니다.

배열 할당시 성능이 저하되는 이유는 무엇입니까?

편집 : 한 번 더 관찰 : 주어진 코드의 2 부에서 우리는 (의견의 숫자)

n1 = arr[i][j]; // 16 clocks 

속도에

arr[i][j] = n1; // 172 clocks 

에서 할당을 변경하면 올라갑니다. 우리가 라인을 변경하는 경우 더 흥미롭게 :

arr[i][j] = n1; // 172 clocks 

arr[i][j] = arr[i][j] * arr[i][j]; // 110 clocks 

에가 속도가 읽기에 어떤 차이가 있나요 및 /에서 메모리에 쓰는 간단한 과제 보다도 더 높다? 왜 이상한 성능을 얻습니까?

미리 감사드립니다.

+0

그건 단지 과제 이상입니다 ... – AJG85

+1

중첩 된'vector' 대신에 flat 배열을 사용하면 더 나은 결과를 얻을 수 있습니다. 'boost :: multiarray'를 시도하십시오. – pmr

+0

이고 셀을 메모리 순서대로 처리하려는 경우가 있습니다 (캐시를 더 잘 사용하는 경우). – ysdx

답변

5

실제 "파트 1"이 예를 들어 생각한 것보다 훨씬 복잡하지 않으면 여기에 놀랄 것도 없습니다 - 메모리 액세스 은 기본 산술에 비해 느림입니다. 또한 최적화로 컴파일하는 경우 결과가 전혀 사용되지 않기 때문에 "파트 1"의 대부분 또는 전부가 최적화되지 않을 수 있습니다. 메인 메모리에 쓰기

귀하의 가정이 정말 잘못
+0

파트 1은 복잡하지 않지만 루프 자체는 스 니펫에서 보여준 것보다 더 복잡합니다. 실제로는 여러 부동 소수점 연산이 포함되므로 기대치가 상당히 높아야합니다. 그러나 메모리 액세스가 훨씬 복잡하다는 것이 밝혀졌습니다. – user938720

+1

@ user938720 : 왜 부동 소수점이 비싸다고 생각합니까? –

6

...

  1. 간단한 추가하는 것보다 훨씬 느립니다.
  2. 쓰기를하지 않으면 일 가능성이 높습니다. 루프가 완전히 최적화됩니다.
2

당신은 현대 컴퓨터에 대한 슬픈 진실을 발견했다 : 메모리 액세스는 연산에 비해 정말 느린 입니다. 그것에 대해 할 수있는 일이 없습니다. 이것은 구리선의 전계가 빛의 속도의 약 2/3에서만 움직이기 때문입니다.

+1

히트는 * CPU * 클럭이 더 빨라지는 것을 막아 주지만 빛의 속도가 * 메모리 버스 * 클럭이 더 빨라지는 이유입니다 (CPU 클럭이 끝나기 전의 시간). – zwol

+0

나는 수학을했는데, 50ns 조회를 위해, 그것은 약 3.75 미터의 전선이다. 나는 우리가 빛의 속도에 의해 제한된다는 것을 믿을 수 있다고 생각한다. 나는 우리가 그렇게 가까이에 있다는 것을 깨닫지 못했습니다. 그건 미친 짓이야_. –

2

중첩 된 배열은 50-500MB 메모리 영역에 있습니다. 많은 메모리를 쓰는 데는 시간이 걸리고, 영리한 하드웨어 메모리 캐싱도 그만큼 도움이되지 않습니다.더욱이, 단일 메모리 기록이라도 회로 기판의 일부 구리선을 통해 먼 거리의 실리콘 덩어리로 나아가 야하므로 시간이 걸립니다. 물리학이 이긴다.

하지만 더 자세히 살펴보고 싶다면 cachegrind 도구를 사용해보십시오 (플랫폼에 있다고 가정). 위에서 사용하고있는 코드가 실제로 많은 최적화를 허용하지 않는다는 사실을 명심하십시오. 데이터 액세스 패턴은 엄청난 양의 재사용 가능성을 갖지 않습니다.

2

간단한 견적을 내 보자. 요즘 일반적인 CPU 클럭 속도는 약 1 ~ 2GHz입니다 (기가 10 = 9 기가비트). (실제로) 많은 것을 단순화한다는 것은 단일 프로세서 작동이 약 1ns (나노 = 10은 네거티브 9에 전원을 공급함)가 걸린다는 것을 의미합니다. int 덧셈과 같은 간단한 산술은 수십 개의 CPU 사이클을 필요로합니다.

메모리 : 일반적인 메모리 액세스 시간은 약 50ns입니다. (다시 말해, 이제는 세부 사항을 자세히 살펴볼 필요가 없습니다.

당신은, 심지어 가장 좋은 시나리오에서, 메모리는 약 5 (10)

에 사실, 나는 양탄자에서 세부의 엄청난 양을 휩쓸고있어 배 CPU보다 느린 것을 볼 그러나 나는 그 기본 생각이 분명하기를 바란다. 관심이 있다면 책 (키워드 : 캐시, 캐시 미스, 데이터 지역성 등)이 있습니다. This one은 날짜가 있지만, 일반적인 개념을 설명하는 데는 여전히 뛰어납니다.

관련 문제