라벨이 optimization인 게시물 표시

[Python] cache 데코레이터로 최적화하기

최적화는 귀찮다. 눈에 띄는 실수를 한 게 아니면 어떻게 고쳐야 할지 감이 오지도 않고, 대부분의 최적화는 가독성을 떨어뜨리기 때문에 버그가 발생할 확률이 늘어난다. 하지만 어떤 최적화 테크닉은 코드를 크게 수정하지 않고 큰 성능 향상을 가져온다. 메모이제이션 이 그 대표적인 예제다. 계산이 무겁거나, 디스크의 값을 읽거나, 네트워크 통신처럼 근본적으로 시간이 오래 걸리는 일은 그 실행 결과를 저장했다 재사용하는 것만으로 큰 성능향상을 가지고 온다. 파이썬은 메모이제이션을 쉽게 적용할 수 있는 데코레이터 를 제공한다. functools 모듈의 lru_cache 데코레이터 가 이것이다. 이 데코레이터를 붙이면 함수의 실행 결과를 캐싱해준다. 캐시의 크기는 maxsize 로 지정할 수 있다. 저장할 실행 값이 이 개수를 넘어가는 경우 LRU 알고리즘 에 따라 가장 오래전에 사용한 결과를 지우고 새 값을 캐싱한다. lru_cache 를 사용하면 쉽게 최적화할 수 있지만 아무 함수에나 사용할 수 있는 건 아니다. 함수의 인자를 캐시키로 사용하기 때문에 함수의 실행 결과가 함수의 인자 이외에 다른 요소에 의존적인 함수에는 사용하지 못한다. 즉, 랜덤 요소가 들어가거나 시간에 따라 결괏값이 변하는 함수에는 사용하면 안 된다. 결정성이 보장되는 함수에만 사용할 수 있다는 것은 모든 캐시의 공통적인 특성이다. 여기에 더해 파이썬이 제공하는 lru_cache 는 그 구현상의 문제로 한 가지 제약이 더 있다. 이 데코레이터는 값을 저장하기 위해 인자를 키로 가지는 dictionary 를 사용한다. 따라서 모든 인자가 hashable 타입이어야 한다. 다시 말해 mutable 하지 않은 dictionary, set, list 등을 인자로 받는 함수는 이 데코레이터를 사용해 캐싱할 수 없다. 이런 타입을 인자로 받던 함수는 그 인자를 frozenset 이나 tuple 같은 immutable 타입으로 변환해야 한다. 게다가 keyword argument 를...

[C] tagged pointer - 포인터에 정보 담기

Tagged pointer는 메모리 크기를 줄이기 위한 고전적인 테크닉이다. 기본적인 아이디어는 포인터의 모든 값이 의미 있는 값은 아니라는 것이다. 예를 들어 4 byte 단위로 align 되는 객체의 32-bit 포인터를 생각해보자. 그렇다면 이 객체의 주소는 4로 나누어 떨어지는 값이 돼야 하니 LSB(Least Significant Bit) 으로 부터 2 bit은 언제나 0b00 으로 고정될 것이다. 그렇다면 이 2 bit을 다른 정보를 담는 데 써도 아무 문제가 없다. 조금 더 구체적으로 경우 포인터의 값이 0x5678FFF0 , 0x5678FFF1 , 0x5678FFF2 , 0x5678FFF3 인 경우 모두 0x5678FFF0 에 있는 객체를 가리키도록 하고, 0x5678FFF4 , 0x5678FFF5 , 0x5678FFF6 , 0x5678FFF7 인 경우 모두 0x5678FFF4 를 가리키는 포인터로 해석하는 것이다. Tagged pointer를 만드는데 LSB 만 쓸 수 있는 건 아니다. 보통 user space에서 쓸 수 있는 최대 메모리가 제한돼 있다. 예를 들어 32-bit 윈도우에서 user space는 최대 3GB 까지 늘릴 수 있지만 , 기본적으로 2GB이다. 즉, MSB(Most Significant Bit) 1 bit를 tag에 쓸 수 있다. 64-bit 리눅스라면, 프로세스당 최대 메모리 스페이스는 256 TB까지 이므로 48 bit만 사용된다. 즉, MSB로부터 16 bit를 tag에 사용할 수 있다. 하지만 위의 두 예시에서 보았듯이 tag에 이용할 수 있는 MSB의 크기는 시스템별로 다르다. 따라서 MSB를 tagged pointer로 사용하는 경우 portable 한 코드를 만들기 어려워진다. Tagged pointer를 모든 포인터에 일반적으로 적용하지 않아도 된다. 그보다는 테이블같은 것에 저장할 포인터에만 사용하거나 포인터를 리턴하는 함수에 대해서만 사용하는 것이 일반적이다. 특히 포인터와 추가 정보를 리턴하...

이 블로그의 인기 게시물

USB 2.0 케이블의 내부 구조

[C++] enum class - 안전하고 쓰기 쉬운 enum

Log Aggregator 비교 - Scribe, Flume, Fluentd, logstash

[Web] SpeechSynthesis - TTS API

[Python] cache 데코레이터로 최적화하기