ЁЯУЛ рдзрдбрд╛ 03 тАФ Profiling: рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдХреЗрдЪрд╛ clipboard
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 03 ┬╖ рдорд╛рдЧреЗ: lesson-02-benchmarking ┬╖ рдкреБрдвреЗ: lesson-04-complexity
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ02, рдЕрдзрд┐рдХ profiling: рдХрд╛рд╣реАрд╣реА рдмрджрд▓рдгреНрдпрд╛рдЖрдзреА, рд╡реЗрд│ рдХреБрдареЗ
рдЬрд╛рддреЛ рддреЗ рд╢реЛрдзрд╛. Profiler рдХреЛрдгрддреА functions рдЪрд╛рд▓рддрд╛рдд, рдХрд┐рддреА рд╡реЗрд│рд╛, рдЖрдгрд┐ рдХрд┐рддреА рдХрд╛рд│ рддреЗ рдиреЛрдВрджрд╡рддреЛ. рд╣рд╛ рдзрдбрд╛
Python рдЪреНрдпрд╛ built-in cProfile рдордзреВрди call counts рд╡рд╛рдЪрддреЛ тАФ counts рдкреНрд░рддреНрдпреЗрдХ machine рд╡рд░ рд╕рд╛рд░рдЦреЗ рдЕрд╕рддрд╛рдд,
рд╡реЗрд│рд╛ рдирд╛рд╣реАрдд тАФ рдЖрдгрд┐ рддреНрдпрд╛рдВрдЪрд╛ рд╡рд╛рдкрд░ рдХрд░реВрди рдПрдХ hot spot рд╢реЛрдзреВрди рддреЛ рджреБрд░реБрд╕реНрдд рдХрд░рддреЛ.
perf/demo.py рдордзрд▓реЗ profile() рдЖрдгрд┐ perf/sim.py рдордзрд▓реЗ results_slow, results_fast рдЖрдгрд┐
profile_calls.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рдХреНрд░реАрдбрд╛ рджрд┐рдирд╛рдЪреЗ results рдЫрд╛рдкрд╛рдпрд▓рд╛ рдЦреВрдк рд╡реЗрд│ рд▓рд╛рдЧрддреЛ. рдкреНрд░рддреНрдпреЗрдХрд╛рдЪрд╛ рдХрд╛рд╣реАрддрд░реА рдЕрдВрджрд╛рдЬ рдЖрд╣реЗ. "Printer рдЬреБрдирд╛ рдЖрд╣реЗ!" "рдмреЗрд░рдЬрд╛ рдХрдареАрдг рдЖрд╣реЗрдд!"
рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рджреАрдкрд┐рдХрд╛ рдЕрдВрджрд╛рдЬ рдХрд░рдд рдирд╛рд╣реА. рддреА рдПрдХ clipboard ЁЯУЛ рдШреЗрддреЗ рдЖрдгрд┐ results рдЪреНрдпрд╛ рдЯреЗрдмрд▓рд╛рд╢реЗрдЬрд╛рд░реА рдЙрднреА рд░рд╛рд╣рддреЗ. рдХреЛрдгреАрд╣реА рдПрдЦрд╛рджреЗ рдХрд╛рдо рдХрд░рддрд╛рдЪ, рддреА рддреНрдпрд╛ рдХрд╛рдорд╛рд╕рдореЛрд░ рдПрдХ рдЦреВрдг рдХрд░рддреЗ.
рд╢реЗрд╡рдЯреА clipboard рд╕рд╛рдВрдЧрддреЛ:
- "рдореЛрдареНрдпрд╛ рдпрд╛рджреАрдд рдПрдЦрд╛рджрд╛ рдзрд╛рд╡рдкрдЯреВ рд╢реЛрдзрдгреЗ": 2000 рдЦреБрдгрд╛
- "рдПрдХрд╛ house рдЪреА рд╕рд░реНрд╡реЛрддреНрддрдо рд╡реЗрд│ рд╢реЛрдзрдгреЗ": 4 рдЦреБрдгрд╛
рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ "рдзрд╛рд╡рдкрдЯреВ рд╢реЛрдзрдгреЗ" рдореНрд╣рдгрдЬреЗ 300 рдирд╛рд╡рд╛рдВрдЪреА рдпрд╛рджреА рдПрдХреЗрдХ рдХрд░рдд рдЪрд╛рд│рдгреЗ. рдореНрд╣рдгрдЬреЗ рдЬрд╡рд│рдЬрд╡рд│ 300,000 рдирд╛рд╡рд╛рдВрдЪреНрдпрд╛ рддрдкрд╛рд╕рдгреНрдпрд╛. Printer рдареАрдХ рд╣реЛрддрд╛. рд╢реЛрдзрдгреЗ (lookup) рд╣реАрдЪ рд╕рдорд╕реНрдпрд╛ рд╣реЛрддреА.
рдореНрд╣рдгреВрди рджреАрдкрд┐рдХрд╛ рдПрдХрджрд╛рдЪ bib рдХреНрд░рдорд╛рдВрдХрд╛рдиреБрд╕рд╛рд░ рд▓рд╛рд╡рд▓реЗрд▓реА card box ЁЯЧВя╕П рдмрдирд╡рддреЗ. рдЖрддрд╛ рдкреНрд░рддреНрдпреЗрдХ lookup рдореНрд╣рдгрдЬреЗ рдПрдХ рдЭрдЯрдкрдЯ рдЙрдЪрд▓рдгреЗ. Results рддреЗрдЪ, рдЖрдгрд┐ рд╣рд│реВ рдХрд╛рдо рдирд╛рд╣реАрд╕реЗ рдЭрд╛рд▓реЗ.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
prog["ЁЯПГ results program<br/>300 runners ┬╖ 2000 laps"] --> prof["ЁЯУЛ cProfile<br/>counts every call"]
prof --> hot["find_runner 2000├Ч<br/>297,098 comparisons"]
prof --> cold["best_of 4├Ч ┬╖ results_slow 1├Ч"]
hot --> fix["ЁЯЧВя╕П build a dict by bib once"]
fix --> after["find_runner 0 comparisons<br/>same results: True"]
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрдХреГрддреА + рдПрдХ lab: https://school-edh.pages.dev/performance/lesson-diagrams.html#l03
тЭУ рдХрд╛рдп
- Profiler тАФ program рдЖрдкрд▓рд╛ рд╡реЗрд│ (рдХрд┐рдВрд╡рд╛ memory) рдХреБрдареЗ рдЦрд░реНрдЪ рдХрд░рддреЛ рд╣реЗ рд╕рд╛рдВрдЧрдгрд╛рд░реЗ tool.
- Deterministic (tracing) profiler тАФ рдкреНрд░рддреНрдпреЗрдХ function call рдЖрдгрд┐ return рдиреЛрдВрджрд╡рддреЛ.
cProfileрдЕрд╕рд╛рдЪ рдЖрд╣реЗ. рддреЛ рдЕрдЪреВрдХ call counts рджреЗрддреЛ, рдкрдг рдкреНрд░рддреНрдпреЗрдХ call рд▓рд╛ рдереЛрдбрд╛ рдЦрд░реНрдЪ рдЬреЛрдбрддреЛ, рдореНрд╣рдгреВрди рдЕрдиреЗрдХ рдЫреЛрдЯреНрдпрд╛ calls рдЕрд╕рд▓реЗрд▓рд╛ code рдкреНрд░рддреНрдпрдХреНрд╖рд╛рдкреЗрдХреНрд╖рд╛ рд╣рд│реВ рджрд┐рд╕рддреЛ. - Sampling profiler тАФ program рдЪрд╛ stack рджрд░ second рдЕрдиреЗрдХ рд╡реЗрд│рд╛ рдкрд╛рд╣рддреЛ рдЖрдгрд┐ рддреЛ рдХреБрдареЗ рд╣реЛрддрд╛ рддреЗ
рдореЛрдЬрддреЛ. рдХрдореА overhead, рдЪрд╛рд▓реВ process рд▓рд╛ рдЬреЛрдбрддрд╛ рдпреЗрддреЛ, production рд╕рд╛рдареА рдЪрд╛рдВрдЧрд▓рд╛:
py-spy, Linuxperf, Scalene. рддреНрдпрд╛рдЪреЗ рдЖрдХрдбреЗ рдЕрдВрджрд╛рдЬ рдЖрд╣реЗрдд, рдЕрдЪреВрдХ counts рдирд╛рд╣реАрдд. ncallsтАФ function рдХрд┐рддреА рд╡реЗрд│рд╛ call рдЭрд╛рд▓реЗ (3/1рдореНрд╣рдгрдЬреЗ рдПрдХреВрдг 3 calls, рддреНрдпрд╛рдкреИрдХреА 1 primitive тАФ recursive call рдирд╛рд╣реА).tottimeтАФ function рдЪреНрдпрд╛ рд╕реНрд╡рддрдГрдЪреНрдпрд╛ рдЖрддрд▓рд╛ рд╡реЗрд│.cumtimeтАФ рддреНрдпрд╛рдЪреНрдпрд╛ рдЖрддрд▓рд╛ рдЖрдгрд┐ рддреНрдпрд╛рдиреЗ call рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╕рдЧрд│реНрдпрд╛рдЪрд╛ рд╡реЗрд│. рд╣рд│реВ рд╢рд╛рдЦрд╛ рд╢реЛрдзрд╛рдпрд▓рд╛cumtimeрдиреЗ sort рдХрд░рд╛, рдХрд╛рдо рдХрд░рдгрд╛рд░реЗ function рд╢реЛрдзрд╛рдпрд▓рд╛tottimeрдиреЗ.- Hot spot тАФ code рдЪрд╛ рдЕрд╕рд╛ рдЫреЛрдЯрд╛ рднрд╛рдЧ рдЬрд┐рдереЗ рдмрд╣реБрддреЗрдХ рд╡реЗрд│ рдЬрд╛рддреЛ. рдмрд▒реНрдпрд╛рдЪрджрд╛ рдПрдХ рдХрд┐рдВрд╡рд╛ рджреЛрди functions рдЪ рдмрд╣реБрддреЗрдХ рдЦрд░реНрдЪ рдЕрд╕рддрд╛рдд, рдореНрд╣рдгреВрди profile рд╕рд╛рдВрдЧрддреЛ рдХреА рдХреЛрдгрддрд╛ рдПрдХ рдмрджрд▓ рдорд╣рддреНрддреНрд╡рд╛рдЪрд╛ рдЖрд╣реЗ.
- Flame graph тАФ рдЕрдиреЗрдХ stack samples рдЪреЗ рдЪрд┐рддреНрд░: рдкреНрд░рддреНрдпреЗрдХ box рдПрдХ function рдЖрд╣реЗ, рддреНрдпрд╛рдЪреА рд░реБрдВрджреА рдореНрд╣рдгрдЬреЗ рддреЛ stack рд╡рд░ рдХрд┐рддреА рд╡реЗрд│ рд╣реЛрддрд╛. рд╡рд░рдЪреНрдпрд╛ рдмрд╛рдЬреВрдЪреЗ рд░реБрдВрдж boxes рд╣реЗ hot spots рдЖрд╣реЗрдд.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг рд▓реЛрдХрд╛рдВрдЪреЗ рдЕрдВрджрд╛рдЬ рдЪреБрдХрддрд╛рдд. рд╣рд│реВ рднрд╛рдЧ рдХреНрд╡рдЪрд┐рддрдЪ рддрд┐рдереЗ рдЕрд╕рддреЛ рдЬрд┐рдереЗ рддреЛ рд╣рд│реВ "рджрд┐рд╕рддреЛ", рдЖрдгрд┐ 2% рд╡реЗрд│ рдШреЗрдгрд╛рд░рд╛ code рдЬрд▓рдж рдХреЗрд▓реНрдпрд╛рдиреЗ рдЬрд╛рд╕реНрддреАрдд рдЬрд╛рд╕реНрдд 2% рдлрд╛рдпрджрд╛ рд╣реЛрдК рд╢рдХрддреЛ. рдЖрдзреА profiling рдХреЗрд▓реНрдпрд╛рдиреЗ "рд╣реЗ рдЬрд▓рдж рдХрд░рд╛" рдЪреЗ рд░реВрдкрд╛рдВрддрд░ "рд╣реЗ function рдЬрд▓рдж рдХрд░рд╛" рдордзреНрдпреЗ рд╣реЛрддреЗ, рдЖрдгрд┐ рдордЧ рдзрдбрд╛ 02 рдЪрд╛ benchmark рддреЗ рдХрд╛рдо рдЭрд╛рд▓реЗ рдХрд╛ рддреЗ рд╕рд╛рдВрдЧрддреЛ.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
perf/sim.py рдордзрд▓реЗ sports_day() 300 рдзрд╛рд╡рдкрдЯреВ (shuffled рдХреНрд░рдорд╛рдиреЗ) рдЖрдгрд┐
2000 laps рдмрдирд╡рддреЗ. results_slow рдкреНрд░рддреНрдпреЗрдХ lap рдЪрд╛ рдзрд╛рд╡рдкрдЯреВ find_runner рдиреЗ рд╢реЛрдзрддреЗ, рдЬреЗ рдпрд╛рджреА рдЪрд╛рд│рддреЗ
рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ comparison COUNT["compares"] рдордзреНрдпреЗ рдореЛрдЬрддреЗ. results_fast рдПрдХрджрд╛рдЪ bib рдиреБрд╕рд╛рд░ dict
рдмрдирд╡рддреЗ. profile_calls(fn, ...) function рдЦрд▒реНрдпрд╛ cProfile рдЦрд╛рд▓реА рдЪрд╛рд▓рд╡рддреЗ рдЖрдгрд┐ рдпрд╛
module рдЪреА functions рддреНрдпрд╛рдВрдЪреНрдпрд╛ ncalls рд╕рд╣ рдкрд░рдд рджреЗрддреЗ (рддреЗ comprehension helpers рд╡рдЧрд│рддреЗ, рдЬреЗ рдирд╡реНрдпрд╛ Python
versions рд╕реНрд╡рддрдВрддреНрд░ functions рдореНрд╣рдгреВрди рджрд╛рдЦрд╡рдд рдирд╛рд╣реАрдд).
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 perf/demo.py profile
python3 - <<'EOF'
import sys; sys.path.insert(0, "perf"); import sim
runners, laps = sim.sports_day()
for n_laps in (500, 1000, 2000):
sim.COUNT["compares"] = 0
_, rows = sim.profile_calls(sim.results_slow, runners, laps[:n_laps])
print(f"{n_laps:>4} laps тЖТ find_runner {dict(rows)['find_runner']:>4}├Ч ┬╖ {sim.COUNT['compares']:>7,} comparisons")
EOF
python3 -c "import cProfile, sys; sys.path.insert(0, 'perf'); import sim; r, l = sim.sports_day(); cProfile.run('sim.results_slow(r, l)', sort='ncalls')" | head -14
рд╢реЗрд╡рдЯрдЪрд╛ command cProfile рдЪреЗ рд╕рдВрдкреВрд░реНрдг table рдЫрд╛рдкрддреЛ. рддреНрдпрд╛рдЪрд╛ ncalls column
find_runner рд╕рд╛рдареА 2000 рд╕рд╛рдВрдЧрддреЛ; рддреНрдпрд╛рдЪреЗ рд╡реЗрд│реЗрдЪреЗ columns (tottime, cumtime, рдЖрдгрд┐ рдкрд╣рд┐рд▓реНрдпрд╛ рдУрд│реАрддрд▓реА рдПрдХреВрдг рд╡реЗрд│)
рдЦрд▒реНрдпрд╛ рдШрдбреНрдпрд╛рд│рд╛рдЪреНрдпрд╛ рд╡реЗрд│рд╛ рдЖрд╣реЗрдд тАФ рддреБрдордЪреЗ рдЖрдХрдбреЗ рд╡реЗрдЧрд│реЗ рдЕрд╕рддреАрд▓.
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
profile рд╣реЗ рдЫрд╛рдкрддреЗ:
тФАтФА Dipika profiles the results program with cProfile: 300 runners, 2000 laps (we read CALL COUNTS, not times)
find_runner 2000├Ч ┬╖ best_of 4├Ч ┬╖ results_slow 1├Ч
find_runner walks the runner list every time: 297,098 comparisons for 2000 lookups тАФ the hot spot
тФАтФА fix: build a dict by bib once тЖТ best_of 4├Ч ┬╖ results_fast 1├Ч
find_runner comparisons now 0 ┬╖ same results: True ┬╖ best 100 m per house (ms) {'blue': 11000, 'green': 11006, 'red': 11006, 'yellow': 11005}
рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ:
500 laps тЖТ find_runner 500├Ч ┬╖ 74,124 comparisons
1000 laps тЖТ find_runner 1000├Ч ┬╖ 149,300 comparisons
2000 laps тЖТ find_runner 2000├Ч ┬╖ 297,098 comparisons
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рдПрдХрд╣реА рдЕрдВрджрд╛рдЬ рди рдХрд░рддрд╛ profile рдиреЗ рджреЛрд╖реА рд╢реЛрдзрд▓рд╛: find_runner, 2000 calls, рд╕реБрдорд╛рд░реЗ
рдкреНрд░рддреНрдпреЗрдХреА 149 comparisons (рд╕рд░рд╛рд╕рд░реА 300 рдЪреЗ рдирд┐рдореНрдореЗ). рджреБрдкреНрдкрдЯ laps, рджреБрдкреНрдкрдЯ рдХрд╛рдо тАФ рдореНрд╣рдгрдЬреЗ
20,000 laps рдЕрд╕рд▓реЗрд▓реНрдпрд╛ results рджрд┐рд╡рд╢реА рд╕реБрдорд╛рд░реЗ 3 million comparisons рд╣реЛрддреАрд▓. рдЙрдкрд╛рдпрд╛рдиреЗ рдПрдХ data
structure рдмрджрд▓рд▓реЗ рдЖрдгрд┐ 0 рдпрд╛рджреА-рдЪрд╛рд│рдгреНрдпрд╛рдВрд╕рд╣ рддреЗрдЪ results рджрд┐рд▓реЗ. рд╣реЗрдЪ рдпрд╛ рд╕рдВрдкреВрд░реНрдг
рдХреЛрд░реНрд╕рдЪреЗ рдЪрдХреНрд░ рдЖрд╣реЗ: рдореЛрдЬрд╛ тЖТ profile рдХрд░рд╛ тЖТ рдПрдХ рдЧреЛрд╖реНрдЯ рдмрджрд▓рд╛ тЖТ рдкреБрдиреНрд╣рд╛ рдореЛрдЬрд╛.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- profiling рдХрд░рдгреНрдпрд╛рдЖрдзреАрдЪ optimise рдХрд░рдгреЗ ("рдорд▓рд╛ рдЦрд╛рддреНрд░реА рдЖрд╣реЗ, рдЫрдкрд╛рдИрдЪ рдЖрд╣реЗ")
main()рдЪрд╛cumtimeрд╡рд╛рдЪреВрди "main рд╣рд│реВ рдЖрд╣реЗ" рдЕрд╕рд╛ рдирд┐рд╖реНрдХрд░реНрд╖ рдХрд╛рдврдгреЗ тАФ рдЦрд╛рд▓реА,tottimeрдХрдбреЗ рдкрд╛рд╣рд╛- рд▓рд╛рдЦреЛ рдЫреЛрдЯреНрдпрд╛ calls рдЕрд╕рд▓реЗрд▓реНрдпрд╛ code рд╕рд╛рдареА
cProfileрдЪреНрдпрд╛ рд╡реЗрд│рд╛рдВрд╡рд░ рд╡рд┐рд╢реНрд╡рд╛рд╕ рдареЗрд╡рдгреЗ тАФ рддреНрдпрд╛рдЪрд╛ overhead рддреНрдпрд╛ рдлреБрдЧрд╡рддреЛ - рдЫреЛрдЯреНрдпрд╛ test input рд╡рд░ profiling рдХрд░рдгреЗ, рдЬрд┐рдереЗ рдЦрд▒реНрдпрд╛ input рдЪрд╛ hot spot рдХрдзреАрдЪ рджрд┐рд╕рдд рдирд╛рд╣реА
- рдлрдХреНрдд laptop рд╡рд░ profiling рдХрд░рдгреЗ; production рдордзреНрдпреЗ рд╡реЗрдЧрд│рд╛ hot spot рдЕрд╕реВ рд╢рдХрддреЛ (data, cache, load)
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ machine рд╡рд░ тАФ рдПрдХрд╛ script рдЪреЗ profile рдХрд░рд╛ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ function рдордзреНрдпреЗ рдЧреЗрд▓реЗрд▓реНрдпрд╛ рд╡реЗрд│реЗрдиреБрд╕рд╛рд░ sort рдХрд░реВрди result рдЙрдШрдбрд╛:
python3 -m cProfile -o results.prof make_results.py
python3 -c "import pstats; pstats.Stats('results.prof').sort_stats('cumtime').print_stats(15)"
py-spy рдЖрдзреАрдЪ рдЪрд╛рд▓реВ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ process рдЪреЗ samples рдШреЗрддреЛ, code рди рдмрджрд▓рддрд╛ рдЖрдгрд┐ рдХрдореА
overhead рд╕рд╣ (рддреНрдпрд╛рд▓рд╛ sudo рд▓рд╛рдЧреВ рд╢рдХрддреЛ):
py-spy top --pid 12345 # a live 'top' of Python functions
py-spy record -o flame.svg --pid 12345 --duration 30 # a flame graph
py-spy dump --pid 12345 # every thread's stack, right now
Linux perf native code рд╕реБрджреНрдзрд╛ рдкрд╛рд╣рддреЛ; Python 3.12 рдкрд╛рд╕реВрди, CPython -X perf рд╡рд╛рдкрд░реВрди perf рдордзреНрдпреЗ Python function
рдЪреА рдирд╛рд╡реЗ рджрд╛рдЦрд╡реВ рд╢рдХрддреЛ:
perf record -g -- python3 -X perf make_results.py
perf report
ЁЯПн Production рдордзреНрдпреЗ рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ: рдЦрд▒реНрдпрд╛ service рдЪреЗ profile рдХрд░рдгреНрдпрд╛рдЪрд╛ рдорд╛рд░реНрдЧ рдареЗрд╡рд╛ тАФ pod рд╡рд░ py-spy, continuous profiler, рдХрд┐рдВрд╡рд╛ auth рдорд╛рдЧреЗ profiling endpoint. рдЦрд▒реНрдпрд╛ traffic рдЖрдгрд┐ рдЦрд▒реНрдпрд╛ data рдЦрд╛рд▓рдЪрд╛ hot spot рд╣рд╛рдЪ рджреБрд░реБрд╕реНрдд рдХрд░рдгреНрдпрд╛рд╕рд╛рд░рдЦрд╛ рдЕрд╕рддреЛ.
тПня╕П рдкреБрдвреЗ
find_runner рдиреЗ рдЕрд╕реЗ рдХрд╛рдо рдХреЗрд▓реЗ рдЬреЗ рдпрд╛рджреАрдЪреНрдпрд╛ рдЖрдХрд╛рд░рд╛рд╕реЛрдмрдд рд╡рд╛рдврдд рд╣реЛрддреЗ тАФ рдкреНрд░рддреНрдпреЗрдХ lap рд╕рд╛рдареА. рд╣рд╛
complexity рдЪрд╛ рдкреНрд░рд╢реНрди рдЖрд╣реЗ. рдкреБрдвреЗ: O(n┬▓) рд╡рд┐рд░реБрджреНрдз O(n log n), рдореЛрдЬреВрди.
git checkout lesson-04-complexity