(맹목적인 어리석음을 포함해) 그 어떤 핑계보다 효율성이라는 이름 아래 행해진 컴퓨터 죄악이 더 많다(심지어 효율을 높이지도 못하면서).
- 윌리엄 울프
(전체의 97% 정도인) 자그마한 효율성은 모두 잊자. 섣부른 최적화가 만악의 근원이다.
도널드 크루스
최적화를 할 때는 다음 두 규칙을 따르라.
첫 번째, 하지 마라.
두 번째, (전문가 한정) 아직 하지 마라.
다시 말해, 완전히 명백하고 최적화되지 않은 해법을 찾을 때까지는 하지 마라.
- M. A. 잭슨
💡 빠른 프로그램보다는 좋은 프로그램을 작성하라
- 성능 때문에 견고한 구조를 희생하지 마라.
- 좋은 프로그램이지만 성능이 나오지 않는 것이라면 아키텍처 자체가 최적화의 길을 안내해줄 것이다.
- 좋은 프로그램은 정보 은닉 원칙을 따르므로 개별 구성요소의 내부를 독립적으로 설계할 수 있다.
- 따라서 시스템의 나머지에 영향을 주지 않으면서, 각 요소를 다시 설계할 수 있다.
- 구현상의 문제는 나중에 최적화가 가능하지만, 아키텍처의 결함이 성능을 제한한다면 시스템 전체를 다시 작성해야할 수도 있다.
📌 소프트웨어 설계 시 고려사항
1️⃣ 성능을 제한하는 설계를 피하라.
- 컴포넌트끼리, 혹은 외부 시스템과의 소통 방식은 완성 후 변경하기 가장 어려운 설계 요소다.
- API, Network Protocol, 영구 저장용 데이터 포맷 등
- 이런 설계 요소들은 완성 후 변경이 어렵거나 불가능할 수 있으며, 시스템 성능을 심각하게 제한할 수 있다.
2️⃣ API를 설계할 때 성능에 주는 영향을 고려하라.
- public 타입을 가변으로 만들면, 불필요한 방어적 복사를 수없이 유발할 수 있다. (Item 50)
- java.awt.Component 클래스의 getSize가 반환하는 Dimension 인스턴스는 가변이라 getSize를 호출하는 모든 곳에서 방어적 복사를 수행해야 한다.
- Composition으로 해결 가능하면서 상속 방식으로 설계한 public은 상위 클래스에 영원히 종속되며, 성능과 제약까지도 물려받는다. (Item 18)
- Interface type이 있는데 구현체 타입을 사용하면, 특정 구현체에 종속되며 더 나은 구현체가 나와도 사용할 수 없게 된다. (Item 64)
3️⃣ 성능을 위해 API를 왜곡하는 건 매우 안 좋은 생각이다.
- 잘 설계된 API는 성능도 좋은 게 보통이다.
- API를 왜곡하도록 만든 성능 문제는 플랫폼이나 아랫단 소프트웨어 개선으로 사라질 수도 있다.
- 왜곡된 API로 인한 고통은 사라지지 않는다.
4️⃣ 각각의 최적화 시도 전후로 성능을 측정하라
- 신중하게 설계하여 깨끗하고 명확하고 멋진 구조를 갖춘 상태에서 성능에 만족하지 못한 경우에만 최적화를 고려해볼 차례가 된다.
- 프로파일링 도구(profiling tool)는 최적화 노력을 집중해야 할 곳을 알려준다.
- 개별 메서드 소비 시간과 호출 횟수 같은 런타임 정보를 제공한다.
- 시스템 규모가 커질 수록 찾기가 힘들기 때문에 프로파일러가 중요해진다.
- jmh는 프로파일러는 아니지만 Java 코드 상세 성능을 알기 쉽게 보여주는 마이크로 벤치마킹 프레임 워크다.
- Java는 성능 모델이 덜 정교하여 측정의 중요성이 더 크다.
- 프로그래머가 작성한 코드와 CPU에서 수행하는 명령 사이의 '추상화 격차'가 크다.
- 이로 인해 최적화로 인한 성능 변화를 일정하게 예측하기가 더 어렵다.
- 여러 플랫폼에서 구동한다면 구현과 하드웨어 플랫폼 사이에서 성능을 타협해야 할 수도 있다.