2015.03.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 善 곽중선
2015.03.20 03:15

▶ 로깅(logging) 설정 이전에 "이클립스 Maven 환경 설정"을 선행해야 한다.

▶ 로깅(logging 설정

  • pom.xml 파일에 slf4j 와 logback 의존성(dependency)를 추가한다.

    <dependencies>
  	<dependency>
  		<groupId>org.slf4j</groupId>
  		<artifactId>slf4j-api</artifactId>
  		<version>1.7.10</version>
  		<scope>runtime</scope>
  	</dependency>
  	<dependency>
  		<groupId>ch.qos.logback</groupId>
  		<artifactId>logback-classic</artifactId>
  		<version>1.1.2</version>
  		<exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
            </exclusion>
        </exclusions>
  	</dependency>
  	<dependency>
	    <groupId>org.slf4j</groupId>
	    <artifactId>jcl-over-slf4j</artifactId>
	    <version>1.7.7</version>
	</dependency>
  </dependencies>
  • logback 설정파일(logback.xml)을 생성한다.
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- encoders are assigned the type
             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <logger name="docar" level="DEBUG"></logger>
    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>
  • log 출력 테스트 코드를 작성한다. 

▶ Maven 설정 및 로그 설정 프로젝트 백업

DocAr_MavenAndLogging.zip


▶ 참고 링크 : SLF4J 로깅 처리



Posted by 善 곽중선
2015.03.20 02:54
▶ 메이븐 기본 이해

메이븐(Maven)에 대한 기본 개념을 이해하기 위해서는 다음 슬라이드를 참조하기 바란다.


▶ 이클립스 메이븐 환경 설정


만일 이클립스에 메이븐이 설치되어 있지 않다면 다음 절차들을 따라하기 바란다.


  • https://www.eclipse.org/m2e/m2e-downloads.html 링크를 웹 브라우저에서 연다. 테이블 형태로 출력 m2e 플러그인 목록을 찾을 수 있을 것이다.
  • 가장 최신 버전을 선택하고, "p2 url" 컬럼의 URL 경로를 클립보드에 복사해둔다.

  • 이클립스로 돌아간 후, Help 메뉴를 선택하고 "Install New Software" 항목을 클릭한다.



  • "Add" 버튼을 클릭한 후, 아무 명칭이나 입력한다. "Location" 항목에 클립보드에 복사해둔 URL을 붙여넣는다.

  • 이전 단계에서 선택한 플러그인이 목록에 출력될  것이며, 앞에 표시된 체크박스를 체크한 후, "Next" 버튼을 클릭한다.

  • 설치가 진행되고, 설치가 완료되면 이클립스를 재시작한다.

  • 프로젝트를 우클릭하고, 설정(configure) 옵션 중에서 "convert to maven project"를 선택한다.

  • 새로운 POM을 생성하기 위한 "new wizard"가 나타날 것이다. 적절한 그룹 ID와 버전을 입력한다. "Packing as jar"를 선택한 후, "Finish" 버튼을 클릭한다.

  • Maven 프로젝트가 성공적으로 설치되었다. 이제 프로젝트가 성공적으로 Maven 프로젝트로 변환된 것이며, pom.xml 파일을 수정해야 한다.


Posted by 善 곽중선