Posts [이펙티브자바] 아이템67-최적화는 신중히 하라
Post
Cancel

[이펙티브자바] 아이템67-최적화는 신중히 하라

핵심 정리: 빠른 프로그램을 작성하려 안달하지 말자. 좋은 프로그램을 작성하다 보면 성능은 따라오게 마련이다. 하지만 시스템을 설계할 때, 특히 API, 네트워크 프로토콜, 영구 저장용 데이터 포맷을 설계할 때는 성능을 염두에 두어야 한다. 시스템 구현을 완료했다면 이제 성능을 측정해보자. 충분히 빠르면 그것으로 끝이다. 그렇지 않다면 프로파일러를 사용해 문제의 원인이 되는 지점을 찾아 최적화를 수행하라. 가장 먼저 어떤 알고리즘을 사용했는지를 살펴보자. 알고리즘을 잘 못 골랐다면 다른 저수준 최적화는 아무리 해봐야 소용이 없다. 만족할 때까지 이 과정을 반복하고, 모든 변경 후엔 성능을 측정하라.

모든 사람이 마음 깊이 새겨야 할 최적화 격언 세 개

  • (맹목적인 어리석음을 포함해) 그 어떤 핑계보다 효율성이라는 이름 아래 행해진 컴퓨팅 죄악이 더 많다. (심지어 효율을 높이지도 못하면서) - 윌리엄 울프
  • (전체의 97% 정도인) 자그마한 효율성은 모두 잊자. 섣부른 최적화가 만악의 근원이다. - 도널드 크누스
  • 최적화를 할 때는 다음 두 규칙을 따르라. - M.A. 잭슨
    • 첫 번째, 하지 마라.
    • 두 번째, (전문가 한정) 아직 하지 마라. 다시 말해, 완전히 명백하고 최적화되지 않은 해법을 찾을 때까지는 하지 마라.

최적화는 좋은 결과보단 해로운 결과로 이어지기 쉽고, 섣불리 진행하면 특히 더 그렇다. 빠르지도 않고 제대로 동작하지도 않으면서 수정하긴 어려운 소프트웨어를 탄생시키는 것이다.

성능 때문에 견고한 구조를 희생하지 말자.

  • 빠른 프로그램보단 좋은 프로그램을 작성하자.
  • 좋은 프로그램이지만 원하는 성능이 나오지 않는다면 그 아키텍처 자체가 최적화할 수 있는 길을 안내해줄 것이다.
  • 좋은 프로그램은 정보 은닉 원천을 따르므로 개별 구성요소의 내부를 독립적으로 설계할 수 있다.
    • 시스템의 나머지에 영향을 주지 않고도 각 요소를 다시 설계할 수 있다.(아이템15)
  • 완성된 설계의 기본틀을 변경하려다 보면 유지보수하거나 개선하기 어려운 꼬인 구조의 시스템이 만들어지기 쉽기 때문이다.
    • 따라서 설계단계에서 성능을 반드시 염두에 두어야 한다.

성능을 제한하는 설계는 피하자.

  • 완성 후 변경하기 가장 어려운 설계 요소가 컴포넌트 간 혹은 외부 시스템과의 소통 방식이다. API, 네트워크 프로토콜, 영구 저장용 데이터 포맷 등을 대표적이다. 이런 설계 요소들은 완성 후 변경하기 어렵거나 불가능할 수 있으며, 동시에 시스템 성능을 심각하게 제한할 수 있다.

API를 설계할 때 성능에 주는 영향을 고려하자.

1) public 타입을 가변으로 만들어 내부 데이터를 변경할 수 있다면, 불필요한 방어적 복사를 유발할 수 있다.(아이템50)

대표적인 사례로 java.awt.Component 클래스의 getSize()를 예로 들 수 있다.

스크린샷 2022-11-29 오후 10 58 16

스크린샷 2022-11-29 오후 11 00 21

  • 이 메서드는 내부적으로 size()메서드를 호출하고 새로운 Dimension 인스턴스를 생성하여 반환한다.
  • Dimension은 가변으로 설계되었으니, getSize()를 호출하는 모든 곳에서 Dimension 인스턴스를 새로 생성(방어적 복사) 해야한다.
  • 만약 이 메서드가 수백만 번 호출된다면 어떨까? 방어적으로 복사하느라 수백만 개의 Dimension 인스턴스를 생성해야 할 것이다.
    • 하지만 Dimension이 불변으로 설계되었다면, getSize()가 수백만 번 호출되어도 하나의 Dimension 인스턴스가 필요할 뿐이다.
  • 자바2에선 성능 문제를 해결하고자 getSize()getWidth()getHeight()로 메서드를 나누는 방법을 활용했다.
  • 이처럼 API 설계가 성능에 주는 영향을 고려하지 않으면 API 설계 결정의 폐해를 클라이언트가 감내해야 한다.

2) 컴포지션으로 해결할 수 있음에도 상속으로 설계한 public 클래스

컴포지션으로 해결할 수 있음에도 상속 방식으로 설계한 public 클래스는 상위 클래스에 영원히 종속되며 그 성능제약까지도 물려받게 된다(아이템18)

3) 인터페이스가 있는데 구현 타입을 사용하는 사례

특정 구현체에 종속되게 하여, 나중에 더 빠른 구현체가 나오더라도 이용하지 못하게 된다(아이템64)

성능을 위해 API를 왜곡하지 말자.

왜곡된 API 로 지금 당장의 성능 이슈는 해결될지 몰라도 그 API를 지원하는데 따르는 고통은 영원히 계속될 것이다.

최적화를 고려하고 있다면..

  • 신중하게 설계하여 깨끗하고 명확하고 멋진 구조를 갖춘 프로그램을 완성한 후에, 성능에 만족하지 못한다면 최적화를 고려해볼 차례가 된다.
  • 대부분 프로그램에서 시간을 잡아먹는 부분을 추측하기 어렵기 때문에 최적화 노력을 어디에 기울여야 하는지 시간 낭비를 할 때가 많다.
  • 이때 프로파일링 도구가 어디에 집중해야 할지 찾는데 도움을 준다.
  • 대부분의 성능 문제는 비효율적인 알고리즘을 교체하는 것만으로도 개선된다. 시스템이 거대해질수록 이러한 부분을 찾기 어려운데, 이때 프로파일러가 더 중요해진다.
  • 마이크로 벤치마킹 프레임워크인 jmh도 자바 코드의 상세한 성능을 알기 쉽게 보여주는 도구니 참고하면 좋다.
This post is licensed under CC BY 4.0 by the author.

[이펙티브자바] 아이템59-라이브러리를 익히고 사용하라

[이펙티브자바] 아이템69-예외는 진짜 예외 상황에만 사용하라 신중히 하라