Programming

[Swift] Memoization

 2018. 10. 12. 00:59
반응형

함수형 프로그래밍에서는 캐싱을 어떻게 구현할까?

클래스 내부에 캐시를 두면 결국 상태값을 클래스에 보관하는 것이 아닐까?

함수안에 변수를 쌓아둘 수는 없을까?


함수형 프로그래밍을 접하게 되면 위와 같은 고민을 자연스럽게 하게 될 것이다. '함수형 사고' 책 4장에는 이러한 고민을 덜어줄 기법이 나온다. 바로 '메모이제이션'이라는 기법이다. 도널드 미치의 Memo functions and machine learning 논문에서 처음 등장했다고 한다. 메모이제이션은 함수 레벨에서 캐싱을 할 수 있도록 도와준다.


그루비에서는 memoize()라는 함수를 제공한다. someFunction.memoize() 와 같이 호출할 수 있다. 이렇게 되면 클래스 내부에 캐시를 가지고 있지 않더라도 캐시와 동일한 기능을 구현할 수 있다. 심지어 더 빠르다. 메모아이즈의 핵심은 불변성이다. 함수가 캐싱되어 있는데, 결과값이 매번 변경되면 안되기 때문에 불변성이 중요하다. 함수 외부 정보에 의존해서는 안된다.


Swift를 기반으로 작업하고 있다보니 자연스럽게 Swift에도 그루비 같은 함수를 제공하고 있지는 않을까 궁금해졌다. 검색해보니 함수를 직접 제공하지는 않지만, WWDC 2014에서 Advanced Swift 세션에서 메모이제이션 기법에 대해 소개하고 있다. 이 세션에 관해 잘 정리되어 있는 글도 있으니 참고해보면 좋을 것 같다. 아래는 재귀적 상황에서 메모이제이션을 사용하는 것을 소개한 부분이다. 처음엔 조금 복잡해보이지만 재귀적 상황에서도 사용할 수 있다.



이 글에서는 재귀적 상황이 아닐 때 어떤 식으로 함수를 적용하면 좋을지 알아보고자 한다. '함수형 사고' 책에서 나오는 예제를 스위프트 버전으로 바꿔봤다. 먼저 완전수 인지 체크하는 클래스이다. 캐시를 사용하지 않았기 때문에 두번째 실행때도 속도 차이가 거의 나지 않는다. (컴파일 최적화로 인해서 조금의 속도 차이는 있는 것으로 보인다.)


첫 번째 수행 시간: 14.911 초

두 번째 수행 시간: 13.581 초



이번에는 캐시를 사용해서 성능 개선을 한 버전이다. 클래스 내부에 캐시를 위한 변수를 둔다. 두 번째 호출 때 확실히 속도가 빨라진 것을 확인할 수 있다.


첫 번째 수행 시간: 14.897 초

두 번째 수행 시간: 0.080 초



그리고 이번에는 메모이제이션이 적용된 함수이다. memoize(someFunction) 한 줄 이면 캐시 가능한 함수가 반환된다. 속도 또한 더욱 빠른 것을 확인할 수 있다.


첫 번째 수행 시간: 14.748 초

두 번째 수행 시간: 0.0465 초



테스트 코드



번거로운 작업 없이도 캐싱을 구현할 수 있고, side effect 에 대해서도 좀 더 안전한 코드를 얻을 수 있다. 함수를 활용한 캐싱이라는 것이 생소하게 느껴졌지만, 앞으로 더 익숙해지면 작업량도 줄이고, 좋은 성능의 프로그램을 구현할 수 있을 것 같다. 책의 예제는 다른 언어로 되어 있지만 내가 사용하는 언어로 바꿔보는 것도 색다른 재미가 있다는 것을 알게되었다.


함수형 사고
국내도서
저자 : 닐 포드(Leal Ford) / 김재완역
출판 : 한빛미디어 2016.07.01
상세보기


반응형