profile
모듈 대신 cProfile
모듈을 사용하라. cProfile
이 더 정확한 프로파일링 정보를 제공한다.cProfile
모듈의 Profile
객체의 runcall
메서드를 사용하기만 하면 된다.Stats
겍체를 사용하면, 프로파일링 정보 중에서 프로그램 성능을 이해하기 위해 살펴봐야 할 부분만 선택해 출력할 수 있다.profile
cProfile
def insertion_sort(data):
result = []
for value in data:
insert_value(result, value)
return result
def insert_value(array, value):
for i, existing in enumerate(array):
if existing > value:
array.insert(i, value)
return
array.append(value)
from random import randint
max_size = 10**4
data = [randint(0, max_size) for _ in range(max_size)]
test = lambda: insertion_sort(data)
from cProfile import Profile
profiler = Profile()
profiler.runcall(test)
테스트 함수가 실행되고 나면, pstats
내장 모듈에 들어 있는 Stats
클래스를 사용해 성능 통계를 추출할 수 있다.
Stats
에 들어 있는 여러 메서드를 사용해
strip_dirs
sort_stats(*keys)
print_stats(*restrictions)
from pstats import Stats
stats = Stats(profiler)
stats.strip_dirs()
stats.sort_stats('cumulative') # 누적 통계
stats.print_stats()
ncalls
tottime
tottime percall
tottime / ncalls
cumtime
cumtime percall
cultime/ncalls
stats.print_callers()
append
를 호출해 원소를 추가하고, 소비자는 pop(0)
을 사용해 원소를 받게 만들면, 리스트타입을 FIFO 큐로 사용할 수 있다. pop(0)
의 성능이 선형보다 더 크게 나빠지기 때문에 문제가 될 수 있다.collections
내장 모듈에 있는 deque
클래스는, 큐 길이와 관계없이 상수 시간 만에 append
와 popleft
를 수행하기 때문에 FIFO 큐 구현에 이상적이다. index 메서드를 사용하거나
/ for 루프와 맹목적인 비교를 사용
하면 선형 시간이 걸린다.bisect
내장 모듈의 bisect_left
함수는, 정렬된 리스트에서 원하는 값을 찾는 데 로그 시간이 걸린다. 따라서 다른 접근 방법보다 훨씬 빠르다.index
함수를 사용해 특정 값을 지닌 index를 찾아내려면, 리스트 길이에 선형으로 비례하는 시간이 필요하다.data = list(range(10**5))
index = data.index(91234)
assert index == 91234
bisect_left
from bisect import bisect_left
index = bisect_left(data, 91234) # 정확히 일치
assert index == 91234
index = bisect_left(data, 91234.56) # 근접한 값과 일치
assert index == 91235
index = bisect_left(data, 91234.23) # 근접한 값과 일치(찾는 값 이상의 값 중 근접한 값을 찾음)
assert index == 91235
heapq
내장 모듈은, 효율적으로 규모 확장이 가능한 우선순위 큐
를 구현하는 데 필요한 모든 기능을 제공한다.heapq
를 사용하려면, 우선순위를 부여하려는 원소들이 자연스러운 순서를 가져야 한다. __It__
와 같은 special method가 있어야 한다는 뜻이다.queue.PriortyQueue
클래스를 보라.queue = []
add_book(queue, Book('돈키호테', '2020-06-07'))
add_book(queue, Book('프랑켄슈타인', '2020-06-05'))
add_book(queue, Book('레미제라블', '2020-06-08'))
add_book(queue, Book('전쟁과 평화', '2020-06-03'))
def add_book(queue, book):
queue.append(book)
queue.sort(key=lambda x: x.due_date, reverse=True)
class Book:
def __init__(self, title, due_date):
self.title = title
self.due_date = due_date
#########################
now = '2020-06-10'
found = next_overdue_book(queue, now)
print(found.title)
found = next_overdue_book(queue, now)
print(found.title)
try:
next_overdue_book(queue, now)
except NoOverdueBooks:
pass # 이 문장이 실행되리라 예상함
else:
assert False # 이 문장은 결코 실행되지 않음
def next_overdue_book(queue, now):
if queue:
book = queue[-1]
if book.due_date < now:
queue.pop()
return book
raise NoOverdueBooks
class NoOverdueBooks(Exception):
pass
#########################
queue = []
book = Book('보물섬', '2020-06-04')
add_book(queue, book)
print('반납 전:', [x.title for x in queue])
return_book(queue, book)
print('반납 후:', [x.title for x in queue])
def return_book(queue, book):
queue.remove(book)
__lt__
special method를 통해 구현 가능하다.import functools
@functools.total_ordering
class Book:
def __init__(self, title, due_date):
self.title = title
self.due_date = due_date
def __lt__(self, other):
return self.due_date < other.due_date
heappush
)from heapq import heappush
queue = []
add_book(queue, Book('오만과 편견', '2020-06-01'))
add_book(queue, Book('타임 머신', '2020-05-30'))
add_book(queue, Book('죄와 벌', '2020-06-06'))
add_book(queue, Book('폭풍의 언덕', '2020-06-12'))
def add_book(queue, book):
heappush(queue, book)
list.sort()
)queue = [
Book('오만과 편견', '2020-06-01'),
Book('타임 머신', '2020-05-30'),
Book('죄와 벌', '2020-06-06'),
Book('폭풍의 언덕', '2020-06-12'),
]
queue.sort()
heapify
)from heapq import heapify
queue = [
Book('오만과 편견', '2020-06-01'),
Book('타임 머신', '2020-05-30'),
Book('죄와 벌', '2020-06-06'),
Book('폭풍의 언덕', '2020-06-12'),
]
heapify(queue)
heappop
from heapq import heappop
now = '2020-06-02'
book = next_overdue_book(queue, now)
print(book.title)
book = next_overdue_book(queue, now)
print(book.title)
try:
next_overdue_book(queue, now)
except NoOverdueBooks:
pass # 이 문장이 실행되리라 예상함
else:
assert False # 이 문장은 결코 실행되지 않음
##################
def next_overdue_book(queue, now):
if queue:
book = queue[0] # 만기가 가장 이른 책이 맨 앞에 있다
if book.due_date < now:
heappop(queue) # 연체된 책을 제거한다
return book
raise NoOverdueBooks
heapq를 사용하면 힙 연산은 빨라지지만, 메모리 사용량이 크게 늘어날 수 있음에 주의하라.
@functools.total_ordering
class Book:
def __init__(self, title, due_date):
self.title = title
self.due_date = due_date
self.returned = False # 새로운 필드
def __lt__(self, other):
return self.due_date < other.due_date
def next_overdue_book(queue, now):
while queue:
book = queue[0]
if book.returned:
heappop(queue)
continue
if book.due_date < now:
heappop(queue)
return book
break
raise NoOverdueBooks
def return_book(queue, book):
book.returned = True
memoryview
내장 타입은, 객체의 슬라이스에 대해 -> 파이썬 고성능 버퍼 프로토콜로 읽고 쓰기를 지원하는, 복사가 없는 interface를 제공한다.bytearray
내장 타입은 복사가 없는 읽기 함수(socket.recv_from
과 같은)에 사용할 수 있는 bytes
와 비슷한 변경 가능한 타입을 제공한다.memoryview
로 bytearray
를 감싸면, 복사에 따른 비용을 추가 부담하지 않고도 수신받은 데이터를 범퍼에서 원하는 위치에 slice할 수 있다.timeit
을 통해, 걸리는 시간에 대한 마이크로 벤치마크를 수행하라.def run_linear(data, to_lookup):
for index in to_lookup:
data.index(index)
def run_bisect(data, to_lookup):
for index in to_lookup:
bisect_left(data, index)
baseline = timeit.timeit(
stmt='run_linear(data, to_lookup)',
globals=globals(),
number=10)
print(f'선형 검색: {baseline:.6f}초')
comparison = timeit.timeit(
stmt='run_bisect(data, to_lookup)',
globals=globals(),
number=10)
print(f'이진 검색: {comparison:.6f}초')
slowdown = 1 + ((baseline - comparison) / comparison)
def heap_overdue_benchmark(count):
def prepare():
to_add = list(range(count))
random.shuffle(to_add)
return [], to_add
def run(queue, to_add):
for i in to_add:
heappush(queue, i)
while queue:
heappop(queue)
tests = timeit.repeat(
setup='queue, to_add = prepare()',
stmt=f'run(queue, to_add)',
globals=locals(),
repeat=100,
number=1)
return print_results(count, tests)
baseline = heap_overdue_benchmark(500)
for count in (1_000, 1_500, 2_000):
comparison = heap_overdue_benchmark(count)
print_delta(baseline, comparison)