2015. 3. 21. 13:21

▶ Non Functional Requirement

  • "성능 측정(performance estimation) 혹은 프로파일링(profiling)"은 서비스 혹은 어플리케이션이 '실용적인지 여부'를 판단하는 기준이 된다. 아무로 기능 면에서 좋은 소프트웨어라고 할지라도 필요한 시스템 사양(specification)이 과다하다면 써먹을 수 없다. 혹은 실행 속도가 느리다면 환영 받지 못할 것이다. 
  • 성능 측정을 모든 기능을 개발한 후에 수행하게 되면 2가지 문제가 발생할 수 있다.

    • 성능 문제를 해결하는데 필요한 시간이 부족해서, 최초에 수립한 일정을 지키기 못하거나,
    • 문제를 해결할 수 없어서 실패한 프로젝트가 되고 마는 것이다. 

  • 따라서, 성능 측정 준비는 기능 설계를 진행하면서 함께 준비하고, 기능 구현 과정 중에 몇 차례에 걸쳐 성능을 측정함으로써 중간 과정에서 취약점(weak point)가 어느 부분인지, 어떤 기능으로 인해 급작스럽게 성능이 하락하는지 검사하는 것이 현명하다.


▶ 성능에 대한 핵심 개념과 사전 지식

  • "성능(performance)"를 좌우하는 2가지 요소는 "공간"과 "시간"이다. 프로그래밍에서 "공간"이라는 개념은 프로그램이 소모하는 메모리(작게는 메인 메모리, 크게 보면 디스크와 네트워크)이며, "시간" 개념은 얼마나 CPU 자원을 오래 사용하는가 이다.
  • 따라서, 소프트웨어 성능은 "메모리 사용량(공간")과 "CPU 사용량(시간)" 등 2가지 측정 지표(metrics)가 존재한다.
  • 개발자가 작성하는 "코드"가 얼마나 많은 "공간"과 "시간"을 필요로 하는가에 대해서는 "알고리즘"과 "자료구조" 지식을 통해 이론적으로 예측할 수는 있으나, 다음과 같은 조건에 따라 "같은 문제"라고 하더라도 실질적으로 상당한 차이가 발생한다.

    • 어떤 언어로 개발하는가?
      : 하위 수준 언어일수록 빠르고, 컴파일러(compiler) 방식의 언어가 인터프리터(interpreter) 언어보다 빠르다.
    • 개발자가 구체적으로 어떤 방법으로 구현했는가?
      : 구현 방식은 API 호출, 라이브러리 활용, 직접 구현 등 방식을 한정할 수 없다.
    • 어떤 컴파일러를 사용해서 컴파일 했는다?
      : 컴파일러에 따라서 기계어를 생성하는 로직이 다르다. 컴파일러 자체에 성능 최적화 기능이 포함되어 있는 경우도 많다.
    • 어떤 머신(machine)에서 실행하는가?
      : 게임과 같은 실수형 계산 및 그래픽 처리를 많이 하는 프로그램은 GPU 성능에 의존하는 경향이 있다.

  • 위와 같은 이유로 성능은 정적인 "코드"만으로 예측할 수 없기 때문에 실행하면서 측정하는 것이 가장 '정확하다'! 프로그램을 실행하면서 성능을 측정하고, 분석하는 행위를 "프로파일링(profiling)"이라고 하며, 프로그램의 성능을 측정하는 도구를 "프로파일러(profiler)"라고 한다. 프로그램의 성능을 측정한 후, 개선하는 작업을 "최적화(optimizing)"라고 한다.

▶ 성능에 대한 정의와 이해 at slideshare.net



▶ DIY Profiling at slideshare.net



▶ 성능 측정 유형

  • CPU 측정
    • Times/ Calls 
      : 함수 혹은 메소드가 얼마나 오랜동안 CPU를 사용하는가 혹은 얼마나 많이 호출되는가를 측정하는 것이다.
    • Sampling
      : 아주 짧은 시간동안 호출되는 함수의 수행 시간을 측정하면 해당 함수의 수행 시간을 왜곡하게 된다. (함수가 수행되는 시간보다 더 많은 시간을 측정하는데 소모하거나, 수행시간이 느린 것으로 잘못 판단할 수 있다.) 혹은 초당 10만번 이상 호출되는 함수들을 측정하고자 할 경우 샘플링(sampling) 기법을 사용한다. 샘플링 기법은 프로그램 수행 중 일정 주기 마다 현재 실행 상태 (실행 중인 위치 정보)를 기록하는 기법이다.

  • 메모리 측정
    • Usage
      : 메모리를 얼마나 "자주" 할당 및 해제하는가를 측정한다. 메모리의 할당 및 해제는 비용이 많이 드는 '비싼' 작업이다.
    • Allocation
      : 메모리를 얼마나 "많이" 할당하느냐를 측정한다. 메모리를 과다하게 할당할 경우 "메모리 부족(Out of memory)" 오류를 발생 시키거나, 스왑(swap) 메모리를 사용하게 되어 급격한 성능 저하를 유발하게 된다.


▶ 스스로 하는 성능 측정 (DIY Java Profiling)

  • 앞서 Romon Elizrov 의 자료에서 제시하는 프로파일링 기법들은 다음과 같다.

  • 자바 코드 내에서 성능 측정 코드 삽입 (Just code it in Java)
    : 개발자가 직접 성능을 측정하는 코드를 자바 코드 내에 삽입하는 방법이다.

  • 자바 가상 머신의 기능 활용 (Know your JVM features)
    : JVM 에서는 메모리 사용 상태 및 스레드(thread)의 실행 상태를 확인할 수 있는 기능(옵션)이 제공된다.

  • 바이트 코드 조작 활용 (Use byte code manipulation)
    : 자신이 작성한 프로그램이 아니거가 - 소스 코드가 없다거나 -, 이미 완성된 프로그램의 성능을 측정하고자 하는 경우에 사용되는 기법이다. 컴파일된 클래스 파일 (확장자가 .class)을 조작해서 성능 로그를 생성하는 코드를 삽입하는 기법이며, 자칫 JVM의 비정상 종료(crush)를 유발할 수 있기 때문에 조심스럽게 적용해야 한다.

▶ 프로파일링 클래스 구현

  • docar.util.profile.Profiler
    : 성능 측정 정보를 기록하는 싱글턴(singleton) 패턴의 클래스. addCallEvent() 메소드를 호출할 때마다, 특정 기능의 수행 횟수 및 누적 수행 시간을 기록한다.
  • docar.util.profile.StopWatch
    : 특정 기능(메소드, 클래스)의 수행 시간을 측정하는 클래스.
  • docar.uti.Report
    : 성능 측정 결과를 출력하는 클래스. "기능 명칭 및 호출 횟수", "총 수행 시간", "평균 수행 시간" 등을 출력한다.

  • 사용 방법 예시
    // 메소드 시작 부분에서 "기능 명칭"을 인자로 스톱워치 생성
    StopWatch stopWatch = new StopWatch("DuplicationFinder.main");
    
    // 메소드 종료 부분에서 수행 시간 기록(누적)
    Profiler.getInstance().addCallEvent(stopWatch.stop());
    
    // 수행 시간 측정 결과 출력
    Report.report(System.out);
    
    
  • 측정 결과 예시



Posted by 곽중선