빠른 프로그램보다는 좋은 프로그램을 작성하라.
: 아키텍처 수준에서의 성능 최적화를 위해 가독성이 좋고, 재사용성이 높고 수정이 용이한 코드를 작성하라.
- 프로그램의 아키텍처가 잘 설계되어 있으면, 개별 구성 요소의 성능 문제는 대체로 구성 요소 내부의 개별적인 최적화를 통해 해결할 수 있다.
- 즉, 정보 은닉 또는 캡슐화 원칙을 따르므로 각 요소를 독립적으로 설계하고 개선할 수 있게 만든다.
- 반면, 성능 문제가 아키텍처 수준에서 발생하는 경우, 일반적으로 전체 시스템을 변경해야 하며 이는 대규모의 재설계와 많은 리소스가 필요하다.
- 결론적으로, 좋은 프로그램을 작성하는 것이 중요하며, 이를 통해 개별 구성 요소를 독립적으로 최적화할 수 있는 유연성을 얻을 수 있다.
- 또한, 아키텍처 수준에서의 성능 최적화가 중요하며, 이는 프로그램 설계 초기 단계부터 고려되어야 한다.
성능을 제한하는 설계를 피하라.
- 성능에 큰 영향을 미치는 설계 요소들은 신중하게 결정해야 한다.
- 특히 컴포넌트 간의 소통 방식이나 외부 시스템과의 연결 방식 같은 핵심적인 설계 요소들은 한 번 결정되면 추후에 변경하기 어렵기 때문에 초기 설계 단계에서 성능을 고려하여 결정해야 한다.
- API, 네트워크 프로토콜, 영구 저장용 데이터 포맷 등의 설계 요소들은 시스템의 성능에 큰 영향을 미치며, 변경하기 어렵기 때문에 초기 설계 단계에서 신중히 결정해야 한다.
API를 설계할 때 성능에 주는 영향을 고려하라.
- public 타입을 가변으로 만들면 불필요한 방어적 복사를 유발할 수 있다.(item 50)
- 메모리 사용이 증가하고, 프로그램 성능이 저하될 수 있다.
- 따라서 가능하면 불변 객체를 사용하여 이러한 문제를 방지하고, 필요한 경우에만 가변 객체를 사용하자.
- 컴포지션으로 해결할 수 있는 경우에도 상속을 이용하면 상위 클래스에 영원히 종속되고, 성능에 제약까지 물려받는다.(item 18)
- 상속은 코드의 재사용성을 높이지만, 상위 클래스의 특성을 물려받아 변경이 어렵다.
- 반면, 컴포지션을 이용하면 필요한 부분만을 선택하여 클래스를 구성할 수 있어, 유연성이 높아지고 성능 최적화에 유리하다.
- 굳이 인터페이스가 아닌 구현 타입을 사용하면, 차후 개선된 구현체를 사용하기 어려워진다.
- 클래스보다 인터페이스를 이용하면 코드의 유연성을 높이고, 성능 최적화에 좋다.
예시 1 - java.awt.Component 클래스의 getSize 메소드
public abstract class Component implements ImageObserver, MenuContainer,
Serializable
{
public Dimension getSize() {
return size();
}
@Deprecated
public Dimension size() {
return new Dimension(width, height); // 방어적 복사 수행
}
}
- getSize 메소드는 Dimension 클래스의 새 인스턴스를 반환한다. Dimension 클래스는 가변으로 설계되어, getSize를 호출할 때마다 새로운 Dimension 인스턴스가 생성된다. 이는 성능 저하를 유발한다.
public abstract class Component implements ImageObserver, MenuContainer, Serializable {
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
- 따라서, 자바 2에서는 getWidth와 getHeigth 메소드를 추가하여 성능 문제를 개선하려 하였다.
- 이 두 메소드는 각각 너비와 높이의 기본 타입을 반환하므로, getSize를 호출할 때마다 새로운 Dimension 객체를 생성하는 것보다 훨씬 효율적이다.
- 그러나, 기존의 클라이언트 코드는 여전히 getSize 메소드를 사용하고 있다.
- 기존 클라이언트 코드를 변경하지 않고서는 새로운 getSize와 getHeigth 메소드를 사용할 수 없기 때문이다.
성능을 위해 API를 왜곡하지 마라.
- API 왜곡은 장기적으로 API의 사용성을 저하시키고 유지보수를 어렵게 만든다.
- 기술의 발전으로 성능 문제는 자연스럽게 해결될 수 있지만, 일단 왜곡된 API는 수정이 어렵고 관리의 어려움이 지속될 수 있다.
각각의 최적화 시도 전후로 성능을 측정하라.
: 프로파일링 도구를 사용하자.
- 프로파일링 도구는 성능 최적화에서 굉장히 중요한 역할을 한다.
- 코드에서 가장 많은 시간을 소비하거나 가장 빈번하게 호출되는 부분을 식별하는 데 도움을 줘, 개발자는 성능 최적화 노력을 어디에 집중해야 할지 결정할 수 있다.
- 또한, 개별 메소드의 소비 시간과 호출 횟수 같은 런타임 정보를 제공한다.
- 이정보를 통해
- 개발자는 성능 문제가 있는 지점을 쉽게 찾을 수 있다.
- 알고리즘이 적절하게 선택되었는지 확인할 수 있다.
- 이정보를 통해
- JMH(Java Microbenchmark Harness)는 프로파일링 도구가 아니다.
- 그러나 자바 코드의 성능을 상세하게 측정하고 분석하기 위한 도구로, 마이크로 벤치마킹 프레임워크다.
- 이를 사용하면 코드의 특정 부분이나 작은 단위의 성능을 측정하고, 여러 구현 간의 성능을 비교할 수 있다.
- 자바의 성능 모델은 C/C++보다 덜 정교하다.
- 이는 자바의 고수준 추상화 때문으로, 프로그래머가 작성하는 코드와 CPU에서 수행하는 명령 사이에 추상화 격차가 있다.
- 이로 인해 최적화의 효과를 일정하게 예측하기 어렵다.
- 이런 문제를 해결하기 위해서는 프로파일링 도구와 같은 도구를 사용하여 실제 실행 시간을 측정하고 분석하는 것이 필요하다.
'Language > Java' 카테고리의 다른 글
[effective java] 아이템 69. 예외는 진짜 예외 상황에만 사용하라. (0) | 2023.07.31 |
---|---|
[effective java] 아이템 68. 일반적으로 통용되는 명명 규칙을 따르라. (0) | 2023.07.31 |
[effective java] 아이템 66. 네이티브 메서드는 신중히 사용하라. (0) | 2023.07.27 |
[effective java] 아이템 65. 리플렉션보다는 인터페이스를 사용하라. (0) | 2023.07.27 |
[effective java] 아이템 64. 객체는 인터페이스를 사용해 참조하라. (0) | 2023.07.27 |