이번장에서 다룰 내용
- 일반성을 최소화해서 커플링 최소화 하기
- 불변속성 측면에서 최적화 바라보기
- 최적화를 통해 취약성 관리하기
(성능)최적화: 코드 처리량을 늘리거나 처리 시간을 줄이기
일반화: 코드가 더 일반적인 매개변수를 통해 더 많은 기능을 포함하도록 하는 것
요리사에게 칼을 건네야 한다면 군용칼이 좋을까 과일 칼이 좋을까?
일반화를 수용하는 설계는 일반화 하여 얻는 이점보다 부담스러울 수 있다. 일반화 해야하는 것은 컨텍스트 뿐이다.
단순성 추구
인간의 인지능력은 제한적이므로 단순함이 필수다.
결합된 컴포넌트와 기능을 이해하기 위해 추적해야 하는 일은 인지능력이 꽤 많이 요구된다.
주로 아래 두가지 습관 때문에 단순함에서 멀어진다.
1. 일반화된 기능 제공
일반화된 기능을 제공하면 그 기능의 용도가 증가하고 더 많은 것이 결합되고, 기능을 이해하기에 더 많은 에너지가 소모된다
2. 불변속성을 활용하는 최적화
총 량을 매번 조회하는 로직을 -> total 필드를 만들어 활용
일반화나 최적화를 진행할 때는 그 근거가 이어야 한다.
단순함을 희생할 때는 부작용을 최소화 하기 위한 예방 조치가 있어야 한다.
일반화의 시기와 방법
구현의 최소화로 일반화 지양하기
"하지 않는 일의 양을 최대화 하라"
- 켄트 벡
- 무언가를 만들기 전에 먼저 컨텍스트 (= 구현하려는 동작의 범위)를 이해하기
- 소프트웨어가 발전함에 따라 요구사항이 변경되는 경향이 있기 때문에 불필요한 일반화를 구현해도 무효화 되기 쉽다.
안정성이 유사한 것 통합하기
- 새로운 것과 오래된 것을 바로 통합하지 않기 - 통합 대상이 비슷한 안정석에 도달할 때까지 기다리기
불필요한 일반화 제거
- 정기적으로 모니터링 후 불필요한 일반화 제거하기
- 메서드 전문화, 삭제 후 컴파일하기 패턴 사용
- 함수에 전달된 런타임 인자들을 모니터링 하면 불필요한 일반화를 효과적으로 찾을 수 있다.
최적화 시기와 방법
최적화 역시 높은 인지부하를 유발하므로 최적화를 하기전 진짜 필요한가 타당한 근거가 필요하다.
자동 성능 테스트(벤치마크 테스트, 부하 테스트, 성능 승인 테스트) 를 이용해 테스트가 실패할 때 최석화를 적용시키는 것이 좋다.
최적화 전 리팩터링
- 최적화 전 적절하게 리팩터링 되었는지 확인한다
- 최적화는 불변속성에 의존하기 때문에 잘 분해된 코드를 최적화 하는 것이 쉽다.
컴파일러에게 맡기기
- 과도한 기교는 컴파일러가 우리가 원하는 것을 인식할 수 없게 하여, 코드를 더 느리게 실행 할 수 있다.
- 컴파일러를 활용하자!
- 컴파일러가 개선된다는 것은 우리가 관용적 코드를 작성하면 시간이 지남에 따라 코드가 자동으로 빨라진다는 것을 의미한다.
- 복잡한 코드 관리나 특이한 패턴은 지양하자.
제약 이론에 따른 최적화
- 코드 리팩터링 후 테스트가 여전히 만족스럽지 못하다면 최적화 진행 필요
- 스레드나 프로세스, 서비스의 협업을 통해 동시 시스템에서 작업하는 경우 제약이론(theory of constraints)이 적용된다.
순차적으로 연결된 워크스테이션으로 구성된 시스템에서는 어떤 작업이 수행되면 그 결과를 버퍼를 통해 다른 작업자게에 전달되는데, 입력에서 출력까지의 흐름에서 한 명의 병목 지점 작업자가 있다면 병목 지점 작업자 입구의 버퍼에 작업이 쌓이기 시작한다.
이 병목 지점의 작업자를 최적화 해야 시스템 성능이 좋아진다.
- 병목 현상을 최적화 하면 새로운 병목 현상 발생 -> 이전 병목 현상의 증가된 처리량을 따라가지 못하거나 출력 빠르게 생성하지 못함
- 리소스 풀링을 이용해 위와 같은 문제 해결 가능
리소스 풀링
모든 리소스를 필요할 때 사용할 수 있는 공용 풀에 배치하는 것을 의미한다. 가능한 최대 용량이 병목 지점에 제공된다. 로드밸런서를 통해 외부 서비스 수준이나 애플리케이션 내 스레드 풀링을 통해 이 접근 방식을 구현할 수 있다.
측정 지표를 사용한 최적화
- 리소스 풀링으로 시스템을 최적화 한 후에도 성능 요구사항을 충족하지 못하면 병목 지점 내에서 최적화해야한다.
- 코드에서 핫스팟(Hot spot: 스레드가 대부분의 시간을 보내는 지점)을 식별해야 한다.
- 프로파일링 도구를 활용해 전체 시간의 80%를 소비하는 코드 구간을 찾아내야 한다.
* 프로파일링: 메서드에서 얼마나 많은 누적시간을 소비했는지 추척하는것
좋은 알고리즘과 데이터 구조 선택하기
핫스팟을 찾은 후 최적화 방법을 생각해야 한다.
동등한 인터페이스를 가진 다른 데이터 구조로 바꾸기
- 새로운 데이터 구조를 위해 도메인 관련 코드를 변경할 필요가 없기 때문에 안전하다.
캐시 사용하기
캐시: 계산을 여러 번 수행하는 대신 한 번 수행하고 결과를 저장한 후 재사용 하는 것
- 캐시는 멱등 불변속성에 적용할 때 가장 안전하다. => 동일한 인자로 호출하면 항상 동일한 결과가 제공 되므로 외부에서 캐시를 수행 할 수 있다.
- 멱등성이 일시적인 경우 캐시는 취약해진다. 예) 상품 가격 캐싱중인데 변경될 때
- 멱등성이 없어도 캐시를 수행할 수 있으나 내부적으로만 사용해야 한다.
최적화된 코드 분리하기
알고리즘, 동시성 및 캐시로 성능 테스트를 충분히 만족 시킬 수 있지만,
불춘분한 경우 마이크로 최적화 라고 하는 성능튜닝을 활용할 수 있다. => 주로 복잡하고 어려운,, 쉽게 변경 할 수 없는 작업이다
잠금 영역 최소화를 위한 메서드와 클래스 사용
- 일반적으로 까다롭게 튜닝된 함수를 이해하지 않고서는 변경할 수 없다.
- 따라서 튜닝된 코드를 격리하여 (작업)코드의 양을 최소화 시킨다.
- 튜닝된 코드의 경우 그것을 추출 할 때보다 더 잘 이해할 수 있는 사람은 없기 때문에 네이밍이나 문서화가 잘 만들어져야 한다.
향 후 개발자들에게 알리기 위한 패키지 사용
- 미래의 개발자들에게 튜닝된 코드니 자세히 다루지 말라는 사실 전달하여 이익을 얻어야 한다.
- 튜닝된 코드를 위한 전용 패키지를 사용하는 것이 좋다
> 임포트해서 사용할 패키지가 보이지 않게 할 수 있다.
> 튜닝된 모든 코드를 함께 모으는 것은 그 영역에 대한 다른 품질 요구 사항이 있다는 것을 나타낸다.
튜닝된 영역 = 소수의 개발자들이 예외적으로 잘 이해하는 코드를 위해 각별히 관리된 성스러운 곳이 되어야 한다.
'스터디 > Five Lines of Code' 카테고리의 다른 글
Five Lines of Code 13장. 나쁜 코드를 식별 가능하게 만들기 (1) | 2023.11.21 |
---|---|
Five Lines of Code 11장. 코드 구조 따르기 (0) | 2023.11.16 |
Five Lines of Code 10장. 코드 추가에 대한 두려움 떨쳐내기 (0) | 2023.11.07 |
Five Lines of Code 9장. 코드 삭제의 미학 (0) | 2023.11.05 |
7장 컴파일러와의 협업 (1) | 2023.11.04 |