3세대 Intel Core 프로세서


무어의 법칙[각주:1]이 사실상 깨진 것이나 다름 없고, 수년 전부터 CPU 성능의 향상은 클럭 속도 향상보다는 병렬처리 쪽에 더 힘이 실리고 있다.

이를 위해 다중 코어 프로세서가 일반화되었고, 다양한 SIMD 기술들이 적용되고 있다.

또한, 컴파일러에서도 지시자를 통한 OpenMP, 자동 병렬화 및 SIMD의 intrinsic 함수를 원활히 지원하는 추세이다.


이미지 리샘플링을 구현하면서 다중 코어 활용 기술과 SIMD를 모두 적용해서 최대한의 성능을 발휘하도록 해뒀다.

그런데, 이 구현 방식들 간에 어느 정도나 성능 차이가 발생하는지 확인을 해보고 싶어졌다.


구현한 리샘플링은 Lanczos3Mitchell-Netravali 두 종류이고, 이를 float[각주:2]long int[각주:3]로 각각 구현해 다시 SIMD를 적용해서 최적화했다.

구체적인 구현 내역은 다음과 같다.


RGB 또는 RGBA 한 픽셀을 동시에 처리하도록 구현. 즉, 최대 4개의 값을 동시에 처리함


앞에서도 간단히 설명했지만, 부동소수점(float) 외에 정수(long int) 범위에서 처리하는 것으로 동일한 구현을 해봤는데, 아무래도 소수점의 곱셈보다는 정수가 조금은 더 빠를 것이라 판단했기 때문이다.

소수점 단위 처리는 24비트(32 - 8 = 24) 영역을 활용하면 정밀도는 다소 떨어지더라도 충분히 수용 가능한 결과를 얻을 수 있기도 하고.


리샘플링 테스트는 임의의 폴더에 저장한 35개의 이미지 전체를 리샘플링하는 시간을 측정했다.

각각의 이미지에 대해 8bpp, 24bpp, 32bpp이미지로 변환한 뒤 3 종류의 비율을 적용하여 리샘플링하는데 걸린 시간을 모두 더했다.

물론, 이 과정에서 리샘플링 후 이미지가 깨지거나 화질이 저하되는지 여부도 함께 확인했다.


그런데…



1. Lanczos3의 의외의 결과


일단 long int로 구현한 것과 float로 구현한 것의 화질 차이는 전혀 없다. 그런데, 일단 long int 쪽이 float로 구현한 것보다 더 느리다

상당히 당황스러운 결과다.


게다가, long int로 구현한 경우에는 SSE2, SSE4.1 등을 활용하면 scalar 연산에 비해 조금 더 빨라지는데 반해, float 쪽은 그것마저 더 느려진다.

반복되는 수치 연산도 연산이지만, 연산 결과를 0~255 범위로 잘라내는 clampscalar 쪽은 if () 문으로 구현되어 있는데 말이다.


AVX를 적용해서 두 픽셀 단위로 동시에 처리(즉, RGBA 두 세트이므로 8개 단위로 동시에 처리)해도 성능 향상은 없었다.

즉, 쿼드 코어 이상의 최신 CPU에서는 그냥 float로 구현해서 CPU만 활용하는 쪽이 오히려 최상의 성능을 발휘하는 것이다.


참고로, 이 결과는 AMD A8-3870K4세대 i5, 3세대 i7 등에서 모두 유사하게 나왔다.




2. Mitchell-Netravali 역시 비슷한 결과


혹시나 하는 생각으로 Mitchell-Netravali 리샘플링도 돌려봤지만, 유사한 결과가 나왔다.


유일한 차이라면 long int로 구현했을 때에도 SSE2가 오히려 scalar 연산에 비해 아주 조금이지만 느려졌다는 점.




3. 위의 1과 2를 함께 보면…


Lanczos3 쪽이 미묘하게 조금 더 느리다. 이건 그냥 Lanczos3 쪽이 계산 범위가 조금 더 넓어 연산량이 많기 때문.

역시 float로 구현해서 SIMD는 사용하지 않고 scalar 연산만 사용하는 것이 가장 빠르다는 결론만 재확인.




4. 다른 리샘플링 코드는 어떨까?


혹시 내가 뭔가를 잘못 구현했을 수도 있다는 생각에 다른 리샘플링 소스를 뒤져봤다.

이 중 선택한 것은 Aleksey Vaneev의 AVIR 리샘플러.

상당한 완성도라고 스스로 얘기하고 있으며, SSE2, AVX 등의 기술도 활용할 수 있다.


그래서 돌려보니 결국 유사한 패턴의 결과가 도출되었다.


위의 구현들보다 시간이 4배 정도 더 소요되는 것은, 다양한 기능 적용을 위해 최적화가 덜 될 수 밖에 없기 때문[각주:4]인 것 같다.




5. 결론


일단 최신 CPU… 에서는 모든 연산에 대해 SIMD가 scalar 연산에 비해 빠른 것은 아니다.


SIMD는 구현 단계에서 다양한 최적화 이슈를 만족하는 경우[각주:5]엔 비약적인 성능 향상을 보여준다.

하지만, 최신 컴파일러들은 scalar 연산만을 사용하더라도 다양한 기술을 적용해서 가능한 최대의 성능을 발휘하도록 도와준다.


SIMD를 적용하는 것은 당연한 추세이지만, 무조건 scalar보다 SIMD가 더 빠르다는 편견을 가질 필요는 없다.



  1. 반도체 집적회로의 성능이 24개월마다 2배로 증가 [본문으로]
  2. 최초 구현은 double로 했으나, 최적화 과정에서 float로 변경 [본문으로]
  3. 원본 데이터가 8비트이며, long은 32비트 이므로 차이인 24비트 영역을 활용해 정수 영역에서 계산하면 float에 비해 약간의 성능 향상을 기대할 수 있다고 판단 [본문으로]
  4. AVIR은 다양한 파라미터를 지정 가능하고 템플릿을 적용하여 입력 값으로 BYTE 외의 형도 가능함 [본문으로]
  5. 메모리 병목현상 등을 제대로 고려해서 코딩해야 충분한 성능이 발휘됨 [본문으로]
  1. Tz 2018.06.30 15:00 신고

    의미있는 정보 감사합니다.

+ Recent posts