본문으로 건너뛰기

7장 함수형 최적화

이 장의 내용
  • 함수형 코드가 성능 기준을 만족하는지 판별
  • 자바스크립트 함수 실행의 내부 원리
  • 함수 콘텍스트와 재귀 중첩의 함의
  • 느긋한 평가로 함수 계산을 최적화
  • 메모화로 프로그램을 빠르게 함
  • 꼬리 재귀 함수로 재귀 호출을 풂
도널드 커누스, 1974년 논문에서
자그마한 효율은 그냥 잊으세요.
대략 97%의 경우 어설픈 최적화가 모든 걸 망쳐놓는 원인이 됩니다.
하지만 나머지 결정적 3%는 최적화할 기회를 절대로 놓쳐서는 안 됩니다.

함수형 프로그래밍은 코드 주위에 추상화 계층을 제공해서 높은 수준의 유창함과 선언형을 실현합니다. 이보다 더 단순할까 싶은 문제도 내부적으로 커링, 재귀, 모나드 래핑 등을 조합해 해결하는 과정을 보면서 '그런데, 함수형 코드가 명령형 코드만큼 성능이 나올까?' 하는 의구심이 생긴 독자도 있을 겁니다.

컴퓨터는 엄청나게 빨라젔고 컴파일러 기술도 비약적으로 발전했기 때문에 지금은 신속 정확하게 코드를 실행할 수 있습니다. FP는 사람들이 의심하는 것처럼 명령형 코드보다 성능이 나쁘지 않습니다.

함수형 프로그래밍은 개별 함수의 평가 속도를 올리기보다는 주로 함수 중복 호출을 피해서 코드가 정말 필요할 때까지 평가를 지연시키는 전략을 구사합니다. 그래서 애플리케이션이 전체적으로 빨라집니다. 순수 함수형 언어는 플랫폼 자체로 이러한 최적화를 수행하므로 개발자가 바로 꺼내 쓸 수 있지만, 그렇지 못한 언어(자바스크립트)에서는 커스텀 코드나 함수형 라이브러리를 동원해 최적화 도구를 수동 장착해야 합니다.

7.1 함수 실행의 내부 작동 원리

FP에서는 항상 함수를 평가하며 움직입니다. 따라서 성능/최적화를 공부하기 전에 함수를 호출하면 무슨 일이 벌어지는지 알아야 합니다. 자바스크립트에서는 함수를 호출할때마다 함수 콘텍스트 스택에 레코드가 생성됩니다.

콘텍스트 스택은 함수 실행 및 함수가 에워싼 변수를 관리하는 자바스크립트 프로그래밍 모델입니다. 스택은 언제나 전역 데이터가 담긴, 전역 실행 콘텍스트 프레임에서 출발합니다.

전역 콘텍스트 프레임은 항상 스택 맨 밑에 위치합니다. 함수 콘텍스트 프레임은 각각 내부 전역 변수의 개수만큼 메모리를 점유합니다. 지역 변수가 하나도 없는 빈 프레임은 48바이트 정도 되고, 숫자, 불리언 같은 지역 변수/매개변수는 8바이트를 차지합니다. 함수 본체에 변수를 많이 선언할수록 당연히 스택 프레임은 커지조. 각 프레임 안에는 다음과 같은 정보가 있습니다.

executionContextData = {
scopeChain,
variableObject,
this
}

variableObject는 지역 변수와 함수는 물론, 함수의 인수, 유사배열 객체 arguments를 가리키는 속성이므로, 사실상 스택 프레임의 크기는 이 속성으로 결정됩니다. 둘째, 스코프 체인은 이 함수의 콘텍스트를 그 부모 실행 콘텍스트와 연결하거나 참조합니다. 모든 함수는 스코프 체인이 결국 직/간접적으로 전역 콘텍스트와 연결됩니다. 둘째, 스코프 체인은 이 함수의 콘텍스트를 그 부모 실행 콘텍스트와 연결하거나 참조합니다. 모든 함수는 스코프 체인이 결국 직/간접적으로 전역 콘텍스트와 연결됩니다.

스택의 주요 작동 규칙은 이렇습니다.

  • 자바스크립트는 단일 스레드로 작동합니다. 즉, 동기 실행 방식입니다.
  • 전역 콘텍스트는 단 하나만 존재합니다.(모든 함수 콘텍스트는 전역 콘텍스트를 공유합니다)
  • 함수 콘텍스트 개수에 제한은 없습니다(클라이언트 측 코드는 브라우저마다 제한 개수가 다릅니다).
  • 함수를 호출할 때마다 실행 콘텍스트가 새로 생성되며, 자기 자신을 재귀 호출할 떄에도 마찬가집니다.

함수형 프로그래밍은 함수를 최대한 사용하려고 하므로, 유연성과 재사용을 늘리고자 당면한 문제를 가능한 한 많은 함수로 분해하고 커리하는 건 얼마든지 좋지만, 커리된 함수를 지나치게 사용하면 콘텍스트 스택에 어떤 식으로든 영향을 끼칩니다.

7.1.1 커링과 함수 콘텍스트 스택

어떤 함수를 커리하면, 한 번에 인수를 전부 넣고 평가하는 체제에서 한 번에 인수를 하나씩 받는 함수 호출을 여러 번 하는 체제로 전환된다고 했습니다.