본문 바로가기

3.구현/C or C++

블록 메모리 복사 성능시험

이 글은 IBM에 올라온 글이며 윈도우와 리눅스의 메모리 복사에 대한 흥미로운 비교를 다룬 기사이다.
-ospace

################################################

RunTime: 블록 메모리 복사

Linux Windows에서의 고성능 프로그래밍 기술

문서 옵션

난이도 : 초급

Edward G. Bradford 박사, 수석 Programmer, IBM

2001 6 01 a

이전 칼럼 에서 Bradford 박사는 Linux Windows 2000을 측정할 툴과 앞으로의 성능 조사 계획을 설명했다. 이 글에서는 간단한 작동, 메모리에서 메모리로의 복사, 메모리에서 byte를 이동하는데 걸리는 시간 등을 중심으로 설명한다.

메모리 복사

Memory copy 는 컴퓨터 환경에서는 흔한 일이다. 네트워크 애플리케이션, 데이터베이스 애플리케이션, 과학 애플리케이션 (scientific application), 기타 애플리케이션에서 찾아볼 수 있다. 메모리 복사는 일반적이기 때문에 프로그래머들은 다양한 프로그래밍 기술과 함께 자유롭게 수행하곤 한다. 메모리 복사는 전체 메모리 블록을 옮기는 것이 될 수 있고 단순한 복사(copy)가 아닌 일종의 액세스 패턴(access pattern)이 될 수도 있다.

액세스 패턴은 메트릭스의 칼럼에 액세스 함으로서 형성된다. 예를 들어 12x12 매트릭스에서 12번째 마다 단어가 필요하다고 하자. 12개 문자들은 매트릭스의 첫번째 칼럼이다. 일반적으로 칼럼 액세스에는 칼럼 엘리먼트 당 32bit 또는 64bit가 필요하다; 64bit 이상 필요한 경우는 거의 없다. 복잡한 매트릭스들은 한 칼럼 엘리먼트에 64bit 값 두개가 필요하다. 액세스 사이의 바이트(byte)의 수는 스트라이드(stride)라고 하며 메모리로의 액세스는 스트라이드 값과 함께 매개변수화(parameterize) 된다..

위로

블록 메모리 복사 속도 측정

이 글에서는 블록 메모리 복사 만을 다루겠다. 메모리 복사 속도를 측정하는 것은 힘든 일이다. 컴퓨터가 level-1 또는 level-2 또는 (가끔씩) level-3 캐시를 가지고 있어서 테스트를 할 때에 캐시 사이즈를 다루어야 하기 때문이다. 여기서는 데이터 전송이 얼마나 빨리 이루어지는 지를 살펴볼 것이다. 시스템 버퍼링이나 캐시 관련 문제 보다는 코드 경로의 길이를 이해하는 것에 초점을 맞출 것이다. 시스템 버퍼링과 캐시는 다음 글에서 자세히 다루겠다. 코드 경로(Code Path)는 중요하다. 코드 경로는 byte 가 얼마나 빨리 이동될 수 있는지 나타내는 지표가 되기도 하고 전송과 관련하여서 얼마나 많은 오버헤드가 있는지도 나타내기 때문이다.

메모리 전송(transfer)은 간단한 프로그래밍 테크닉으로 이루어진다. 간단한 for 루프와 memcpy() 라이브러리 루틴이 가장 자주 쓰이는 메커니즘이다. 드문 경우지만 구조체 할당과 파일 입출력이 사용되기도 한다.

일반적으로 메모리 전송은 전체 퍼포먼스로 볼 때 작은 역할이라고 볼 수 있다. 그들이 어떤 작동을 할 때, OS와 하드웨어가 한 팀이 되어 무엇을 수행하는 가를 알아두는 것도 좋을 것 같다.

이 글을 통해 다루고 싶은 것은 많지만 불가피하게 좁혀나가야 겠다. 연구 범위를 확대할 수록 쓸만한 결론에 도달하기란 더욱 힘들어지기 때문이다. 다음은 연구 범위이다. :

  • Stride- 매트릭스 액세스와 종종 연관됨
  • 총 전송 사이즈 (Total transfer size)- 전송된 총 데이터의 양
  • 블록 사이즈 (Block size)- 단일 오퍼레이션으로 이동할 수 있는 데이터의 양 (다음 글 주제와도 연관됨)
  • Programming technique-memcpy, (char *, double *) pointer
  • 시스템 페이지(page) 크기
  • Translation Lookaside Buffer (TLB) size

본 연구에서는 총 전송 사이즈를 16MB로 고정할 것이다. 또한 stride 0으로 고정할 것이다. 다시 말해서 간단한 블록 메모리 전송에 대해서만 다룰 것이다. 블록 사이즈는 프로그래밍 기술이 다양하면 그만큼 다양해 질 것이다. 시스템 페이지의 크기는 Windows 2000 Linux 모두 4K 이다. 같은 컴퓨터로 테스트하는 것이기 때문에 TLB size는 같다. 메모리 전송에는 단순한 쓰레드가 사용 될 것이기 때문에 특별한 쓰레딩 관련 이슈는 없다.

위로

테스트 프로그램

우리가 사용하게 될 프로그램은 memxfer5b.cpp 이다. 다양한 버전에서 가능하다. 사용법 메시지는 다음과 같다 :


memxfer5b usage message

  Usage: memxfer5b.exe [-f] [-w] [-s] [-p] size cnt [method]

       -f flag says to malloc and free of the "cnt" times.

       -w = set process min and max working set size to "size"

       -s = silent; only print averages

       -p = prep; "freshen" cache before; -w disables

       -csv = print output in CSV format

       methods:

        0:     "memcpy (default)"

        1:     "char *"

        2:     "short *"

       3:     "int *"

        4:     "long *"

        5:     "__int64 *"

        6:     "double *"

"-P" 옵션은 유용하다. 거의 모든 테스트 실행에 있어서 초기 복사는 나머지 실행 보다 느렸다. 이는 캐시가 로드 되고 있었다는 것을 의미한다. 여기에서는 메모리 전송 속도 보다는 코드 경로에 특별히 초점을 맞춰 측정할 것이다. 따라서 "prime" 메모리에 "-p" 옵션을 사용한다. 가장 빠른 (가장 짧은 시간) 메모리 전송을 수행하는 것이 목표이다. 실제로는 대부분 "un-primed" 캐시와 함께 초기 전송을 하는 것 같다. 그럼에도 불구하고 테스트에서 최상의 퍼포먼스를 위해 코드 경로를 선택한다면 최적의 코드 퍼포먼스를 위한 올바른 절차를 밟고있는 것이다.

Memxfer5b 7개의 다른 메모리 전송 테크닉을 사용할 수 있다. "권장할 만한" memcpy() API Linux Windows 모두 가능하다. 6개의 다른 포인터(pointer) 타입도 사용 가능하다.

Memxfer5b.cpp는 다음과 같은 명령을 이용하여 쉽게 컴파일 한다 :

    gcc -O2 memxfer5b.cpp -o memxfer5b

    cl -O2 memxfer5b.cpp -o memxfer5b.exe

Memxfer5b.cpp이전 칼럼에서 설명했던 것과 같은 지원 루틴을 사용한다. Malloc() 루틴을 지원 루틴에 추가 시켜라. Malloc() malloc()과 같이 작동하지만 메모리를 할당할 수 없을 때 에러 메시지를 출력하고 종료된다. 우리는 메모리 할당의 속도를 측정하고 있는 것이 아니다. 따라서 이 테스트에서 메모리 할당에 실패했다면 프로그램에 버그가 있거나 시스템 한계에 도달했다는 것을 의미한다. (Malloc() 루틴은 이전 칼럼에서 언급은 되어 있지만 소스 코드는 포함되어 있지 않아서 이 글에 포함시켰다. 참고자료 확인)

P이전에, 우리는 Microsoft C++ 컴파일러에 대한 다양한 옵션을 연구했다. "O2" 보다 나은 것이 있는지를 알아보기 위해서 였다. 하지만 테스트를 통해서도 더 나은 것을 찾지 못했고 그 후 지속된 연구도 허사였다. 이 글을 읽고있는 독자 중에 개인적으로 선호하는 최적화 매개변수화로 cl.exe memxfer5b.cpp를 컴파일하여 더 나은 퍼포먼스를 얻었다면 discussion forum(US) 을 통해서 알려주기 바란다. 투입된 인원이 많다면 cl.exe parameter space를 찾는 것(searching)이 더욱 효과적이다. :)

memxfer5b.cpp 의 main loop는 실제로 메모리를 이동시키고 전송된 것을 호출과 함께 타이밍 루틴에 묶는 부분이다. memxfer5b.cpp는 블록 메모리 전송의 다른 영역을 검색(explore)한다. 향후 버전에는 striping 이 추가되어 매트릭스 작동을 시뮬레이션 할 수 있도록 할 것이다.

위로

테스트 시스템

Windows 2000 Advanced Server Service Pack 1, Linux 2.2.16, Linux 2.4.4에서 테스트를 실행할 것이다. Linux 버전은 Red Hat 7.0 이다. Red Hat 7.0 배포판에 포함된 gcc를 사용할 것이다. Windows 2000 에서는 Visual Studio 6.0에 있는 Microsoft C++ Version 12.00.8168 을 사용할 것이다. 테스트 시스템은 ThinkPad 600X Model 2645-9FU (576 MB 메모리, 12 GB 디스크)이다. "600X" Windows 2000 에서는 648 MHz Pentium III 이고, Linux 647.767 MHz 이다. 다음은 CPU 정보를 찾기 위한 Windows 2000 Linux 에서의 방법이다 :


Windows 2000
MHz로의 네비게이션 경로:

              Start/

              Settings/

                Control Panel/

                Administrative Tools/

                Computer Management/

                      System Tools/

                        System Information/

                        System Summary



MHz
를 나타내는 Linux 명령:

              cat /proc/cpuinfo

이 프로그램은 초(sec)당 메가바이트(megabyte)로 메모리 속도 결과를 프린트한다. "-s" 플래그가 지정되면, 메모리 복사 속도는 "cnt" 가 실행되는 동안 계산된다. 그렇지 않으면 각각의 run 은 프린트 된다. 테스트는 다음과 같이 수행될 것이다 :

       memxfer5b -p -s -csv 16m 8 0 1 2 3 4 5 6

       memxfer5b -p -s -csv 16m 8 0 1 2 3 4 5 6

       memxfer5b -p -s -csv 16m 8 0 1 2 3 4 5 6

       memxfer5b -p -s -csv 16m 8 0 1 2 3 4 5 6

이것은 8개의 실행 중에서 4개만을 보여준 것이다. 다양한 방법은 기대하지 말라.

위로

테스트 결과

결과는 다음과 같이 테이블에 나타나있다. Linux의 구형 버전과 새로운 버전 모두 Windows 2000 보다 메모리 전송에 있어서 훨씬 빠르다. 앞으로 더 많은 연구가 필요하다.

memxfer5b -s -p -csv 16777216 8

Method Average memory speed in megabytes per second

Linux 2.2.16-22 Linux 2.4.4 Windows 2000 AS

memcpy 173.644 179.417 132.077

char * 169.683 169.000 93.494

short * 170.065 172.333 96.156

int * 170.136 172.648 102.507

long * 170.066 172.050 123.498

__int64 * 170.094 172.330 123.498

double * 169.778 171.192 123.283

Windows memxfer5b.exe 프로그램의 어셈블리 리스팅 실험에서 Microsoft C++ 컴파일러는 mcmcpy API로의 호출을 받을 때 "rep movs" 명령어(instruction)를 사용한다. 반면에 "char *" character-by-character 이동, "short *" word-by-word 이동, 그리고 "int *", "long *", "__int64 *", "double *" dword-by-dword 이동 (Memxfer5b는 잘못 배열된 boundary 조건을 막는다)을 이용한다.

Linux 바이너리에서도 비슷한 실험을 했을 때 (거의) 같은 코드가 생겼다. 단 한가지 예외는 gcc가 실제로 memcpy() 루틴을 사용한다는 것이다. 두 가지 컴파일러 모두 바이트, 16-bit 문자와 32-bit 문자만을 이동시킨다. Linux Windows의 생성된 어셈블리 코드는 매우 유사하다.

메모리 복사 퍼포먼스에 있어서 차이점이라고 할 수 있는 것은, Windows Linux 보다 배후에서 더 많은 작동(work)을 한다는 것이다. 이를 시험하기 위해서 단일 프랙탈(fractal) 포인트를 계산하는 강화된 프로그램을 작성해보자. 아규먼트 없이, 100,000,000 회 반복을 수행했거나 포인트가 "없어질 때까지" fract2.cpp 는 프랙탈 수식을 반복한다. Fract2.cpp는 부동소수 (double) 루프 계산 프로그램이다.

다음 결과를 보자 :

Operating system 완료까지의 시간 ()

Linux 2.2.16-22 4.065

Linux 2.4.4 4.087

Windows 2000 AS 4.300

시간(times)이 의미하는 것은 두 개의 Linux의 버전 중 하나를 실행한다면 같은 하드웨어 상에서 더 많은 계산이 이루어질 수 있다는 것을 의미한다. 한편, 두개의 최적화된 컴파일 프로그램이 역어셈블 될 때, 생성된 루프가 거의 일치한다는 것을 알 수 있다. 사실, Linux 루프는 Windows가 생성한 루프보다 두개나 더 많은 instruction을 포함하고 있다.

fract2.cpp times 는 메모리 복사 속도에 있어서 큰 차이를 보이지 않는다. fract2.cpp 는 몇 가지 차이점만을 보인다.

위로

결론

지금까지의 연구로는 메모리 복사를 완전히 이해할 만한 충분한 정보를 얻어낼 수 없다. 더욱이 우리는 블록 메모리 전송의 일부만을 살펴보았다. 따라서 Linux Windows의 장점이 무엇인지에 대한 결론에는 아직 도달하지 못했다. 결론지을 수 있는 유일한 사실은 양 플랫폼 상에서 16 메가바이트(megabyte)를 전송할 때에는 memcpy   사용하는 것이 훌륭한 방법이라는 것이다. 또한 fract2.cpp 프로그램에 의해 나타난 것처럼 Windows에는 아주 작은 추가적 오버헤드가 있었다. 여러분은 작은 실험을 통해서 좀 더 효율적으로 시스템을 만드는데 도움을 얻을 수 있길 바란다.

해답 보다는 많은 의문점을 남겨놓은 것 같다. 다음 글에서 메모리 퍼포먼스에 대해서 좀더 자세히 다루겠다.

위로

참고자료

  • developerWorks worldwide 사이트에서 이 기사에 관한 영어원문.
  • introductory article : RunTime 시리즈의 첫 칼럼.
  • 본 기술자료에 사용된 참조 파일: :

위로

필자소개

Edward Bradford 박사는 현재 IBM Software Group을 위한 Microsoft Premier Support를 관리하고 있으며, Linux Windows 2000 소프트웨어 개발자를 위한 주간 뉴스레터를 담당하고 있다.

반응형

'3.구현 > C or C++' 카테고리의 다른 글

[패턴] Command  (0) 2007.02.16
Vectors in STL  (0) 2007.02.16
메모리 복사 성능시험 (memcpy)  (0) 2007.02.16
디버거 - 로그 윈도우 2.5 (Win32 디버그 지원)  (2) 2006.11.23
[c/c++] 구조체 복제에 대한 이야기  (0) 2006.11.21