PythonIntermediateProgramming
임찬식(chanshik@gmail.com)
1
PythonIntermediateProgramming
타입과 객체
함수와함수형프로그래밍
클래스와객체지향프로그래밍
데이터구조
튜닝과 최적화
2
튜닝과 최적화
시간 측정
메모리측정
프로파일링
튜닝전략
3
시간 측정
time.clock()
현재CPU 경과 시간 값을얻어올때사용
time.time()
실제실행시간을얻어올때사용
timeit.timeit(code[, setup])
매우짧게 실행되는문장에대해서시간을측정할때사용
>>> from timeit import timeit
>>> timeit('math.sqrt(2.0)', 'import math')
0.11009419399488252
>>> timeit('sqrt(2.0)', 'from math import sqrt')
0.06311527400976047
4
시간 측정
timeit('math.sqrt(2.0)', 'import math')
첫번째인수는벤치마크하려는코드
두번째인수는실행환경을설정하기 위해한번만실행될문장
number=count 키워드인수를설정해반복할실행횟수지정
timeit() 함수는벤치마크코드를100만번실행하고 경과 시간 보고
timeit.repeat(code[, setup])
시간을세번측정하고 결과를리스트로반환
>>> from timeit import repeat
>>> repeat('math.sqrt(2.0)', 'import math')
[0.09879132499918342, 0.09977803799847607, 0.09539347399550024
>>> repeat('sqrt(2.0)', 'from math import sqrt')
[0.0634966959914891, 0.060734901009709574, 0.05894679999619257
5
메모리측정
sys.getsizeof()
개별파이썬객체메모리사용량을바이트단위로반환
>>> import sys
>>> sys.getsizeof(10)
28
>>> sys.getsizeof("Hello, World!")
62
>>> sys.getsizeof([1, 2, 3, 4])
96
>>> sum(sys.getsizeof(x) for x in [1, 2, 3, 4])
112
리스트, 튜플, 사전같은컨테이너에대해서얻은크기는
컨테이너객체그 자체의크기만을의미
내부에있는모든객체크기를계산하려면sum() 을이용
6
프로그램프로파일링
profile, cProfile
프로파일정보를수집하는데사용되는모듈
도달범위(coverage) 분석
성능통계 정보수집
명령줄에서cProfile실행
% python -m cProfile program.py
7
프로그램프로파일링
보고서항목별의미
항목 설명
primitivecalls 비재귀 함수호출횟수
ncalls 총호출횟수(자신에대한호출도포함)
tottime 이함수에서머무른시간
percall tottime/ncalls
cumtime 이함수에서머무른총시간
percall cumtime/(primitivecalls)
filename:lineno(function) 각함수의위치와이름
8
튜닝전략: 프로그램이해
최적화하려는코드를먼저프로파일링후에가장시간을많이
사용하는함수나메서드에초점을맞추어최적화진행
드물게 호출되는확실하지않은연산을최적화하는것은
전체적인속도향상에도움이되지않음
9
튜닝전략: 알고리즘이해
비효율적인알고리즘을최적화하기 보다는더나은알고리즘고민을먼저
형편없이구현된O(nlogn) 알고리즘이잘구현된O (n^3) 보다는
월등히빠른성능을내는게 일반적
10
튜닝전략: 내장타입사용
파이썬내장타입(튜플, 리스트, 집합, 사전)은모두C 로구현되어
빠른성능을보이므로내장타입을활발하게 사용하고 데이터구조를
만들어내장데이터타입을흉내내는것은비효율적
표준라이브러리에있는타입도필요한상황에맞게 사용
리스트앞에데이터를추가하는작업에는collection.deque사용고려
리스트가장앞에항목을추가하면모든데이터를옆으로옮기는비효율적인
작업을수행해야하는데, 이경우에는deque가 적절한데이터구조
>>> timeit('s.insert(0, 42)', 's=[]', number=100000)
3.213872305001132
>>> timeit('s.appendleft(42)',
>>> 'import collections; s = collections.deque()',
>>> number=100000)
0.010021128007792868
11
튜닝전략: 계층을추가하지않는것
객체나함수에대해서계층을추가하는것은성능에영향을주기 때문에
계층을추가하는것에대해고민하는것이필요
dict() 함수를사용해서문자열키를갖는사전을생성하는것은
따옴표를제거하는효과가 있지만, 성능에영향을줌
s = dict(name='GOOG',shares=100,price=450.10)
# s = {'name':'GOOG','shares':100,'price':450.10} 와 동일
>>> timeit(
... "s = {'name':'GOOG','shares':100,'price':450.10}")
0.1306551239977125
>>> timeit(
... "s = dict(name='GOOG',shares=100,price=450.10)")
0.3718919529928826
12
튜닝전략: 사용자정의클래스와인스턴스의기반은사전
인스턴스데이터를검색하고 설정하는작업은사전을
직접이용하는것보다대부분느림
데이터를저장하기 위한간단한자료구조를원한다면
클래스정의보다는사전을사용하는것이더효율적
stock.py
class Stock(object):
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
>>> timeit("s=Stock('GOOG',100,450.0)",
>>> "from stock import Stock")
0.5741711250011576
>>> timeit("s={'name':'GOOG','shares':100,'price':450.0}")
0.13291993999155238
13
튜닝전략: __slots__ 사용
프로그램에서사용자정의클래스인스턴스를다수생성할경우에는
__slots__ 속성을이용해사전을사용하지않는것 고려
stock.py
class StockSlots(object):
__slots__ = ['name', 'shares', 'price']
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
__slots__ 을사용하면인스턴스데이터를사전이아닌자료구조에저장
>>> timeit("s=Stock('GOOG',100,450.0)", "from stock import Stoc
0.5932310930074891
>>> timeit("s=StockSlots('GOOG',100,450.0)", "from stock import
0.4837448360049166
14
튜닝전략: 점(.) 연산자사용자제
객체속성을찾을때점(.) 연산자를사용하면항상이름검색이수행
메서드검색이나모듈검색이잦은경우에는지역변수를사용해
속성검색연산을제거하는것이효율적
>>> timeit("math.sqrt(5.0)", "import math")
0.0976861710078083
>>> timeit("sqrt(5.0)", "from math import sqrt")
0.06044331699376926
속성검색을제거하는것이가독성에영향을줄수있으므로
성능이중요한곳에서사용하는것이좋음
15
튜닝전략: 예외처리
흔하게 발생하지않는경우를처리하기 위해서예외를사용
예외를잘발생시키지않는코드를try 블록으로처리
if문을이용해잘발생하지않는조건을검색하는것은
비효율적
흔한경우에대한예외처리는피하는형태로구현
흔하게 발생하는예외는조건을직접검사하는방식이효율적
16
튜닝전략: 예외처리
get_item.py
def get_item_try(items, key):
try:
value = items[key]
except KeyError:
value = None
return value
def get_item_in(items, key):
if key in items:
value = items[key]
else:
value = None
return value
17
튜닝전략: 예외처리
>>> timeit("get_item_try({'a': 10, 'b': 20}, 'c')",
>>> "from get_item import get_item_try")
0.6027262949937722
>>> timeit("get_item_in({'a': 10, 'b': 20}, 'c')",
>>> "from get_item import get_item_in")
0.26192101500055287
18
튜닝전략: 함수형프로그래밍
데이터처리를수행할때리스트내포와생성기 표현식은직접데이터에대해
반복을수행하는연산에비해효율적
생성기를사용해더빠르면서메모리를효율적으로사용하는코드작성가능
19

Python Programming: Tuning and Optimization