๐Ÿ”จ[๊ฐœ๋ฐœ] ํ”„๋กœํŒŒ์ผ๋ง (feat. python)

manduยท2025๋…„ 3์›” 15์ผ

[๊ฐœ๋ฐœ]

๋ชฉ๋ก ๋ณด๊ธฐ
1/9

1. ํ”„๋กœํŒŒ์ผ๋ง

1.1 ํ”„๋กœํŒŒ์ผ๋ง(Profiling)์ด๋ž€?

ํ”„๋กœ๊ทธ๋žจ์˜ ์„ฑ๋Šฅ(์‹œ๊ฐ„ ๋ฐ ๊ณต๊ฐ„ ๋ณต์žก๋„, ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ์ฃผ๊ธฐ์™€ ๋นˆ๋„ ๋“ฑ)์„ ๋ถ„์„ํ•˜๋Š” ๊ณผ์ •

  • ์ˆจ๊ฒจ์ง„ ๋ณ‘๋ชฉ ์ง€์ ์„ ์ฐพ์•„ ํ”„๋กœ๊ทธ๋žจ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Œ
  • ํŠนํžˆ, ๋™์  ํƒ€์ดํ•‘ ์–ธ์–ด์ธ Python์€ ์˜ˆ์ƒ๋˜๋Š” ์ง€์—ฐ ๊ตฌ๊ฐ„๊ณผ ์‹ค์ œ ์ง€์—ฐ์ด ์ผ์–ด๋‚˜๋Š” ๋ถ€๋ถ„์ด ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ (ํƒ€์ž… ๋ณ€ํ™˜์ด๋‚˜ ๊ฐ์ฒด ์ƒ์„ฑ ๋น„์šฉ์ด ์–ด๋””์„ ๊ฐ€ ์ปค์งˆ ์ˆ˜ ์žˆ์Œ)

1.2 Python ํ”„๋กœํŒŒ์ผ๋ง ๋„๊ตฌ

1. time, datetime ๋ชจ๋“ˆ

  • ํ˜„์žฌ ์‹œ๊ฐ„์„ return ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ํŠน์ • ์ฝ”๋“œ์˜ ์†Œ์š” ์‹œ๊ฐ„์„ ๋น ๋ฅด๊ฒŒ ์ธก์ •ํ•ด๋ณผ ์ˆ˜ ์žˆ์Œ
  • ๋งค๋ฒˆ print๋ฅผ ์ฝ”๋“œ๋งˆ๋‹ค ์ถ”๊ฐ€ํ•ด ๋‹ค์‹œ ๋นŒ๋“œํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๊ณ , ์„ธ๋ถ€์ ์ธ ์„ฑ๋Šฅ ๋ถ„์„์ด ๋ถˆ๊ฐ€๋Šฅํ•จ

2. profile ๋ชจ๋“ˆ

  • ์ฝ”๋“œ ์‹คํ–‰ ์ค‘ ๊ฐ ํ•จ์ˆ˜์˜ ์‹คํ–‰ ์‹œ๊ฐ„๊ณผ ํ˜ธ์ถœ ํšŸ์ˆ˜ ๊ธฐ๋ก
  • Python ์ฝ”๋“œ๋กœ๋งŒ ์ž‘์„ฑ๋œ ๊ธฐ๋ณธ ํ”„๋กœํŒŒ์ผ๋Ÿฌ
    โ†’ ๋™์  ํƒ€์ž…, ์ธํ„ฐํ”„๋ฆฌํ„ฐ ์–ธ์–ด์ด๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฒ„ํ—ค๋“œ ์กด์žฌ
    โ†’ CPython์œผ๋กœ ์ž‘์„ฑ๋œ 5. cProfile๋ณด๋‹ค ๋” ๋А๋ ค์„œ ์ž˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Œ

3. line_profiler

  • ํ•จ์ˆ˜ ๋‚ด ๊ฐœ๋ณ„ ์ฝ”๋“œ ๋ผ์ธ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •

4. memory_profiler

  • ํ•จ์ˆ˜ ๋‚ด ๊ฐœ๋ณ„ ์ฝ”๋“œ ๋ผ์ธ์˜ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ ์ธก์ •

5. cProfile ๋ชจ๋“ˆ

  • Python ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ํฌํ•จ๋œ ํ”„๋กœํŒŒ์ผ๋ง ๋„๊ตฌ๋กœ, ํ•จ์ˆ˜ ํ˜ธ์ถœ ํšŸ์ˆ˜ ๋ฐ ์‹คํ–‰ ์‹œ๊ฐ„์„ ๋ถ„์„ํ•˜๋Š” ๋ฐ ์œ ์šฉ
  • Python ์ฝ”๋“œ ์‹คํ–‰ ํ๋ฆ„์„ ์ถ”์ ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜๋ฉฐ, C ์–ธ์–ด๋กœ ๊ตฌํ˜„๋œ lsprof ๋ชจ๋“ˆ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•จ
    โ†’ profile๋ณด๋‹ค ๋น ๋ฆ„

1.3 Sample Code

import random

def generate_data(n):
    """๋žœ๋ค ์ˆซ์ž ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ"""
    return [random.randint(1, 10000000) for _ in range(n)]

def sort_data(data):
    """๋ฐ์ดํ„ฐ๋ฅผ ์ •๋ ฌ"""
    sorted_data = sorted(data)
    return sorted_data

def get_sum_from_data(data):
    """๋ฐ์ดํ„ฐ ํ•ฉ๊ณ„ ๊ตฌํ•˜๊ธฐ"""
    total = sum(data)
    return total

def test_func():
    """๋ฐ์ดํ„ฐ ์ƒ์„ฑ โ†’ ์ฒ˜๋ฆฌ โ†’ ๊ฒฐ๊ณผ ์ถœ๋ ฅ"""
    data = generate_data(100000)
    sorted_data = sort_data(data)
    total = get_sum_from_data(sorted_data)

    print(f"์ดํ•ฉ: {total}, ์ตœ๋Œ€๊ฐ’: {sorted_data[-1]}")

1.4 line_profiler, memory_profiler๋กœ ํ”„๋กœํŒŒ์ผ๋ง ํ•ด๋ณด๊ธฐ

line_profiler

pip install line_profiler

from line_profiler import LineProfiler
@profile # ํ”„๋กœํŒŒ์ผ๋ง ์›ํ•˜๋Š” ํ•จ์ˆ˜ ์œ„์— ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ž‘์„ฑ
def test_func():

kernprof -l -v test_code.py # ํ„ฐ๋ฏธ๋„์—์„œ ์‹คํ–‰
# -l : line_profiler๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ฐ ๋ผ์ธ๋ณ„ ์„ฑ๋Šฅ์„ ์ถ”์ 
# -v : ํ”„๋กœํŒŒ์ผ๋ง ๊ฒฐ๊ณผ๋ฅผ ํ™”๋ฉด์— ์ถœ๋ ฅ

# Hits: ๊ฐ ์ฝ”๋“œ ๋ผ์ธ์ด ์‹คํ–‰๋œ ํšŸ์ˆ˜
# Time: ํ•ด๋‹น ๋ผ์ธ์—์„œ ์†Œ์š”๋œ ์‹œ๊ฐ„ (๋งˆ์ดํฌ๋กœ์ดˆ ๋‹จ์œ„)
# Per Hit: ๊ฐ ํ˜ธ์ถœ๋‹น ์†Œ์š”๋œ ์‹œ๊ฐ„
# % Time: ํ•ด๋‹น ๋ผ์ธ์ด ์ „์ฒด ํ•จ์ˆ˜์—์„œ ์ฐจ์ง€ํ•˜๋Š” ๋น„์œจ

memory_profiler

pip install memory_profiler

@profile # ํ”„๋กœํŒŒ์ผ๋ง ์›ํ•˜๋Š” ํ•จ์ˆ˜ ์œ„์— ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ž‘์„ฑ
def test_func():

python -m memory_profiler test_code.py # ํ„ฐ๋ฏธ๋„์—์„œ ์‹คํ–‰
# -m: ๋ชจ๋“ˆ ๋ชจ๋“œ(Python ์Šคํฌ๋ฆฝํŠธ๋ฅผ memory_profiler ๋ชจ๋“ˆ๋กœ ์‹คํ–‰)

# Mem usage: ํ•ด๋‹น ๋ผ์ธ์—์„œ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์ „์ฒด ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰
# Increment: ํ•ด๋‹น ๋ผ์ธ์—์„œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ฆ๊ฐ€ํ–ˆ๋Š”์ง€
# Occurrences: ํ•ด๋‹น ๋ผ์ธ์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๋ฐœ์ƒํ•œ ํšŸ์ˆ˜

1.5 cProfile, pstats๋กœ ํ”„๋กœํŒŒ์ผ๋ง ํ•ด๋ณด๊ธฐ

cProfile.Profile
Python ์ฝ”๋“œ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์„ ํ”„๋กœํŒŒ์ผ๋งํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ

์ฃผ์š” ๋ฉ”์„œ๋“œ:

  • run(statement)
    • statement: ํ”„๋กœํŒŒ์ผ๋ง ํ•  ํ•จ์ˆ˜๋ฅผ string type์œผ๋กœ ์ž‘์„ฑ
    • ์ง€์ •ํ•œ ํ•จ์ˆ˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ํ”„๋กœํŒŒ์ผ๋ง ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘
    • ex) profiler.run("test_func(arg1, arg2)")
  • enable() / disable()
    • ํ”„๋กœํŒŒ์ผ๋ง ๊ตฌ๊ฐ„์„ ์ •ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ
    • ex) profiler.enable(): ํ”„๋กœํŒŒ์ผ๋ง ์‹œ์ž‘, profiler.disable(): ํ”„๋กœํŒŒ์ผ๋ง ์ค‘์ง€

pstats.Stats
cProfile.Profile๋กœ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ ํ”„๋กœํŒŒ์ผ๋ง ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์ถœ๋ ฅํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ

์ฃผ์š” ๋ฉ”์„œ๋“œ:

  • strip_dirs()
    • ํ”„๋กœํŒŒ์ผ๋ง ๊ฒฐ๊ณผ์—์„œ ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ์ œ๊ฑฐํ•˜์—ฌ, ํŒŒ์ผ๋ช…๋งŒ ๋‚จ๊น€
    • ex) stats.strip_dirs()
  • sort_stats(*args)
    • ํ”„๋กœํŒŒ์ผ๋ง ๊ฒฐ๊ณผ๋ฅผ ์ •๋ ฌ
    • *args: 'cumulative', 'time', 'calls', 'name'
    • ex) stats.sort_stats('cumulative')
  • print_stats()
    • ํ”„๋กœํŒŒ์ผ๋ง ๊ฒฐ๊ณผ๋ฅผ ์ฝ˜์†”์— ์ถœ๋ ฅ
    • ์ธ์ž๋ฅผ ํ†ตํ•ด ์ถœ๋ ฅ ๊ฐœ์ˆ˜๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Œ
    • ex) stats.print_stats(20)
  • dump_stats(filename)
    • ํ”„๋กœํŒŒ์ผ๋ง ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์ผ์— ์ €์žฅ
    • ex) stats.dump_stats('profile_results.prof')

Example

from cProfile import Profile
from pstats import Stats

profiler = Profile()
profiler.run("test_func()")
stats = Stats(profiler)
stats.strip_dirs()
stats.sort_stats('cumulative')
stats.print_stats(20)

SnakeViz๋กœ cProfile ๊ฒฐ๊ณผ ์‹œ๊ฐํ™” ํ•˜๊ธฐ

pip install snakeviz
snakeviz test_code.prof

profile
๋งŒ๋‘๋Š” ๋ชฉ๋ง๋ผ

0๊ฐœ์˜ ๋Œ“๊ธ€