KR_Optimization - somaz94/python-study GitHub Wiki
ํ์ด์ฌ ์ฝ๋ ์ต์ ํ์ ๊ธฐ๋ณธ ์์น๊ณผ ๊ฐ๋จํ ์ต์ ํ ๊ธฐ๋ฒ์ด๋ค.
from typing import List, Dict, Callable, Any
import time
from functools import lru_cache
import timeit
import dis
def measure_time(func: Callable, *args, **kwargs) -> float:
"""ํจ์ ์คํ ์๊ฐ ์ธก์
Args:
func: ์ธก์ ํ ํจ์
args: ํจ์ ์ธ์
kwargs: ํจ์ ํค์๋ ์ธ์
Returns:
float: ์คํ ์๊ฐ(์ด)
"""
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} ์คํ ์๊ฐ: {end_time - start_time:.6f}์ด")
return end_time - start_time
# 1. ๋ฆฌ์คํธ ์ปดํ๋ฆฌํจ์
์ฌ์ฉ
def create_list_loop(n: int) -> List[int]:
"""๋ฃจํ๋ฅผ ์ฌ์ฉํ ๋ฆฌ์คํธ ์์ฑ (๋นํจ์จ์ )"""
numbers = []
for i in range(n):
if i % 2 == 0:
numbers.append(i)
return numbers
def create_list_comprehension(n: int) -> List[int]:
"""๋ฆฌ์คํธ ์ปดํ๋ฆฌํจ์
์ฌ์ฉ (์ต์ ํ)"""
return [i for i in range(n) if i % 2 == 0]
# 2. ์ ๋๋ ์ดํฐ ์ฌ์ฉ (๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ)
def number_list(n: int) -> List[int]:
"""๋ฆฌ์คํธ ๋ฐํ (๋ฉ๋ชจ๋ฆฌ ์ง์ฝ์ )"""
return [i for i in range(n)]
def number_generator(n: int):
"""์ ๋๋ ์ดํฐ ๋ฐํ (๋ฉ๋ชจ๋ฆฌ ํจ์จ์ )"""
for i in range(n):
yield i
# 3. ์บ์ฑ ์ฌ์ฉ
def fibonacci_naive(n: int) -> int:
"""์ฌ๊ท์ ํผ๋ณด๋์น (๋นํจ์จ์ )"""
if n < 2:
return n
return fibonacci_naive(n-1) + fibonacci_naive(n-2)
@lru_cache(maxsize=128)
def fibonacci_cached(n: int) -> int:
"""์บ์ฑ๋ ํผ๋ณด๋์น (์ต์ ํ)"""
if n < 2:
return n
return fibonacci_cached(n-1) + fibonacci_cached(n-2)
# 4. ํจ์ ํธ์ถ ์ต์ํ
def process_items_inefficient(items: List[int]) -> List[int]:
"""ํจ์ ํธ์ถ์ด ๋ง์ ๋นํจ์จ์ ๋ฐฉ์"""
result = []
for item in items:
result.append(item * 2)
return result
def process_items_efficient(items: List[int]) -> List[int]:
"""ํจ์ ํธ์ถ์ ์ต์ํํ ํจ์จ์ ๋ฐฉ์"""
append = result.append # ์ง์ญ ๋ณ์์ ๋ฉ์๋ ๋ฐ์ธ๋ฉ
result = []
for item in items:
append(item * 2)
return result
# 5. ๋ณ์ ์ง์ญํ
x = 0 # ์ ์ญ ๋ณ์
def increment_global():
"""์ ์ญ ๋ณ์ ์ฌ์ฉ (๋๋ฆผ)"""
global x
for i in range(1000000):
x += 1
return x
def increment_local():
"""์ง์ญ ๋ณ์ ์ฌ์ฉ (๋น ๋ฆ)"""
x = 0
for i in range(1000000):
x += 1
return x
# ํจ์จ์ฑ ๋น๊ต ์์
if __name__ == "__main__":
# ๋ฆฌ์คํธ ์ปดํ๋ฆฌํจ์
vs ๋ฃจํ
n = 1000000
measure_time(create_list_loop, n)
measure_time(create_list_comprehension, n)
# ์ผ๋ฐ ๋ฆฌ์คํธ vs ์ ๋๋ ์ดํฐ
print("\n๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ ๋น๊ต:")
print("๋ฆฌ์คํธ: ๋ชจ๋ ํญ๋ชฉ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ")
numbers_list = number_list(1000000)
print(f"์ ๋๋ ์ดํฐ: ๊ฐ์ ํ์ํ ๋๋ง ์์ฑ")
numbers_gen = number_generator(1000000)
# ์บ์ฑ ํจ๊ณผ
print("\n์บ์ฑ ํจ๊ณผ:")
n = 35
measure_time(fibonacci_naive, n)
measure_time(fibonacci_cached, n)
# ๋ ๋ฒ์งธ ํธ์ถ์ ์บ์์์ ๋ฐ๋ก ๊ฐ์ ธ์ด
measure_time(fibonacci_cached, n)
# ๋ฐ์ดํธ์ฝ๋ ๋ถ์
print("\n๋ฐ์ดํธ์ฝ๋ ๋น๊ต:")
dis.dis(lambda: [i for i in range(10)])
print("---")
dis.dis(lambda: list(range(10)))
โ
ํน์ง:
- ๋ฆฌ์คํธ ์ปดํ๋ฆฌํจ์
- ์ ๋๋ ์ดํฐ ํ์ฉ
- ์บ์ฑ ๊ตฌํ
- ํจ์ ํธ์ถ ์ต์ํ
- ์ ์ ํ ๋ฐ์ดํฐ ๊ตฌ์กฐ
- ๋ณ์ ์ง์ญํ
- ๋ฐ์ดํธ์ฝ๋ ๋ถ์
๋ค์ํ ์ต์ ํ ๊ธฐ๋ฒ์ ์ฑ๋ฅ ๊ฐ์ ํจ๊ณผ์ด๋ค.
๊ธฐ๋ฒ | ์ผ๋ฐ ์ฝ๋ | ์ต์ ํ ์ฝ๋ | ์ฑ๋ฅ ๊ฐ์ | ์ ํฉํ ์ํฉ |
---|---|---|---|---|
๋ฆฌ์คํธ ์ปดํ๋ฆฌํจ์ |
for ๋ฃจํ์ append
|
[x for x in range(n)] |
30~50% | ๋ฆฌ์คํธ ์์ฑ ์์ |
์ ๋๋ ์ดํฐ | ์ ์ฒด ๋ฆฌ์คํธ ์์ฑ |
yield ์ฌ์ฉ |
๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๊ฐ์ | ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ |
์บ์ฑ | ์ค๋ณต ๊ณ์ฐ | @lru_cache |
์ง์์ ๊ฐ์ | ๋ฐ๋ณต ๊ณ์ฐ์ด ๋ง์ ์์ |
์ง์ญ ๋ณ์ | ์ ์ญ ๋ณ์ ์ฌ์ฉ | ์ง์ญ ๋ณ์ ์ฌ์ฉ | 20~30% | ๋ฃจํ ๋ด๋ถ ์์ |
๋ด์ฅ ํจ์ | ์ฌ์ฉ์ ์ ์ ํจ์ |
map , filter ๋ฑ |
10~20% | ๋ฐ๋ณต ์ฒ๋ฆฌ ์์ |
์์
์ ํน์ฑ์ ๋ง๊ฒ ํจ์จ์ ์ธ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ ํํ๋ ๋ฐฉ๋ฒ์ด๋ค.
from collections import defaultdict, Counter, deque, namedtuple
import array
import time
import sys
import numpy as np
def measure_operation(setup_code: str, operation_code: str, repeat: int = 3) -> float:
"""์์
์๊ฐ ์ธก์
Args:
setup_code: ์ค์ ์ฝ๋
operation_code: ์ธก์ ํ ์์
์ฝ๋
repeat: ๋ฐ๋ณต ํ์
Returns:
float: ํ๊ท ์คํ ์๊ฐ(์ด)
"""
times = timeit.repeat(operation_code, setup_code, number=100000, repeat=repeat)
return min(times)
# 1. ์ ์ ํ ์๋ฃ๊ตฌ์กฐ ์ ํ
def membership_test():
"""์๋ฃ๊ตฌ์กฐ๋ณ ๋ฉค๋ฒ์ญ ํ
์คํธ ์ฑ๋ฅ ๋น๊ต"""
# ํ
์คํธ์ฉ ๋ฐ์ดํฐ
n = 10000
data = list(range(n))
lookup_value = n // 2
# ๋ฆฌ์คํธ ๊ฒ์ (O(n))
list_time = measure_time(lambda: lookup_value in data)
# ์ธํธ ๊ฒ์ (O(1))
data_set = set(data)
set_time = measure_time(lambda: lookup_value in data_set)
# ๋์
๋๋ฆฌ ๊ฒ์ (O(1))
data_dict = {i: True for i in data}
dict_time = measure_time(lambda: lookup_value in data_dict)
print(f"๋ฆฌ์คํธ ๊ฒ์: {list_time:.6f}์ด")
print(f"์ธํธ ๊ฒ์: {set_time:.6f}์ด")
print(f"๋์
๋๋ฆฌ ๊ฒ์: {dict_time:.6f}์ด")
print(f"์ธํธ/๋ฆฌ์คํธ ์๋ ๋น์จ: {list_time/set_time:.2f}x")
# 2. Defaultdict ์ฌ์ฉ
def word_count_inefficient(words: List[str]) -> Dict[str, int]:
"""์ผ๋ฐ ๋์
๋๋ฆฌ๋ก ๋จ์ด ์นด์ดํธ (๋นํจ์จ์ )"""
word_count = {}
for word in words:
if word not in word_count:
word_count[word] = 0
word_count[word] += 1
return word_count
def word_count_defaultdict(words: List[str]) -> Dict[str, int]:
"""defaultdict๋ก ๋จ์ด ์นด์ดํธ (ํจ์จ์ )"""
word_count = defaultdict(int)
for word in words:
word_count[word] += 1
return word_count
def word_count_counter(words: List[str]) -> Dict[str, int]:
"""Counter๋ก ๋จ์ด ์นด์ดํธ (๊ฐ์ฅ ํจ์จ์ )"""
return Counter(words)
# 3. ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ธ ๋ฐฐ์ด
def compare_memory_usage():
"""์๋ฃ๊ตฌ์กฐ๋ณ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๋น๊ต"""
n = 1000000
# ์ผ๋ฐ ๋ฆฌ์คํธ
lst = list(range(n))
list_size = sys.getsizeof(lst) + sum(sys.getsizeof(i) for i in lst[:100]) / 100 * n
# array ๋ชจ๋ (ํ์
์ง์ )
arr = array.array('i', range(n))
array_size = sys.getsizeof(arr)
# NumPy ๋ฐฐ์ด
np_arr = np.arange(n, dtype=np.int32)
numpy_size = np_arr.nbytes
print(f"๋ฆฌ์คํธ ๋ฉ๋ชจ๋ฆฌ: {list_size/1024/1024:.2f} MB")
print(f"Array ๋ฉ๋ชจ๋ฆฌ: {array_size/1024/1024:.2f} MB")
print(f"NumPy ๋ฉ๋ชจ๋ฆฌ: {numpy_size/1024/1024:.2f} MB")
print(f"Array/๋ฆฌ์คํธ ๋ฉ๋ชจ๋ฆฌ ๋น์จ: {array_size/list_size:.2f}x")
print(f"NumPy/๋ฆฌ์คํธ ๋ฉ๋ชจ๋ฆฌ ๋น์จ: {numpy_size/list_size:.2f}x")
# 4. ํ ์ต์ ํ
def queue_operations():
"""ํ ๊ตฌํ๋ณ ์ฑ๋ฅ ๋น๊ต"""
# ๋ฆฌ์คํธ๋ฅผ ํ๋ก ์ฌ์ฉ (๋นํจ์จ์ )
queue_list = []
list_time = measure_time(lambda: [queue_list.append(i) for i in range(10000)] +
[queue_list.pop(0) for _ in range(10000)])
# deque ์ฌ์ฉ (ํจ์จ์ )
queue_deque = deque()
deque_time = measure_time(lambda: [queue_deque.append(i) for i in range(10000)] +
[queue_deque.popleft() for _ in range(10000)])
print(f"๋ฆฌ์คํธ ํ: {list_time:.6f}์ด")
print(f"Deque ํ: {deque_time:.6f}์ด")
print(f"๋ฆฌ์คํธ/Deque ์๋ ๋น์จ: {list_time/deque_time:.2f}x")
# 5. namedtuple vs ํด๋์ค
Point = namedtuple('Point', ['x', 'y'])
class PointClass:
def __init__(self, x, y):
self.x = x
self.y = y
def compare_namedtuple_class():
"""namedtuple๊ณผ ํด๋์ค ์ฑ๋ฅ ๋น๊ต"""
# namedtuple ์์ฑ ๋ฐ ์ ๊ทผ
namedtuple_time = measure_time(lambda: [Point(i, i*2).x for i in range(10000)])
# ํด๋์ค ์์ฑ ๋ฐ ์ ๊ทผ
class_time = measure_time(lambda: [PointClass(i, i*2).x for i in range(10000)])
print(f"Namedtuple: {namedtuple_time:.6f}์ด")
print(f"Class: {class_time:.6f}์ด")
print(f"์๋์ ์ฑ๋ฅ: {class_time/namedtuple_time:.2f}x")
# ์๋ฃ๊ตฌ์กฐ ์ต์ ํ ๋ฐ๋ชจ
if __name__ == "__main__":
print("===== ์๋ฃ๊ตฌ์กฐ๋ณ ๊ฒ์ ์ฑ๋ฅ =====")
membership_test()
print("\n===== ๋จ์ด ์นด์ดํ
๋ฐฉ๋ฒ๋ณ ์ฑ๋ฅ =====")
words = ["apple", "banana", "cherry", "apple", "banana", "apple", "date", "fig"]
measure_time(word_count_inefficient, words)
measure_time(word_count_defaultdict, words)
measure_time(word_count_counter, words)
print("\n===== ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๋น๊ต =====")
compare_memory_usage()
print("\n===== ํ ๊ตฌํ๋ณ ์ฑ๋ฅ =====")
queue_operations()
print("\n===== namedtuple vs ํด๋์ค =====")
compare_namedtuple_class()
โ
ํน์ง:
- ํจ์จ์ ์ธ ์๋ฃ๊ตฌ์กฐ
- ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ
- ๊ฒ์ ์ฑ๋ฅ ํฅ์
- ์๊ฐ ๋ณต์ก๋ ๊ณ ๋ ค
- ๊ณต๊ฐ ๋ณต์ก๋ ๊ณ ๋ ค
- ํนํ๋ ์ปฌ๋ ์ ํ์ฉ
- ํ์ ๋ณ ์ต์ ํ
์ฃผ์ ์์
๋ณ ์๋ฃ๊ตฌ์กฐ ์ฑ๋ฅ ๋น๊ต์ด๋ค.
์์ | ๋ฆฌ์คํธ | ์ธํธ | ๋์ ๋๋ฆฌ | Array | Deque |
---|---|---|---|---|---|
๊ฒ์ | O(n) | O(1) | O(1) | O(n) | O(n) |
์ฝ์ (๋) | O(1) | O(1) | O(1) | O(1) | O(1) |
์ฝ์ (์์) | O(n) | N/A | N/A | O(n) | O(1) |
์ญ์ (๋) | O(1) | O(1) | O(1) | O(1) | O(1) |
์ญ์ (์์) | O(n) | N/A | N/A | O(n) | O(1) |
์ ๋ ฌ | O(n log n) | N/A | N/A | O(n log n) | N/A |
๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ | ๋ฎ์ | ์ค๊ฐ | ์ค๊ฐ | ๋์ | ์ค๊ฐ |
๊ฐ๋ณ์ฑ | ๊ฐ๋ณ | ๊ฐ๋ณ | ๊ฐ๋ณ | ๊ฐ๋ณ | ๊ฐ๋ณ |
์๊ณ ๋ฆฌ์ฆ์ ์ ํ๊ณผ ์ต์ ํ๋ฅผ ํตํด ํ์ด์ฌ ํ๋ก๊ทธ๋จ์ ์ฑ๋ฅ์ ๊ฐ์ ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
from typing import List, Set, Dict, Tuple, Optional, Any
import heapq
import time
import random
import bisect
from functools import lru_cache
def measure_time(func, *args, **kwargs):
"""ํจ์ ์คํ ์๊ฐ ์ธก์ """
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} ์คํ ์๊ฐ: {end_time - start_time:.6f}์ด")
return result
# 1. ์ด์ง ๊ฒ์ ์ต์ ํ
def linear_search(arr: List[int], target: int) -> int:
"""์ ํ ๊ฒ์ ๊ตฌํ (O(n))"""
for i, val in enumerate(arr):
if val == target:
return i
return -1
def binary_search(arr: List[int], target: int) -> int:
"""์ด์ง ๊ฒ์ ๊ตฌํ (O(log n))"""
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
def bisect_search(arr: List[int], target: int) -> int:
"""bisect ๋ชจ๋ ์ฌ์ฉ ์ด์ง ๊ฒ์"""
i = bisect.bisect_left(arr, target)
if i != len(arr) and arr[i] == target:
return i
return -1
# 2. ์ ๋ ฌ ์๊ณ ๋ฆฌ์ฆ ์ ํ
def bubble_sort(arr: List[int]) -> List[int]:
"""๋ฒ๋ธ ์ ๋ ฌ ๊ตฌํ (O(nยฒ))"""
n = len(arr)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
def insertion_sort(arr: List[int]) -> List[int]:
"""์ฝ์
์ ๋ ฌ ๊ตฌํ (O(nยฒ), ์์ ๋ฐฐ์ด์ ํจ์จ์ )"""
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
return arr
def merge_sort(arr: List[int]) -> List[int]:
"""๋ณํฉ ์ ๋ ฌ ๊ตฌํ (O(n log n))"""
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left: List[int], right: List[int]) -> List[int]:
"""๋ณํฉ ์ ๋ ฌ์ ๋ณํฉ ๋จ๊ณ"""
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result
def quick_sort(arr: List[int]) -> List[int]:
"""ํต ์ ๋ ฌ ๊ตฌํ (ํ๊ท O(n log n))"""
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
def timsort_python(arr: List[int]) -> List[int]:
"""ํ์ด์ฌ ๋ด์ฅ ์ ๋ ฌ (Timsort)"""
return sorted(arr)
# 3. ๋์ ํ๋ก๊ทธ๋๋ฐ
def fibonacci_recursive(n: int) -> int:
"""์ฌ๊ท์ ํผ๋ณด๋์น (์ง์์ ์๊ฐ ๋ณต์ก๋)"""
if n <= 1:
return n
return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)
def fibonacci_dp(n: int) -> int:
"""๋์ ํ๋ก๊ทธ๋๋ฐ ํผ๋ณด๋์น (์ ํ ์๊ฐ ๋ณต์ก๋)"""
if n <= 1:
return n
fib = [0] * (n + 1)
fib[1] = 1
for i in range(2, n + 1):
fib[i] = fib[i-1] + fib[i-2]
return fib[n]
@lru_cache(maxsize=None)
def fibonacci_memoization(n: int) -> int:
"""๋ฉ๋ชจ์ด์ ์ด์
ํผ๋ณด๋์น (์ ํ ์๊ฐ ๋ณต์ก๋)"""
if n <= 1:
return n
return fibonacci_memoization(n-1) + fibonacci_memoization(n-2)
# 4. ๊ทธ๋ฆฌ๋ ์๊ณ ๋ฆฌ์ฆ
def coin_change_greedy(coins: List[int], amount: int) -> List[int]:
"""๊ทธ๋ฆฌ๋ ๋์ ๊ตํ (์ต์ ํด๊ฐ ์๋ ์ ์์)"""
coins.sort(reverse=True)
result = []
for coin in coins:
while amount >= coin:
amount -= coin
result.append(coin)
if amount == 0:
return result
return [] # ํด๊ฒฐ์ฑ
์ด ์์
def coin_change_dp(coins: List[int], amount: int) -> int:
"""๋์ ํ๋ก๊ทธ๋๋ฐ ๋์ ๊ตํ (์ต์ ํด)"""
# dp[i] = amount i๋ฅผ ๋ง๋๋๋ฐ ํ์ํ ์ต์ ๋์ ์
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for i in range(coin, amount + 1):
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
# 5. ๊ทธ๋ํ ์๊ณ ๋ฆฌ์ฆ
def dijkstra(graph: Dict[str, Dict[str, int]], start: str) -> Dict[str, int]:
"""๋ค์ต์คํธ๋ผ ์๊ณ ๋ฆฌ์ฆ (์ต๋จ ๊ฒฝ๋ก)"""
# ์ด๊ธฐํ
distances = {node: float('infinity') for node in graph}
distances[start] = 0
priority_queue = [(0, start)]
while priority_queue:
current_distance, current_node = heapq.heappop(priority_queue)
# ์ด๋ฏธ ์ฒ๋ฆฌํ ๋
ธ๋๋ ๊ฑด๋๋
if current_distance > distances[current_node]:
continue
for neighbor, weight in graph[current_node].items():
distance = current_distance + weight
# ๋ ์งง์ ๊ฒฝ๋ก๋ฅผ ์ฐพ์ผ๋ฉด ์
๋ฐ์ดํธ
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(priority_queue, (distance, neighbor))
return distances
# ์๊ณ ๋ฆฌ์ฆ ์ฑ๋ฅ ๋น๊ต
if __name__ == "__main__":
# ๊ฒ์ ์๊ณ ๋ฆฌ์ฆ ๋น๊ต
print("===== ๊ฒ์ ์๊ณ ๋ฆฌ์ฆ ๋น๊ต =====")
arr = sorted(random.sample(range(1000000), 10000))
target = arr[5000] # ์กด์ฌํ๋ ๊ฐ
measure_time(linear_search, arr, target)
measure_time(binary_search, arr, target)
measure_time(bisect_search, arr, target)
# ์ ๋ ฌ ์๊ณ ๋ฆฌ์ฆ ๋น๊ต
print("\n===== ์ ๋ ฌ ์๊ณ ๋ฆฌ์ฆ ๋น๊ต =====")
# ์์ ๋ฐฐ์ด์์๋ ์ฝ์
์ ๋ ฌ์ด ํจ์จ์ ์ผ ์ ์์
small_arr = random.sample(range(100), 20)
measure_time(bubble_sort, small_arr.copy())
measure_time(insertion_sort, small_arr.copy())
# ํฐ ๋ฐฐ์ด์์๋ ํจ์จ์ ์ธ ์ ๋ ฌ ์๊ณ ๋ฆฌ์ฆ ํ์
large_arr = random.sample(range(10000), 1000)
measure_time(merge_sort, large_arr.copy())
measure_time(quick_sort, large_arr.copy())
measure_time(timsort_python, large_arr.copy())
# ํผ๋ณด๋์น ๊ตฌํ ๋น๊ต
print("\n===== ํผ๋ณด๋์น ๊ตฌํ ๋น๊ต =====")
n = 30
measure_time(fibonacci_recursive, n)
measure_time(fibonacci_dp, n)
measure_time(fibonacci_memoization, n)
# ๋์ ๊ตํ ๋ฌธ์
print("\n===== ๋์ ๊ตํ ๋ฌธ์ =====")
coins = [1, 5, 10, 25]
amount = 63
greedy_result = measure_time(coin_change_greedy, coins, amount)
dp_result = measure_time(coin_change_dp, coins, amount)
print(f"๊ทธ๋ฆฌ๋ ๊ฒฐ๊ณผ: {greedy_result} (๋์ ์: {len(greedy_result)})")
print(f"DP ๊ฒฐ๊ณผ: ์ต์ ๋์ ์ {dp_result}๊ฐ")
# ๋ค์ต์คํธ๋ผ ์๊ณ ๋ฆฌ์ฆ
print("\n===== ๋ค์ต์คํธ๋ผ ์๊ณ ๋ฆฌ์ฆ =====")
graph = {
'A': {'B': 2, 'C': 5},
'B': {'A': 2, 'D': 3, 'E': 1},
'C': {'A': 5, 'F': 3},
'D': {'B': 3},
'E': {'B': 1, 'F': 1},
'F': {'C': 3, 'E': 1}
}
shortest_paths = measure_time(dijkstra, graph, 'A')
print(f"A๋ก๋ถํฐ์ ์ต๋จ ๊ฒฝ๋ก: {shortest_paths}")
โ
ํน์ง:
- ํจ์จ์ ์ธ ์๊ณ ๋ฆฌ์ฆ
- ์๊ฐ ๋ณต์ก๋ ๊ฐ์
- ๊ณต๊ฐ ๋ณต์ก๋ ๊ณ ๋ ค
- ๋์ ํ๋ก๊ทธ๋๋ฐ
- ๋ฉ๋ชจ์ด์ ์ด์
- ๊ทธ๋ฆฌ๋ ์๊ณ ๋ฆฌ์ฆ
- ๊ณ ๊ธ ๊ทธ๋ํ ์๊ณ ๋ฆฌ์ฆ
์๊ณ ๋ฆฌ์ฆ์ ์ฑ๋ฅ ํน์ฑ์ ์ดํดํ๊ธฐ ์ํ ์๊ฐ ๋ณต์ก๋ ๋น๊ต์ด๋ค.
์๊ณ ๋ฆฌ์ฆ | ์๊ฐ ๋ณต์ก๋ | ๊ณต๊ฐ ๋ณต์ก๋ | ์ฉ๋ | ์ฅ์ | ๋จ์ |
---|---|---|---|---|---|
์ ํ ๊ฒ์ | O(n) | O(1) | ์ ๋ ฌ๋์ง ์์ ๋ฐ์ดํฐ | ๊ตฌํ ๊ฐ๋จ | ๋๊ท๋ชจ ๋ฐ์ดํฐ์ ๋นํจ์จ์ |
์ด์ง ๊ฒ์ | O(log n) | O(1) | ์ ๋ ฌ๋ ๋ฐ์ดํฐ | ๋งค์ฐ ๋น ๋ฅธ ๊ฒ์ | ์ ๋ ฌ๋ ๋ฐ์ดํฐ ํ์ |
๋ฒ๋ธ ์ ๋ ฌ | O(nยฒ) | O(1) | ์์ ๋ฐ์ดํฐ์ | ๊ตฌํ ๊ฐ๋จ | ๋งค์ฐ ๋๋ฆผ |
์ฝ์ ์ ๋ ฌ | O(nยฒ) | O(1) | ๊ฑฐ์ ์ ๋ ฌ๋ ๋ฐ์ดํฐ | ์์ ๋ฐ์ดํฐ์ ํจ์จ์ | ํฐ ๋ฐ์ดํฐ์ ๋นํจ์จ์ |
๋ณํฉ ์ ๋ ฌ | O(n log n) | O(n) | ์์ ์ ์ ๋ ฌ ํ์ | ์ผ๊ด๋ ์ฑ๋ฅ | ์ถ๊ฐ ๋ฉ๋ชจ๋ฆฌ ํ์ |
ํต ์ ๋ ฌ | O(n log n) ~ O(nยฒ) | O(log n) | ์ผ๋ฐ์ ์ ๋ ฌ | ํ๊ท ์ ์ผ๋ก ๋น ๋ฆ | ์ต์ ์ ๊ฒฝ์ฐ O(nยฒ) |
๋ค์ต์คํธ๋ผ | O((V+E)log V) | O(V) | ์ต๋จ ๊ฒฝ๋ก | ๊ฐ์ค์น ๊ทธ๋ํ์ ํจ๊ณผ์ | ์์ ๊ฐ์ค์น ์ฒ๋ฆฌ ๋ถ๊ฐ |
ํ์ด์ฌ์์ ๋ณ๋ ฌ ๋ฐ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ํตํด ์ฑ๋ฅ์ ๊ฐ์ ํ๋ ๋ฐฉ๋ฒ์ด๋ค.
import asyncio
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import multiprocessing
import threading
import time
from typing import List, Callable, Any
import requests
import aiohttp
import math
def measure_time(func: Callable, *args, **kwargs) -> Any:
"""ํจ์ ์คํ ์๊ฐ ์ธก์ """
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} ์คํ ์๊ฐ: {end_time - start_time:.4f}์ด")
return result
# 1. CPU ๋ฐ์ด๋ ์์
: ๋ฉํฐํ๋ก์ธ์ฑ
def cpu_intensive_task(n: int) -> int:
"""CPU ์ง์ฝ์ ์์
(์์ ๊ณ์ฐ)
Args:
n: ๊ณ์ฐํ ๋ฒ์
Returns:
int: ์์ ๊ฐ์
"""
count = 0
for i in range(2, n + 1):
is_prime = True
for j in range(2, int(math.sqrt(i)) + 1):
if i % j == 0:
is_prime = False
break
if is_prime:
count += 1
return count
def process_data_sequential(numbers: List[int]) -> List[int]:
"""์์ฐจ ์ฒ๋ฆฌ"""
return [cpu_intensive_task(n) for n in numbers]
def process_data_multiprocessing(numbers: List[int], num_processes: int = None) -> List[int]:
"""๋ฉํฐํ๋ก์ธ์ฑ ์ฒ๋ฆฌ
Args:
numbers: ์ฒ๋ฆฌํ ์ซ์ ๋ฆฌ์คํธ
num_processes: ํ๋ก์ธ์ค ์ (None์ด๋ฉด CPU ์ฝ์ด ์ ์ฌ์ฉ)
Returns:
List[int]: ๊ฒฐ๊ณผ ๋ฆฌ์คํธ
"""
if num_processes is None:
num_processes = multiprocessing.cpu_count()
with ProcessPoolExecutor(max_workers=num_processes) as executor:
results = list(executor.map(cpu_intensive_task, numbers))
return results
# 2. I/O ๋ฐ์ด๋ ์์
: ๋ฉํฐ์ค๋ ๋ฉ
def io_task(url: str) -> int:
"""I/O ์์
(์น ์์ฒญ)
Args:
url: ์์ฒญํ URL
Returns:
int: ์๋ต ํฌ๊ธฐ
"""
response = requests.get(url)
return len(response.content)
def process_urls_sequential(urls: List[str]) -> List[int]:
"""์์ฐจ ์ฒ๋ฆฌ"""
return [io_task(url) for url in urls]
def process_urls_multithreading(urls: List[str], num_threads: int = 10) -> List[int]:
"""๋ฉํฐ์ค๋ ๋ฉ ์ฒ๋ฆฌ
Args:
urls: ์ฒ๋ฆฌํ URL ๋ฆฌ์คํธ
num_threads: ์ค๋ ๋ ์
Returns:
List[int]: ๊ฒฐ๊ณผ ๋ฆฌ์คํธ
"""
with ThreadPoolExecutor(max_workers=num_threads) as executor:
results = list(executor.map(io_task, urls))
return results
# 3. ๋น๋๊ธฐ I/O
async def async_io_task(url: str, session: aiohttp.ClientSession) -> int:
"""๋น๋๊ธฐ I/O ์์
Args:
url: ์์ฒญํ URL
session: aiohttp ์ธ์
Returns:
int: ์๋ต ํฌ๊ธฐ
"""
async with session.get(url) as response:
content = await response.read()
return len(content)
async def process_urls_async(urls: List[str]) -> List[int]:
"""๋น๋๊ธฐ ์ฒ๋ฆฌ
Args:
urls: ์ฒ๋ฆฌํ URL ๋ฆฌ์คํธ
Returns:
List[int]: ๊ฒฐ๊ณผ ๋ฆฌ์คํธ
"""
async with aiohttp.ClientSession() as session:
tasks = [async_io_task(url, session) for url in urls]
return await asyncio.gather(*tasks)
# 4. ํผํฉ ์ ๊ทผ: ํ๋ก์ธ์ค ํ + ์ค๋ ๋ ํ
def hybrid_task(data: tuple) -> int:
"""CPU ๋ฐ I/O ํผํฉ ์์
Args:
data: (๊ณ์ฐํ ์ซ์, ์์ฒญํ URL) ํํ
Returns:
int: ๊ณ์ฐ ๊ฒฐ๊ณผ + ์๋ต ํฌ๊ธฐ
"""
n, url = data
# CPU ์์
prime_count = cpu_intensive_task(n)
# I/O ์์
with ThreadPoolExecutor(max_workers=1) as executor:
response_size = executor.submit(io_task, url).result()
return prime_count + response_size
def process_hybrid_tasks_sequential(data_list: List[tuple]) -> List[int]:
"""์์ฐจ ์ฒ๋ฆฌ"""
return [hybrid_task(data) for data in data_list]
def process_hybrid_tasks_parallel(data_list: List[tuple], num_processes: int = None) -> List[int]:
"""๋ณ๋ ฌ ์ฒ๋ฆฌ (ํ๋ก์ธ์ค ํ)
Args:
data_list: ์ฒ๋ฆฌํ ๋ฐ์ดํฐ ๋ฆฌ์คํธ
num_processes: ํ๋ก์ธ์ค ์
Returns:
List[int]: ๊ฒฐ๊ณผ ๋ฆฌ์คํธ
"""
if num_processes is None:
num_processes = multiprocessing.cpu_count()
with ProcessPoolExecutor(max_workers=num_processes) as executor:
results = list(executor.map(hybrid_task, data_list))
return results
# 5. ๋น๋๊ธฐ ์ ๋๋ ์ดํฐ
async def async_generator(n: int):
"""๋น๋๊ธฐ ์ ๋๋ ์ดํฐ
Args:
n: ์์ฑํ ํญ๋ชฉ ์
"""
for i in range(n):
await asyncio.sleep(0.01) # ๋น๋๊ธฐ ๋๊ธฐ
yield i * i
async def consume_async_generator(n: int) -> List[int]:
"""๋น๋๊ธฐ ์ ๋๋ ์ดํฐ ์๋น
Args:
n: ์ฒ๋ฆฌํ ํญ๋ชฉ ์
Returns:
List[int]: ์์ง๋ ๊ฒฐ๊ณผ
"""
results = []
async for value in async_generator(n):
results.append(value)
return results
# ๋ณ๋ ฌ ๋ฐ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฒค์น๋งํฌ
def run_benchmarks():
"""๋ค์ํ ๋ณ๋ ฌ ๋ฐ ๋น๋๊ธฐ ์ ๊ทผ ๋ฐฉ์ ๋ฒค์น๋งํฌ"""
# 1. CPU ๋ฐ์ด๋ ์์
๋ฒค์น๋งํฌ
print("===== CPU ๋ฐ์ด๋ ์์
๋น๊ต =====")
numbers = [10000, 20000, 30000, 40000]
sequential_results = measure_time(process_data_sequential, numbers)
multiprocessing_results = measure_time(process_data_multiprocessing, numbers)
print(f"์์ฐจ ์ฒ๋ฆฌ ๊ฒฐ๊ณผ: {sequential_results}")
print(f"๋ฉํฐํ๋ก์ธ์ฑ ๊ฒฐ๊ณผ: {multiprocessing_results}")
# 2. I/O ๋ฐ์ด๋ ์์
๋ฒค์น๋งํฌ
print("\n===== I/O ๋ฐ์ด๋ ์์
๋น๊ต =====")
urls = [
"https://www.python.org",
"https://docs.python.org",
"https://pypi.org",
"https://www.djangoproject.com",
"https://flask.palletsprojects.com"
] * 2 # 10๊ฐ URL
sequential_io = measure_time(process_urls_sequential, urls)
multithreading_io = measure_time(process_urls_multithreading, urls)
# ๋น๋๊ธฐ I/O ์คํ
async def run_async():
return await process_urls_async(urls)
start_time = time.time()
async_io = asyncio.run(run_async())
end_time = time.time()
print(f"process_urls_async ์คํ ์๊ฐ: {end_time - start_time:.4f}์ด")
print(f"์์ฐจ I/O ๊ฒฐ๊ณผ ์: {len(sequential_io)}")
print(f"๋ฉํฐ์ค๋ ๋ฉ I/O ๊ฒฐ๊ณผ ์: {len(multithreading_io)}")
print(f"๋น๋๊ธฐ I/O ๊ฒฐ๊ณผ ์: {len(async_io)}")
# 3. ํผํฉ ์์
๋ฒค์น๋งํฌ
print("\n===== ํผํฉ ์์
๋น๊ต =====")
hybrid_data = list(zip(numbers, urls[:len(numbers)]))
sequential_hybrid = measure_time(process_hybrid_tasks_sequential, hybrid_data)
parallel_hybrid = measure_time(process_hybrid_tasks_parallel, hybrid_data)
print(f"์์ฐจ ํผํฉ ๊ฒฐ๊ณผ: {sequential_hybrid}")
print(f"๋ณ๋ ฌ ํผํฉ ๊ฒฐ๊ณผ: {parallel_hybrid}")
if __name__ == "__main__":
run_benchmarks()
โ
ํน์ง:
- ๋ณ๋ ฌ ์ฒ๋ฆฌ
- ๋น๋๊ธฐ ์ฒ๋ฆฌ
- ๋ฆฌ์์ค ํ์ฉ
- CPU ๋ฐ์ด๋ ์ต์ ํ
- I/O ๋ฐ์ด๋ ์ต์ ํ
- ํผํฉ ์ ๊ทผ๋ฒ
- ์ฑ๋ฅ ํฅ์
๋ค์ํ ๋ณ๋ ฌ ๋ฐ ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ฐฉ์์ ํน์ฑ ๋น๊ต์ด๋ค.
์ ๊ทผ ๋ฐฉ์ | ์ฌ์ฉ ์ฌ๋ก | ์ฅ์ | ๋จ์ | ๊ตฌํ ๋ฐฉ๋ฒ |
---|---|---|---|---|
๋ฉํฐํ๋ก์ธ์ฑ | CPU ๋ฐ์ด๋ ์์ | GIL ์ฐํ, ๋ค์ค ์ฝ์ด ํ์ฉ | ์ค๋ฒํค๋, ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ฆ๊ฐ |
ProcessPoolExecutor , multiprocessing
|
๋ฉํฐ์ค๋ ๋ฉ | I/O ๋ฐ์ด๋ ์์ | ์์ ๊ณต์ , ์ ์ ์ค๋ฒํค๋ | GIL๋ก ์ธํ ์ ์ฝ |
ThreadPoolExecutor , threading
|
๋น๋๊ธฐ I/O | ๋คํธ์ํฌ ์์ | ๋จ์ผ ์ค๋ ๋๋ก ๋์ ๋์์ฑ | ๋น๋๊ธฐ ์ฝ๋ ๋ณต์ก์ฑ |
asyncio , aiohttp
|
ํ์ด๋ธ๋ฆฌ๋ | ๋ณตํฉ ์์ | ์์ ํน์ฑ์ ๋ง๋ ์ต์ ํ | ๊ตฌํ ๋ณต์ก์ฑ | ํ๋ก์ธ์ค ๋ด ์ค๋ ๋ ํ |
ํ์ด์ฌ์์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ ์ต์ ํํ์ฌ ๋๊ท๋ชจ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด๋ค.
from typing import Iterator, Generator, List, Dict, Any, Callable
import gc
import sys
import psutil
import os
import weakref
import time
import numpy as np
from memory_profiler import profile
def get_memory_usage() -> float:
"""ํ์ฌ ํ๋ก์ธ์ค์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๋ฐํ (MB)"""
process = psutil.Process(os.getpid())
return process.memory_info().rss / 1024 / 1024
def print_memory_usage(label: str) -> None:
"""๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ถ๋ ฅ"""
print(f"{label}: {get_memory_usage():.2f} MB")
# 1. ์ ๋๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ
def read_large_file_into_memory(file_path: str) -> List[str]:
"""ํ์ผ์ ๋ชจ๋ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ (๋นํจ์จ์ )"""
with open(file_path, 'r') as f:
return f.readlines()
def read_large_file_generator(file_path: str) -> Generator[str, None, None]:
"""ํ์ผ์ ํ ์ค์ฉ ์ฒ๋ฆฌํ๋ ์ ๋๋ ์ดํฐ (ํจ์จ์ )"""
with open(file_path, 'r') as f:
for line in f:
yield line.strip()
def demonstrate_file_reading(file_path: str) -> None:
"""ํ์ผ ์ฝ๊ธฐ ๋ฐฉ์ ๋น๊ต"""
# ํ
์คํธ ํ์ผ ์์ฑ
with open(file_path, 'w') as f:
for i in range(1000000):
f.write(f"Line {i}\n")
# ์ ์ฒด ๋ฉ๋ชจ๋ฆฌ ๋ก๋
print_memory_usage("ํ์ผ ์ฝ๊ธฐ ์ ")
try:
lines = read_large_file_into_memory(file_path)
print(f"์ ์ฒด ๋ก๋: {len(lines)} ์ค")
except MemoryError:
print("๋ฉ๋ชจ๋ฆฌ ์๋ฌ! ํ์ผ์ด ๋๋ฌด ํฝ๋๋ค.")
print_memory_usage("์ ์ฒด ํ์ผ ๋ก๋ ํ")
# ์ ๋๋ ์ดํฐ ์ฌ์ฉ
print_memory_usage("์ ๋๋ ์ดํฐ ์ฌ์ฉ ์ ")
count = 0
for line in read_large_file_generator(file_path):
count += 1
if count % 100000 == 0:
print(f"์ฒ๋ฆฌ ์ค: {count} ์ค")
print(f"์ ๋๋ ์ดํฐ: {count} ์ค ์ฒ๋ฆฌ")
print_memory_usage("์ ๋๋ ์ดํฐ ์ฌ์ฉ ํ")
# ํ์ผ ์ญ์
os.remove(file_path)
# 2. ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง
class ResourceManager:
"""๋ฆฌ์์ค ๊ด๋ฆฌ ํด๋์ค"""
def __init__(self):
"""์ด๊ธฐํ"""
self.resources = []
def add_resource(self, resource: Any) -> None:
"""๋ฆฌ์์ค ์ถ๊ฐ"""
self.resources.append(resource)
def cleanup(self) -> None:
"""๋ฆฌ์์ค ์ ๋ฆฌ"""
for resource in self.resources:
# ๋ฆฌ์์ค ํด์ ๋ก์ง (ํ์ผ, ์ฐ๊ฒฐ ๋ฑ)
if hasattr(resource, 'close') and callable(resource.close):
resource.close()
self.resources.clear()
# ๊ฐ๋น์ง ์ปฌ๋ ์
๊ฐ์ ์คํ
gc.collect()
def simulate_memory_leak() -> None:
"""๋ฉ๋ชจ๋ฆฌ ๋์ ์๋ฎฌ๋ ์ด์
"""
class Resource:
"""๋ฆฌ์์ค ์๋ฎฌ๋ ์ด์
ํด๋์ค"""
def __init__(self, size: int):
self.data = [0] * size
def close(self):
self.data = None
# ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ์๋ ์ฝ๋
print_memory_usage("๋์ ์๋ฎฌ๋ ์ด์
์์")
leaky_resources = []
for i in range(10):
leaky_resources.append(Resource(1000000)) # ๋ฉ๋ชจ๋ฆฌ ๋์
print_memory_usage(f"๋ฆฌ์์ค {i+1}๊ฐ ์ถ๊ฐ")
# ์ฐธ์กฐ๋ฅผ ์ ๊ฑฐํด๋ ๋ฉ๋ชจ๋ฆฌ๋ ํ์๋์ง ์์
leaky_resources = None
print_memory_usage("์ฐธ์กฐ ์ ๊ฑฐ ํ")
gc.collect()
print_memory_usage("๊ฐ๋น์ง ์ปฌ๋ ์
ํ")
# ์ ์ ํ ๋ฆฌ์์ค ๊ด๋ฆฌ
print_memory_usage("๋ฆฌ์์ค ๊ด๋ฆฌ์ ์์")
manager = ResourceManager()
for i in range(10):
manager.add_resource(Resource(1000000))
print_memory_usage(f"๊ด๋ฆฌ๋ ๋ฆฌ์์ค {i+1}๊ฐ ์ถ๊ฐ")
# ์ ์ ํ ์ ๋ฆฌ
manager.cleanup()
print_memory_usage("๋ฆฌ์์ค ์ ๋ฆฌ ํ")
# 3. ์ฝํ ์ฐธ์กฐ ์ฌ์ฉ
def demonstrate_weak_references() -> None:
"""์ฝํ ์ฐธ์กฐ ์ฌ์ฉ ์์"""
class LargeObject:
"""ํฐ ๊ฐ์ฒด ์๋ฎฌ๋ ์ด์
"""
def __init__(self, name: str):
self.name = name
self.data = [0] * 1000000
# ์ผ๋ฐ ์ฐธ์กฐ
print_memory_usage("์์")
obj = LargeObject("big_object")
print_memory_usage("๊ฐ์ฒด ์์ฑ ํ")
# ์ฝํ ์ฐธ์กฐ
weak_ref = weakref.ref(obj)
print(f"์ฝํ ์ฐธ์กฐ ์ ํจ: {weak_ref() is not None}")
# ์๋ณธ ์ฐธ์กฐ ์ ๊ฑฐ
obj = None
print_memory_usage("์๋ณธ ์ฐธ์กฐ ์ ๊ฑฐ ํ")
print(f"์ฝํ ์ฐธ์กฐ ์ ํจ: {weak_ref() is not None}")
# ๊ฐ๋น์ง ์ปฌ๋ ์
gc.collect()
print_memory_usage("๊ฐ๋น์ง ์ปฌ๋ ์
ํ")
# 4. ์บ์ ์ต์ ํ
def demonstrate_caching() -> None:
"""์บ์ ์ต์ ํ ์์"""
# ๋นํจ์จ์ ์ธ ์บ์ (๋ฌด์ ํ ์ฑ์ฅ)
unlimited_cache = {}
# LRU ์บ์ (์ ํ๋ ํฌ๊ธฐ)
from functools import lru_cache
@lru_cache(maxsize=100)
def fibonacci(n: int) -> int:
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# ์บ์ ์ฑ์ฅ ์๋ฎฌ๋ ์ด์
print_memory_usage("์บ์ ์ฌ์ฉ ์ ")
# ๋ฌด์ ํ ์บ์ ์ฑ์ฅ
for i in range(1000):
key = f"key_{i}"
unlimited_cache[key] = [0] * 10000 # ๊ฐ ํญ๋ชฉ๋น ~80KB
if i % 100 == 0:
print_memory_usage(f"๋ฌด์ ํ ์บ์ {i}๊ฐ ํญ๋ชฉ")
# LRU ์บ์ ์ฌ์ฉ
for i in range(200):
fibonacci(i)
print_memory_usage("LRU ์บ์ ์ฌ์ฉ ํ")
# ์บ์ ์ ๋ฆฌ
unlimited_cache.clear()
gc.collect()
print_memory_usage("์บ์ ์ ๋ฆฌ ํ")
# 5. NumPy ๋ฐ ๋ฉ๋ชจ๋ฆฌ ๋ทฐ ์ฌ์ฉ
def demonstrate_numpy_memory_views() -> None:
"""NumPy ๋ฐ ๋ฉ๋ชจ๋ฆฌ ๋ทฐ ์ต์ ํ ์์"""
# ์ผ๋ฐ ํ์ด์ฌ ๋ฆฌ์คํธ
print_memory_usage("์์")
py_list = list(range(10000000)) # ํฐ ๋ฆฌ์คํธ
print_memory_usage("ํ์ด์ฌ ๋ฆฌ์คํธ ์์ฑ ํ")
# NumPy ๋ฐฐ์ด
np_array = np.arange(10000000) # ๋์ผํ ํฌ๊ธฐ์ NumPy ๋ฐฐ์ด
print_memory_usage("NumPy ๋ฐฐ์ด ์์ฑ ํ")
# ๋ฉ๋ชจ๋ฆฌ ๋ทฐ
buffer = bytes(range(255)) * 1000 # ๋ฐ์ดํธ ๋ฒํผ
mem_view = memoryview(buffer) # ๋ณต์ฌ ์์ด ๋ฒํผ ์ฐธ์กฐ
print_memory_usage("๋ฉ๋ชจ๋ฆฌ ๋ทฐ ์์ฑ ํ")
# ์ฌ๋ผ์ด์ฑ ๋น๊ต
# ํ์ด์ฌ ๋ฆฌ์คํธ ์ฌ๋ผ์ด์ฑ (๋ณต์ฌ๋ณธ ์์ฑ)
py_slice = py_list[1000000:2000000]
print_memory_usage("ํ์ด์ฌ ์ฌ๋ผ์ด์ค ํ")
# NumPy ์ฌ๋ผ์ด์ค (๋ทฐ)
np_slice = np_array[1000000:2000000]
print_memory_usage("NumPy ์ฌ๋ผ์ด์ค ํ")
# ์ ๋ฆฌ
py_list = None
np_array = None
buffer = None
mem_view = None
py_slice = None
np_slice = None
gc.collect()
print_memory_usage("์ ๋ฆฌ ํ")
# ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ ์คํ
if __name__ == "__main__":
print("===== ์ ๋๋ ์ดํฐ์ ํ์ผ ์ฒ๋ฆฌ =====")
demonstrate_file_reading("large_test_file.txt")
print("\n===== ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง =====")
simulate_memory_leak()
print("\n===== ์ฝํ ์ฐธ์กฐ ํ์ฉ =====")
demonstrate_weak_references()
print("\n===== ์บ์ ์ต์ ํ =====")
demonstrate_caching()
print("\n===== NumPy ๋ฐ ๋ฉ๋ชจ๋ฆฌ ๋ทฐ =====")
demonstrate_numpy_memory_views()
โ
ํน์ง:
- ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
- ๋ฆฌ์์ค ํด์
- ๊ฐ๋น์ง ์ปฌ๋ ์
- ์ ๋๋ ์ดํฐ ํ์ฉ
- ์ฝํ ์ฐธ์กฐ
- ๋ฉ๋ชจ๋ฆฌ ํ๋กํ์ผ๋ง
- ์บ์ ์ ๋ต
๋ค์ํ ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ ์ ๋ต์ ํจ๊ณผ ๋น๊ต์ด๋ค.
์ ๋ต | ํจ๊ณผ | ์ ์ฉ ๋์ | ์ฅ์ | ๋จ์ |
---|---|---|---|---|
์ ๋๋ ์ดํฐ | ๋งค์ฐ ํผ | ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ | ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ต์ํ | ์์ ์ ๊ทผ ๋ถ๊ฐ |
๋ฉ๋ชจ๋ฆฌ ๋ทฐ | ํผ | ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ | ๋ณต์ฌ ์๋ ๋ฐ์ดํฐ ์ ๊ทผ | ์ ํ๋ ์ฌ์ฉ ์ฌ๋ก |
NumPy | ํผ | ์์น ๊ณ์ฐ | ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ, ์ฑ๋ฅ | ๋ณ๋ ๋ชจ๋ ํ์ |
์ฝํ ์ฐธ์กฐ | ์ค๊ฐ | ๊ฐ์ฒด ์บ์, ๊ด์ฐฐ์ | ์ฐธ์กฐ ์ฌ์ดํด ๋ฐฉ์ง | ํน์ ์ฌ์ฉ ์ฌ๋ก์ ๊ตญํ |
LRU ์บ์ | ์ค๊ฐ | ๋ฐ๋ณต ๊ณ์ฐ | ๊ณ์ฐ ๋น์ฉ ์ ๊ฐ | ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ฆ๊ฐ |
๋ฆฌ์์ค ๊ด๋ฆฌ | ๋ค์ํจ | ํ์ผ, ์ฐ๊ฒฐ ๋ฑ | ๋ฆฌ์์ค ๋์ ๋ฐฉ์ง | ๋ช ์์ ๊ด๋ฆฌ ํ์ |
โ
๋ชจ๋ฒ ์ฌ๋ก:
- ํ๋กํ์ผ๋ง ๋๊ตฌ ํ์ฉ
- ๋ณ๋ชฉ ์ง์ ์๋ณ
- ์ ์ ํ ์๋ฃ๊ตฌ์กฐ ์ ํ
- ์๊ณ ๋ฆฌ์ฆ ๋ณต์ก๋ ๊ณ ๋ ค
- ์บ์ ์ ๋ต ์๋ฆฝ
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๋ชจ๋ํฐ๋ง
- ๋น๋๊ธฐ ์ฒ๋ฆฌ ํ์ฉ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ต์ ํ
- ์ฝ๋ ๋ฆฌํฉํ ๋ง
- ์ฑ๋ฅ ํ ์คํธ ์๋ํ
ํจ๊ณผ์ ์ธ ํ์ด์ฌ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ๋จ๊ณ๋ณ ์ ๊ทผ๋ฒ์ด๋ค.
-
์ธก์ ๊ณผ ํ๋กํ์ผ๋ง
- ์๊ฐ/๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ธก์
- ๋ณ๋ชฉ ์ง์ ์๋ณ
- ๊ฐ์ค ์ค์
-
์ฝ๋ ์ต์ ํ
- ํจ์จ์ ์ธ ์๋ฃ๊ตฌ์กฐ ์ ํ
- ์๊ณ ๋ฆฌ์ฆ ๊ฐ์
- ๋ด์ฅ ํจ์ ํ์ฉ
-
์ปดํจํ ๋ฆฌ์์ค ์ต์ ํ
- ๋ณ๋ ฌ ์ฒ๋ฆฌ ์ ์ฉ
- ๋น๋๊ธฐ ์ฒ๋ฆฌ ํ์ฉ
- ์บ์ฑ ์ ๋ต ์ ์ฉ
-
๋ฉ๋ชจ๋ฆฌ ์ต์ ํ
- ์ ๋๋ ์ดํฐ ์ฌ์ฉ
- ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง
- ๋ฐ์ดํฐ ์์ถ/์ธ์ฝ๋ฉ
-
์ธ๋ถ ๋ฆฌ์์ค ์ต์ ํ
- I/O ์ต์ ํ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ๊ฐ์
- ๋คํธ์ํฌ ํต์ ํจ์จํ