Developing for Performance

OSXDEV

Jump to: navigation, 찾기

Introduction to Performance Overview로 이동


소프트웨어 디자인에서 퍼포먼스(성능)는 심각한 문제가 되어 나타나기 전까지는 간과되기 쉬운 부분이다. 퍼포먼스 튜닝을 위해 개발 과정의 마지막까지 기다린다면, 어떤 중요한 개선을 이루어내기에 너무 늦을지도 모른다. 퍼포먼스는 개발 단계에 미리 포함되어 개발 과정에서 지속적으로 개선을 해야할 부분이다.

이 문서는 퍼포먼스를 위한 디자인을 할때, 퍼포먼스가 무엇인지 이해하는데 도움을 준다. 이 챕터의 내용은 퍼포먼스에 영향을 미치는 요소에 관한 배경 지식을 제공해 준다. Mac OS X 에서 이러한 요소들을 증명하고, 모니터링 하는 내용을 다룰 것이다.


목차

[편집] 퍼포먼스란 무엇인가?

"퍼포먼스"라는 용어는 사람들마다 각자 다르게 해석된다. 그러므로 애플리케이션의 퍼포먼스를 개선하는 문제를 다루기 전에, 이 용어의 의미부터 생각해보자.

많은 사람들은 퍼포먼스를 속도와 동일하게 생각한다. 실제로 프로그램이 복잡한 연산을 1초안에 끝낸다면, 여러분은 프로그램이 괜찮은 퍼포먼스를 보여준다고 생각할 수도 있다. 그러나 속도는 잘못된 측정방법이 될 수도 있다. 복잡한 소프트웨어 시스템에서는 연산의 처리 속도가 고정된 값이 아니다. 다른 조건에서 똑같은 연산을 몇번 수행해보면, 이 연산을 완료하는데 소요되는 시간이 다양하게 나타날 수도 있다. 이것은 프로그램이 로컬 시스템의 자원을 공유하는 수많은 프로세스들 중의 하나이고, 이 리소스의 사용은 다른 프로세스에 영향을 미치기 때문이다.

다음의 섹션은 퍼포먼스를 효율적인 자원의 사용과 사용자 체감속도의 두개의 개념으로 설명한다. 어떻게 애플리케이션을 디자인 하고 구현할 것인가에 있어서 이 두 개념은 중요하다. 그리고 전반적인 퍼포먼스의 향상을 위해 어떻게 이 두 개념을 이용할 것인가를 이해하는 것도 중요하다.


[편집] 자원의 효율적인 사용

컴퓨터는 제한된 수의 리소스를 모든 실행중인 프로세스 사이에서 공유한다. 가장 낮은 단계로 내려가서, 이 리소스를 다음과 같이 분류할 수 있다.

  • CPU time (CPU 사용 시간)
  • Memory space (메모리 공간)
  • Mass storage space (저장매체 공간)

모든 데이터는 메모리에 들어있거나 다른 저장매체에 들어있고, 반드시 CPU에 의해 처리된다. 효율적인 애플리케이션은 이러한 모든 자원을 신중하게 사용한다. 다음의 섹션에서 각각의 자원들과 프로그램에 미치는 효과에 대해 다루도록 하겠다.


CPU time

CPU time은 시스템에 의해 할당된다, 그러므로 할당된 시간을 가능한 최대한 활용해야 한다. Mac OS X는 대칭적 멀티프로세싱(symmetric multiprocessing)을 구현하고 있기 때문에, 시스템의 각각의 쓰레드는 실행될 짧은 시간(최대 10밀리세컨드)을 할당받는다. 이 시간이 지나면(대부분의 경우 그 이전에) 시스템은 CPU 제어권을 회수하여 다른 쓰레드에게 넘긴다.

전형적인 Mac OS X 시스템(100개 이상의 쓰레드가 동시에 실행)에서, 모든 쓰레드가 할당된 모든 시간을 사용하였다면, 퍼포먼스는 끔찍해질 것이다. 이러한 사실로부터 다음과 같은 목표를 세울 수 있다.


목표: 프로그램이 아무것도 하지 않는다면, CPU time을 소비해서는 안된다.


이 목표를 달성하기 위한 최고의 방법은 이벤트 기반(event-based) 모델을 사용하는 것이다. Carbon Event Manager같은 요즘의 이벤트 처리 시스템을 사용하면 프로그램의 쓰레드들이 수행할 작업이 있을때만 동작한다.

애플리케이션이 처리할 작업이 있다면, 가능한한 최대한 효율적으로 CPU time을 소비해야한다. 이것은 처리할 데이터의 양에 적합한 알고리즘을 선택해야하는 것을 의미한다. 이것은 또한 특별한 종류의 연산을 처리하기 위해 vector unit(Velocity Engine이나 SSE)이나 그래픽 프로세서같은 다른 시스템 자원을 사용하는 것을 의미한다. 이러한 사실로 부터 다음과 같은 목표를 세울 수 있다.


목표: 할수있는대로 CPU가 처리할 작업을 줄여라.


CPU time을 효율적으로 사용할 수 있는 방법에 대한 기본적인 정보가 기본적인 최적화 팁들에 나와있다. 드로잉 연산의 속도를 증가할 수 있는 팁이 필요하면 드로잉 코드를 보기 바란다.


Memory Space

현대 컴퓨팅 하드웨어에서 메모리는 점차 느린 타입의 메모리로 구성된다(하지만 용량이 더 크다). (역자 주: 컴퓨터의 메모리가 점차 느려진다는 뜻이 아니고, Register -> Cache -> Main Memory 와 같이 메모리 구성요소들이 점차 느려진다는 뜻이다.) CPU에게 있어서 가장 빠른 메모리는 CPU의 레지스터이다. 그 다음 빠른것은 L1 캐쉬이고, 그 다음이 L2와 L3 캐쉬이다. 그 다음으로 빠른것은 메인 메모리이다. 가장 느린 메모리는 디스크에 있는 가상 메모리 페이지이다, 이것들은 사용되려면 반드시 page in 되어야 한다.

모든 애플리케이션이 시스템에서 가장빠른 캐쉬 메모리에 들어갈 만큼 중분히 작으면 이상적이겠지만, 불행히도 대부분의 애플리케이션의 코드와 데이터는 메인 메모리나 디스크에 page out되어 들어있다. 그러므로 애플리케이션의 코드와 데이터가 이런 느린 매체에서 소비되는 시간을 최소화 할 수 있는 방법으로 구성되는 것은 중요하다. 이러한 사실로 부터 다음과 같은 목표를 세울 수 있다.


목표: 프로그램의 메모리 사용(memory footprint)을 줄여라. (역자 주: 메모리 사용이 malloc 같은 것의 사용하는 것을 뜻하는게 아니고, 메모리에 올라가는 프로그램 코드 자체를 뜻하는 것이다.)


프로그램의 메모리 사용을 줄이는 것은 퍼포먼스를 크게 향상시킨다. 메모리를 적게 사용하면 보통 두가지 이점이 있는데, 첫째로 프로그램이 작아질수록 메모리 페이지를 적제 차지한다. 메모리 페이지를 적게 차지하는 것은 일반적으로 페이징이 더 적게 일어나는 것을 의미한다. 둘째로 일반적으로 더욱 최적화되고 조직화될수록 코드의 크기가 작아진다. 그러므로 주어진 작업을 수행하기위해 더 적은 명령(instruction)이 필요하다. 그리고 작업을 위한 모든 코드들이 같은 메모리 페이지 세트에 모여질 수 있다.

애플리케이션의 메모리 사용을 줄이는 것에 더해서, 애플리케이션의 쓰기가능한 메모리 페이지의 사용을 줄이도록 해야한다. 쓰기가능한 메모리 페이지에는 애플리케이션의 전역(global) 또는 할당된 데이터가 저장된다. 메모리가 모자라서 page fault 가 발생되면, 가상 메모리 시스템은 다른 프로세스를 위한 공간을 확보하기 위해 여러분의 애플리케이션에 할당된 페이지를 회수해갈 필요가 생길 수도 있다. 만약 페이지가 변경되지 않았다면, 시스템은 그것을 회수하여 바로 데이터를 쓸 수 있다. 그러나 페이지가 변경된 경우엔 시스템은 첫째로 변경내용을 디스크에 써야한다. 디스크에 데이터를 쓰는 것은 page fault를 처리하는데 많은 시간이 걸리게 한다.

프로그램의 메모리 사용을 줄이는 기본적인 정보가 애플리케이션 코드의 메모리 사용에서 제공된다. 메모리를 효율적으로 사용하는 것에 대한 내용은 메모리 할당 코드를 보기 바란다.


Mass Storage Space

어떤 컴퓨터든 거의 모든것이 파일로 저장되어 있기 때문에 파일 시스템의 퍼포먼스는 중요하다. 여러분의 애플리케이션, 데이터, 그리고 심지어 운영체제까지 모든게 파일로 저장되어 있다가 시스템의 다른 부분에 비해 엄청나게 느린 장치로부터 메모리에 로드되어야 한다. 파일 시스템은 로컬이든 네트워크든 퍼포먼스에 있어서 가장 큰 병목(bottleneck)중에 하나이다.


목표: 불필요한 파일 처리를 없애고 그 정보가 실제로 필요해질때까지 처리를 미뤄라.


파일 처리를 없애거나 미뤄버림으로써 이 병목을 제거하여 애플리케이션의 전반적인 성능을 향상시킬 수 있다. 파일로부터 데이터를 요청하는 시간과 프로그램이 그 데이터를 실제로 확인하는 시간 사이에 천만개의 CPU 사이클이 지나간다. 프로그램이 더 많은 수의 파일에 접근하면, 요청한 모든 데이터를 받기전에 많은 시간을 기다려야 한다.

Mac OS X 에서 여러분의 애플리케이션과 이것이 생성하는 파일은 로컬 하드 디스크 대신에 네트웍에 있을 수도 있다는 점을 기억하자. Mac OS X는 가능한한 네트웍을 안보이도록(invisible) 한다. 그러므로 파일이 로컬에 있을거라고 짐작하면 안된다.

프로그램의 파일 기반의 퍼포먼스를 향상시킬 수 있는 방법을 파일 엑세스 코드에서 다루고 있다.

[편집] 체감 속도

최상의 퍼포먼스를 위해 애플리케이션을 튜닝했다 하더라도, 여러분의 애플리케이션이 사용자에게 여전히 느리게 느껴질 수 있다. 이러한 문제는 피해갈수가 없다. 처리할 작업이 많다면, 그 작업을 처리할 CPU time과 자원이 필요하다. 이러한 상황이라면 애플리케이션의 체감 속도를 향상시켜야 한다. 다음의 목표를 통해 이것을 해결할 수 있다.


목표: 프로그램이 사용자에게 빠르게 반응하도록 하라


응답성은 보통 실제 처리속도보다 사용자에게 더 중요한 요소이다. 프로그램이 시기적절한 방법으로 명령에 응답한다면, 사용자는 보통 어떤 작업을 처리하는데 시간이 더 걸릴수도 있다는 사실을 기꺼이 받아들이게 된다. 이런식으로, 프로그램이 백그라운드에서 데이터를 처리하는 동안 사용자가 작업을 계속하게 함으로써 체감속도의 문제는 해결된다. 애플리케이션에 쓰레드를 사용하는 것은 사용자에게 응답성을 향상시키는 좋은 방법이다. 메인 쓰레드가 사용자에게 응답하는 동안, 작업(worker) 쓰레드는 다른 시간소모적인 작업을 처리하거나 계산한다.

또 다른 일반적인 방법은 애플리케이션의 처음 시작시간(launch time)을 가능한 빠르게 만드는 것이다. 애플리케이션이 처음 시작되는데 몇초 이상의 시간이 걸린다면 너무 많은 일을 하고 있는 것이다. 그 시간동안 사용자에게 응답하지 못할 뿐만 아니라, 당장 필요하지 않거나 또는 앞으로 전혀 쓰이지 않을 자원을 로딩하는 것은 낭비이다.

처음 시작시간을 개선하는 방법에 대한 내용은 프로그램 시작시 초기화 코드 에 나와있다. 프로그램의 체감속도를 개선시키는 방법에 대한 내용은 체감 속도를 향상시킨다를 보기 바란다.

[편집] 퍼포먼스 추적하기

퍼포먼스가 높은지 확신할 수 있는 유일한 방법은 퍼포먼스에 대한 목표를 제품 설계에 포함시키고 개발 과정을 통해서 그 목표에 대하여 제품을 측정하는 것이다. 높은 퍼포먼스는 개발 과정의 마지막 단계에서 코드에 접목시킬 수 있는 그런 특성이 아니다. 이것은 밀접하게 그 과정에 묶여야 한다. 코드가 쓰여지면 이것이 프로그램의 전체적인 성능에 어떤 영향을 미치는가를 아는 것은 중요하다. 만약 퍼포먼스 문제를 미리 발견한다면, 너무 늦기전에 그 문제를 해결할 수 있는 좋은 기회를 갖게 되는 것이다.

특정한 목표에 달성했는지 또는 초과했는지 결정할 수 있는 방법은 측정값을 모으는 것이다. Apple은 프로그램의 퍼포먼스를 모니터링하고 분석할 수 있는 몇가지 툴을 제공하고 있다. 측정 툴을 바로 여러분의 코드안에 삽입하고 빌드하여 측정 자료를 모으는 과정을 자동화 시킬 수 있다. 어떤 접근방법을 선택하던지, 이러한 툴을 지속적으로 실행하고 그 결과를 분석해야 한다.


[편집] 기준이 되는 값을 정하기

첫째로 해야할 일은 측정할 기준 값을 결정하는 것이다. 사용자에게 가장 중요하다고 생각되는 작업을 정하고 그 작업을 하는데 제약이 되는 사항들을 식별한다. 예를 들면, 문서 하나를 1초 내로 읽어들이기를 원한다거나 100킬로바이트 이상의 메모리를 할당하지 않기를 원할 수도 있다.

측정하기로 선택한 작업은 반드시 사용자의 요구를 반영하여야 한다. 여러분의 마켓팅 부서는 사용자가 중요하다고 판단할 수 있는 작업들을 선택하는 것을 도와주어야 한다. 완성된 제품을 가지고 있다면, 사용자가 어떤 부분을 느리다고 생각하는지를 알아내어 여러분이 작업할 목록에 추가해야 할지를 고려해야 한다.

일단 추적해야할 작업의 목록이 정해졌으면, 각각의 작업에 대한 퍼포먼스의 목표를 결정할 필요가 있다. 이미 있는 제품에 대해서는, 간단히 이전 버젼보다 퍼포먼스를 향상시키는 것을 목표로 할 수도 있다. 물론 경쟁하고 있는 제품에 대한 퍼포먼스를 측정하여 그 퍼포먼스를 충족시키거나 초과하는 것으로 목표를 잡을 수도 있다. 만약 새로운 제품을 갖고 있다면, 합당한 값을 찾기위해 여러 값들을 가지고 실험을 해야 할 것이다. 다른 방법으로, 도전적인 목표치를 잡고 최대한 그 목표에 가까워지도록 하게 할 수도 있을 것이다.

퍼포먼스의 측정과 함께, 일관성(consistency)도 중요하다. 기준 값을 잡기위한 과정에 그 값들을 모으는데 사용된 시스템에 대한 정보도 포함되어 있어야 한다. 시스템의 하드웨어와 소프트웨어 설정을 어느 정도 기록하고, 항상 같은 설정으로 테스트를 수행하자. 기준값에 도달할 수 있는 가능한 가장 느린 하드웨어 설정을 사용하도록 하자. 빠른 머신에서의 측정은 소프트웨어가 잘 수행된다고 믿게 할 수도 있다, 하지만 많은 사용자가 더 느린 프로세서와 더 적은 메모리의 컴퓨터를 사용할 것이다.


[편집] 일찍 측정하고 자주 측정하자

퍼포먼스 데이터는 일단 모아지면 프로그램의 모든 병목(bottleneck)을 바로 찾아낼 수 있는 종류의 것이 아니다. 프로그램의 퍼포먼스 기록을 유지하고 있다면 문제를 찾기가 더 쉬어진다. 측정 기록을 유지하면 애플리케이션의 퍼포먼스가 향상되었는지 하락했는지 확인하기가 쉽다. 만약 하락했다면, 제품이 발매되기 전에 문제를 해결할 방법을 찾아야 한다.

퍼포먼스의 측정을 정규적으로 해야 하는 또 다른 이유는 이러한 결과를 코드의 체크인(역자 주: 버젼 관리 시스템에 변경한 코드를 반영시키는 것)과 연관시켜 생각해 해 볼 수 있기 때문이다. 만약 퍼포먼스가 어떤 특정한 지점을 기준으로 하락한다면, 그 기간동안 체크인된 코드를 확인하여 이유를 밝혀낼 수 있다. 비슷하게, 성능이 향상되었다면 최근 체크인된 코드를 확인하여 좋은 프로그래밍 습관의 모델로 삼고 여러분의 팀에게 그와 유사한 테크닉을 사용하도록 장려할 수 있다.

부분적으로라도 작동하는 프로그램을 갖게되는대로 퍼포먼스 측정을 시작하여야 한다. 새로운 기능이 추가되면, 그 기능에 대한 측정을 포함시킬 수 있다. 자동화된 분석 루틴을 여러분의 프로그램에 바로 집어넣어 여러분의 팀원들이 결과를 즉시 확인하기 쉽게 할 수 있다. 이러한 정보가 즉시 제공된다면 코드를 체크인하기 전에 퍼포먼스 문제를 고치기가 쉽다.


[편집] 결과를 분석하기

데이터를 수집하는 것은 퍼포먼스 병목을 식별해내는데 가장 중요한 단계이다. 그러나 일단 데이터를 확보하면, 문제를 찾기위해 이것들을 사용하는 것 역시 중요하다. 퍼포먼스 데이터를 분석하는 것은 출력결과를 보고 바로 문제를 찾아내는 것처럼 쉬운 일이 아니다. 운이 좋다면 문제를 빨리 찾아낼 수 있겠지만, 어떤 문제들은 포착하기 어렵고 분석하는데 많은 집중을 요구한다.

결과를 분석하는데 도움이 되는 한가지 방법으로 결과를 그래프로 표시하는 것이다. 퍼포먼스 데이터를 시각화 하는 것은 스프레드쉬트나 다른 텍스트기반의 형태로 되있을때보다 다 빠르게 경향을 파악할 수 있게 해준다. 예를 들어, 특정한 빌드에 대한 작업을 완료하는데 걸리는 시간으로 그래프를 그려서 빌드와 빌드 사이에 퍼포먼스가 향상되었는지 하락했는지 확인할 수 있다.


[편집] 상위 단계(Higher-Level)의 알고리즘의 분석

(역자 주: 여기서 '상위 단계'라는 것은 호출단계에서 상위에 있다고 생각하면 될 것이다. 예를 들어 어떤 알고리즘을 구현한 A 라는 함수가 그 알고리즘 구현을 위해 또 다른 알고리즘을 구현한 B, C라는 함수를 호출하면 A는 B, C에 대한 상위 단계의 알고리즘이라 할 수 있다.)

퍼포먼스 데이터를 분석할때, 그 문제가 놓여있는 추상화 레벨에 대해 열린 사고를 갖고 있어야 한다. 여러분이 갖고 있는 데이터가 특정 함수안에서 많은 시간이 걸리는 것을 나타낸다고 가정하자. 그 함수 내부의 코드를 최적화시켜서 빠르게 동작하도록 할 수 있을 것이다. 하지만 그게 진짜 문제의 원인인 것일까? 프로그램을 다시 실행해 보자, 하지만 이번엔 그 함수의 호출을 샘플링해보자. 얼마나 많이 그 함수가 호출되었는지 확인해보자 그리고 거기에 어떤 특별한 패턴이 있는지 확인하자. 그 함수가 백만번 호출되었다면, 문제는 일단 그 함수를 호출하는 상위 단계 알고리즘에 있을 확률이 크다. 그 함수가 한번만 호출된 거라면, 그 함수의 본체에 문제가 있을 것이다.

Note: Shark는 프로그램을 분석하는데 사용할 수 있는 강력한 툴이다. Shark의 data mining 기능은 상위 단계 알고리즘에서의 문제를 찾아내는데 훌륭한 방법이다. Shark와 다른 Apple에서 제공하는 툴들에 대한 더 많은 정보는 Performance Tools에 나와 있다.

퍼포먼스 툴 그자체로는 데이터를 분석할때 여러분이 이해하고 고려해야할 제한사항이 있다. 예를 들면, 샘플링 프로그램은 애플리케이션이 많은 시간을 소모하는 지점을 가르켜줄 것이다, 그러나 여러분은 Shark나 다른 툴들이 너무 많은 결론을 내리기 전에 그것들이 어떻게 데이터를 모으는지에 대해서 이해하고 있어야 한다. 이 툴들은 모든 함수 호출을 추적하지 않는다. 대신 고정된 간격으로 샘플링한 결과에 기반해서 프로그램의 통계적인 분석을 제공한다. 이 툴들로 부터의 결과물은 하나의 가이드로 사용하라, 그러나 이 결과를 여러분이 기록한 다른 데이터와 서로 관련시켜 보는 것을 잊지말자.


[편집] 그밖의 분석 방법들

퍼포먼스 문제를 유발하는 진짜 원인에 대해 의심을 갖는다면, 그 문제의 원인에 대해서 가정하지 말라. 대신, 관련된 코드로부터 데이터를 모아서 분석을 뽑아내도록 하자. 다른 툴들을 이용해서 수집된 새로운 타입의 정보를 이용하도록 해보자. 다른 툴들이 문제에 대해 동일한 관점을 제공한다면 실제로 문제가 무엇인지 더욱 명확해진다.

프로그램을 분석하는데 쓰이는 추가적인 방법 몇가지가 다음에 나와있다:

  • 코드를 디버거로 확인하라. 디버거를 통해 코드를 하나하나 실행하여 속도를 떨어뜨리는 로직 에러를 찾을 수 있다.
  • 체크포인트를 코드에 추가하여 언제 코드가 실행되었는지에 관한 정보를 로그로 남긴다. 체크포인트를 사용하여 초기화 코드를 추적하는 예제가 필요하다면 Launch Time Performance Guidelines를 보기 바란다.
  • 문제에 대해서 다른 방법으로 코딩을 해보고 똑같은 문제가 발생하는지 확인해본다.