효율적으로 벡터화를 이루기 위해서는 데이터들의 흐름이 메모리부터 캐시, 벡터 레지스터까지 매끄럽게 이동 되어야 한다. 데이터 이동시 오버헤드가 발생하여 지연이 생기면 생길수록 벡터를 이용하는 비중이 낮아지게 된다. 효율적인 데이터 이동은 데이터 배치(layout), 정렬(alignment), 프리페치와 데이터 저장을 얼마나 효율적으로 처리하는가에 달려있다.
왜 데이터 배치가 효율적인 벡터 처리에 영향을 미치는가?
데이터 병렬화라는 것은 여러 데이터에 대해서 동일한 동작(처리)를 동시에 하는 것을 말한다. 몇가지 성능 최적화를 위해 이해하고 가야 할 문제들은 다음과 같다.
- 메모리에서의 데이터 배치, 정렬 및 팩(Packed). 단정도 부동소수점 덧셈을 생각해 보자. c = a + b. 16개의 값을 각 입력 레지스터에 가져와야 한다. 이때, 메모리에 있는 데이터들이 동일 선상에 있고 512비트에 정렬이 잘 되어 있다면 한번의 벡터 읽기로 모든 데이터를 가져올 수 있다. 만약 정렬이 되어 있지 않다면 추가적인 명령어가 필요해서 더 많은 시간이 걸리게 된다. 컴파일러는 정렬을 강제하거나 변수들의 정렬을 위한 지시어들과 커멘드 라인 옵션을 제공한다.
- 데이터 지역성
- 메모리가 아닌 캐시에서 데이터 읽기. 데이터는 궁극적으로 메모리에 있다. 그렇지만 벡터 읽기 명령어가 수행될 시점에 데이터들이 실행 유닛과 더 가까운 곳에서 읽혀 진다면 속도가 훨씬 더 빠를 것이다. 데이터들은 실제로 메모리에서 L2 캐시로 그리고 다시 L1 캐시로 이동된다. 이상적으로 이러한 동작은 데이터에서 L2 캐시로 미리 가져와지고(프리페치), 다시 L2에서 L1으로 그리고 마지막으로 L1 캐시에서 읽기(Load)명령어에 의해 데이터가 읽혀진다. 프리페치는 하드웨어 프리페치에 의해 시작되거나 컴파일러가 자동으로 아니면 프로그래머에 의해 수동으로 소프트웨어 프리페치에 의해 시작된다.
- 데이터 재사용. 만약 프로그램이 한 번 이상 사용되는 데이터를 캐시로 가져온다면 그런 데이터들은 서로 가까운 곳에서 있어야 한다. 이것을 임시 지역 참조성이라 한다. 데이터가 자주 재사용되는 것을 확실히 해주면 실제로 사용되기 전에 데이터가 캐시에서 밖으로 보내지는 것을 낮춰준다.
- Streaming Stores. 만약 프로그램이 나중에 다시 사용되지 않을 데이터를 쓴다면 Streaming Stores가 사용되게 하는 것이 중요하다. Streaming Store는 캐시 사용율을 높인다.
1. 데이터 정렬
벡터화가 잘 안되었다는 리포트가 나오면 데이터가 정렬되었는지를 살펴보아야 한다. 하나의 배열과 다른 배열과 의 상대적인 정렬이 중요하다. 제온 파이 보조 프로세서에서는 모든 데이터(보통 배열)을 64Byte 이내로 정렬하는 것이 중요하다.
2. 프리페치(Prefetching)
최대 성능을 내는 애플리케이션은 잘 정렬된 데이터를 처리할 계산 유닛에 연속해서 보내는 것이다. 인텔 제온 파이 보조 프로세서는 코어의 계산 유닛에 항상 처리할 데이터가 있도록 메모리 프리페치를 하드웨어, 소프트웨어 방식으로 지원한다. 프리페치라는 말은 미리 가져온다라는 말이다. 조만간 사용될 데이터를 메모리에서 L2 캐시로, L2 캐시에서 데이터 접근 시간이 더 빠른 L1캐시로 사용하기 전에 미리 가져다 놓는 것을 말한다. 만약 우리가 필요한 데이터가 캐시에 없고 메모리에 있다면 메모리로부터 가져오는 시간은 실행 유닛 측면에서 보면 엄청나게 느리다. 이것을 미리 하드웨어가 판단을 해서 또는 소프트웨어적으로 프로그래머나 컴파일러가 판단을 해서 미리 캐시에 가져다 놓으면 데이터가 사용될 때 데이터를 가져오는데 걸리는 대기 시간이 줄어들게 되서 속도를 향상 시킬 수 있게 된다. 소프트웨어적인 프리페치는 보조 프로세서의 벡터 명령어에서 지원한다.
캐시에 없는 데이터를 요구하는 벡터 로드 동작을 할 때 프리페치 동작이 일어나게 하는 것이 중요하다. L1 캐시에 데이터가 없으면 L2 캐시를 확인한다. 이 때 L2에서 데이터를 가져올 때 대기 시간이 발생한다. 만약 이때 L2에도 없을 때 메모리에서 가져와야 하는데 훨씬 더 많은 시간이 걸리게 된다. 이 시간이 아주 사소한 것 같지만 성능에 커다란 영향을 미친다. 이러한 캐시 미스를 없애는 것이 성능 향상에 중요하다. 대부분의 상용 라이브러리는 프리페치를 적용하여 성능을 향상 시킨다.
참고로 제온 파이 보조 프로세서에서 L1과 L2 캐시에서 데이터를 가져오기 위해서는 각각 1 클럭과 11 클럭이 필요하다. L2 캐시에서 미스가 발생하여 메모리에서 가져오게 되면 이 보다 훨씬 더 많은 100 클럭 이상의 시간을 소모해야 한다. 좀 더 자세한 내용은 지난 포스팅 글,
제온 파이 캐시 구조 및 TLB 을 참조하기 바란다.
4가지 종류의 프리페치
- 하드웨어가 자동으로 L2 캐시로 프리페치.
- 컴파일러가 자동으로 L1 캐시로 프리페치하는 소프트웨어 명령어 생성
- 프로그래머에 의해 지시를 받아서 컴파일러에 의해 생성된 프리페치 명령어
- 프로그래머에 의한 수동 프리페치 명령어. L1, L2 모든 곳에서 프리페치사용 가능
컴파일러 prefetch
인텔 컴파일러에서 최적화 수준을 –O2 또는 그 이상으로 설정하면 자동으로 프리페치가 설정됨.
Opt-prefetch = 3
-opt-prefetch=n n은 1부터 4까지.(리눅스)
/Qopt-prefetch:n (윈도우)
n이 높으면 높을수록 더 적극적으로 프리페치를 사용함
만약 알고리즘이 L2 캐시 크기에 맞도록 데이터를 잘 나눈다면 프리페치는 덜 중요할 수 있으나 일반적으로 제온 파이 보조 프로세서에서는 유용하다.
일반적으로 프로그래머가 프리페치를 수동으로 하는 것보다 컴파일러가 프리페치를 자동으로 사용하기를 권장한다. 만약 이렇게 해도 잘 되지 않으면 컴파일러에게 여러 가지 힌트를 주는 방법이 있다. 그래도 성능에 문제가 있다면 마지막 방법으로는 수동으로 프로그래머가 강제하는 방법이 있다.
Pragma 지시어를 사용하여 컴파일러가 프리페치를하도록 하는 것이 사용하기 편하고 간단하다. 우선적으로 사용하길 권장한다. 이후 필요할 때만 수동 프리페치 사용을 검토한다.
수동 프리페치(vprefetch0 과 vprefetch1 사용)
컴파일러가 여러 힌트를 이용해서도 프리페치를 효율적으로 사용할 수 없을 때만 사용하길 권한다. 이때 하드웨어의 제한된 프리페치 역량에서 하드웨어, 소프트웨어 둘 다 사용하면 성능 감소가 일어날 수 있기 때문에 컴파일러에 의한 자동 프리페치 사용을 하지 말아야 한다.
3. Streaming stores
Streaming store는 벡터화에서 특별한 경우이다. 만약에 데이터들이 다시 읽거나 인용되어 사용되지 않는다고 가정하면 이런 데이터들을 버퍼나 캐시에 저장을 할 필요가 없이 바로 메모리에 저장을 하는 것이 더 좋다. 왜냐하면 이 데이터들은 나중에 틀림없이 다른 데이터들에 의해 덧 씌어질 것이고 그동안 버퍼나 캐시를 낭비하는 것이기 때문이다. Streaming store는 나중에 메모리에서 또는 캐시에서 읽혀지지 않을 데이터들을 캐시나 버퍼가 아닌 메모리에 바로 저장을 함으로써 성능 향상을 가져오게 할 수 있다. 한정된 캐시를 낭비하지 않고 다른 것이 사용할 수 있게 함으로써 효율성도 높이는 것이다.
예를 들어 아래와 같이 배열 B와 C를 읽은 후에 그 합을 배열 A에 저장하는 코드가 있다고 하자.
for (I=0; I<HUGE; I++)
A[I] = k*B[I] + C[I];
일반적으로 메모리에 배열 A를 저장하기 위해서는 먼저 A 값을 캐시에서 읽어와야 한다. 저장만 하는 작업을 위해서 부가적인 읽기 동작이 일어나게 되는 것이다. Streaming Store 명령어는 먼저 캐시에서 읽을 필요없이 바로 메모리 주소에 저장을 한다. Streaming Store이 없다면 A, B, C를 읽은 후에 계산을 한다. 그 후에 그 결과값을 A에 다시 써야하는 과정을 거친다. 그렇지만 Streaming Store 명령어를 쓰게 되면 A를 읽지 않고 B, C를 읽은 후 계산 값을 바로 메모리 주소에 바로 A를 쓴다. 따라서 있을 때와 없을 때를 비교하면 A를 읽는 과정이 생락되게 된다. 아래 표는 위 코드 예에서 Streaming Store를 사용할 때와 그렇지 않을 때의 성능 비교이다. 30%정도의 차이가 나는 것을 보여준다.
 |
Streaming Store의 성능 효과 |
컴파일러 옵션
-opt-streaming-stores keyword (리눅스, OS X)
/Qopt-streaming-stores: keyword (윈도우즈)
댓글 추가