๐ ํ์ด์ฌ์ ๋์์ฑ
ํ์ด์ฌ์์๋ ์ฐ๋ ๋๋ฅผ ์ฌ์ฉํ๋ threading ๋ชจ๋, ํ์คํฌ๋ฅผ ์ฌ์ฉํ๋ asyncio ๋ชจ๋, ๊ทธ๋ฆฌ๊ณ ํ๋ก์ธ์ค๋ฅผ ์ฌ์ฉํ๋ multiprocessing ๋ชจ๋์ด ์๋๋ฐ ์ฌ๊ธฐ์ ๋ณ๋ ฌ์ ์ผ๋ก ์์ ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ ์ค์ ๋ก multiprocessing ๋ฟ์ด๋ค.
threading ๊ณผ asyncio ๋ชจ๋์ ๋ชจ๋ ํ๋์ ํ๋ก์ธ์๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํ ๋ฒ์ ํ๋์ฉ ์คํํ ์๋ฐ์ ์๋ค. ์๋ก context switching ๋๋ฉด์ ๋์์ ์คํ๋๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ฒ ๋๋ ๊ฒ์ด๋ค. ์ด๋ ๊ฒ ํด์ ์์ ์๊ฐ์ ์ค์ธ๋ค.
๐ threading๊ณผ asyncio ์ฐจ์ด(concurrency)
ํ์ด์ฌ์์ ๋์์ ์ธ ์์ ์ ํ ๋ ์ฌ์ฉํ๋ threading๊ณผ asyncio์ ์ฐจ์ด์ ์ threading์ ๋ชจ๋์์ ์คํ๋๊ณ ์๋ ๊ฐ ์ฐ๋ ๋๋ฅผ ์ด์์ฒด์ ๊ฐ ์ธ์ ๋ ์ง ๋ฉ์ถ๊ณ ๋ค๋ฅธ ์ฐ๋ ๋๋ฅผ ์งํ์ํฌ ์ ์๋ ๊ฒ์ด๋ค. ์ด์์ฒด์ ๊ฐ ์ค๋ ๋๋ฅผ ์ ์ ํ ์ ์๊ธฐ ๋๋ฌธ์, ์ด๊ฑธ ์ ์ ํ ๋ฉํฐํ์คํน(pre-emptive multitasking) ์ด๋ผ๊ณ ํ๋ค.
๋ฐ๋ฉด์ asyncio ๋ชจ๋์ ํ๋ ฅ์ ๋ฉํฐํ์คํน(cooperative multitasking) ๋ฐฉ์์ ์ฌ์ฉํ๋ค. ์ด ๋ฐฉ์์ ๊ฐ ํ์คํฌ๊ฐ ์ง์ ์ธ์ ์ค์์นญ๋ ์ค๋น๊ฐ ๋์๋์ง ๋ช ์ํด์ฃผ์ด์ผ ํ๋ค. ์ด ๋ฐฉ์์ ์ฅ์ ์ ์ธ์ ๋ค๋ฅธ ํ์คํฌ๋ก ๋์ด๊ฐ์ง๋ฅผ ์ฝ๋๋ฅผ ์ง๋ ์ฌ๋์ด ์ ํ ์ ์๋ ๊ฒ์ด๋ค.
๐ ๋ณ๋ ฌ ์์ ์ ๊ฐ๋ฅํ๊ฒ ํ๋ Multiprocessing(parallelism)
threading๊ณผ asyncio๋ ํ ํ๋ก์ธ์์์ ์ด๋ฃจ์ด์ง๋ ๋์์ฑ์ด์๋ค. multiprocessing ๋ชจ๋์ ์ฌ์ฉํ๋ฉด, ํ์ด์ฌ์ ์์ ํ ์๋ก์ด ํ๋ก์ธ์๋ฅผ ์์ฑํ๋ค. ์ฆ ์์ ํ ๊ฐ์ ์๊ฐ์ ๋ณ๋ ฌ์ ์ผ๋ก ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํด์ง๋ ๊ฒ์ด๋ค.
๐ง ๋์์ฑ์ ์ข ๋ฅ I/O bound, CPU bound
I/O bound ์์ ์ ์๋ฏธ๋ ์ ์ถ๋ ฅ ์์ ์ด ์ฃผ๊ฐ ๋๋ค๋ ๊ฒ์ด๋ค. I/O bound๋ผ๊ณ ํด์ cpu ์์ ์ด ์ด๋ฃจ์ด์ง์ง ์๋ ๊ฒ์ ์๋๋ค. ์ฆ I/O waiting ์๊ฐ์ด ๋ง์ ๊ฒฝ์ฐ๋ค. ํ์ผ ์ฐ๊ธฐ, ๋์คํฌ ์์ , ๋๋น ์ ๊ทผ, ๋คํธ์ํฌ ํต์ ์ ํ ๋ ์ฃผ๋ก ๋ํ๋๋ฉฐ ์์ ์ ์ํ ๋ณ๋ชฉ์ ์ํด ์์ ์๋๊ฐ ๊ฒฐ์ ๋๋ค
๊ทธ๋ ๋ค๋ฉด CPU bound ์์ ์ ์๋ฏธ๋ I/O bound ์์ ์ ์๋ฏธ์ ๋ฐ๋๋ก ์๊ฐํ๋ฉด ๋๋ค. CPU ์์ ์ด ์ฃผ๋ฅผ ์ด๋ฃจ๋ ๊ฒ์ด๋ค. ์ฃผ๋ก ์ฐ์ฐ์ ํ ๋ ๋ํ๋๋ฉฐ CPU ์ฑ๋ฅ์ ์ํด ์์ ์๋๊ฐ ๊ฒฐ์ ๋๋ค.
๋ฐ๋ผ์ ์ ์ ํ๊ฒ ์์ ์ด I/O ์ค์ฌ์ธ ์ง CPU ์์ ์ค์ฌ์ธ ์ง ํ์ ํ ํ์๊ฐ ์๋ค. ์ด์ ๊ด๋ จํด์ ๊ฐ๋จํ๊ฒ ํ ์คํธ๋ฅผ ์งํํด๋ณด์๋ค.
๐ FastAPI์ Flask ๋น๊ต๋ฅผ ํตํ I/O bound์ CPU bound ์์ ๋์์ฑ ํ ์คํธ
๊ธฐ๋ณธ์ ์ผ๋ก 10๊ฐ์ request๋ฅผ ํ ๋ฒ์ ๋ณด๋ด๋ ์ค์ ์ผ๋ก ํ ์คํธ๋ฅผ ์งํํ๋ค. ์ฑ๋ฅ ํ ์คํธ ๋๊ตฌ๋ Apache Jmeter๋ฅผ ์ฌ์ฉํ๋ค.
I/O bound test
ํ ์คํธํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค. ๋ชจ๋ ํ ์คํธ๋ ๋์ปค๋ก ์งํํ๋ค.
flask ์ฝ๋
from flask import Flask
import os
import datetime
import time
@app.route('/test-flask', methods=['GET'])
def test():
print(f'start-time - {datetime.datetime.now()}')
time.sleep(10)
print(f'finish-time - {datetime.datetime.now()}')
return 'Done!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
fastAPI ์ฝ๋
from fastapi import FastAPI
import uvicorn
import os
import datetime
import time
@app.get('/test-fast')
def test():
print(f'start-time - {datetime.datetime.now()}')
time.sleep(10)
print(f'finish-time - {datetime.datetime.now()}')
return 'Done!'
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
* Flask - gunicorn ์ค์ : worker 1, thread 5 ๊ฐ์ธ ๊ฒฝ์ฐ *
20์ด๊ฐ ์์๋์๋ค. thread๊ฐ 5๊ฐ์ด๊ธฐ ๋๋ฌธ์ request 5 ๊ฐ๋ฅผ ์ฒ๋ฆฌํ ํ ์ด์ request๊ฐ ์ข ๋ฃ๋๋ ๋๋ก ๋๋จธ์ง 5๊ฐ์ request๊ฐ ์คํ๋์๋ค. thread๋ฅผ 10๊ฐ๋ก ๋๋ฆฌ๋ฉด 10๊ฐ์ request๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ 11์ด์ ์ข ๋ฃ๋๋ค. ๋ก๊ทธ์์ ๋ณด๋ค์ํผ ์ฒ์ request์ start-time 5๊ฐ๊ฐ ์คํ์ด ๋๊ณ finsih-time์ด ์ฐํ๋ฉฐ ๋๋๋ฉด์ ๋ค๋ฅธ start-time ์ด ์์๋์๋ค.
*FastAPI - gunicorn with uvicorn ์ค์ : worker 1, thread 5๊ฐ์ธ ๊ฒฝ์ฐ *
11์ด๊ฐ ์์๋์๋ค. fastAPI๋ ๊ธฐ๋ณธ์ ์ผ๋ก async def๋ฅผ ์ฐ์ง ์์๋ def๋ง์ผ๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ๋๋๋ก FastAPI ํ๋ ์์ํฌ๋ก ๊ตฌํํ๊ณ ์๋ค. ์ ์์ ์ด ํ์ฌ time.sleep(10) ์ ์ฌ์ฉํด์ CPU ๊ฐ ์ฌ๊ณ ์๋ I/O bound ์์ ์ด์ด์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ๋์๊ณ ์๋๋ Flask๋ฅผ ์ฌ์ฉํด์ ๋๊ธฐ์ ์ผ๋ก ์คํํ์ ๋๋ณด๋ค ์๊ฐ์ด ์ ๋ฐ์ด ์ค์ด๋ ๊ฒ์ด๋ค. thread๋ฅผ 10๊ฐ๋ก ๋๋ ค๋ 11์ด๋ก ๋์ผํ๋ค. ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์ผ๋ง๋ I/O bound ์์ ์์ ์ฑ๋ฅ์ ๋์ฌ์ฃผ๋์ง ํ์ธํ ์ ์์๋ค.
๋ก๊ทธ์์ ๋ณด๋ค์ํผ ์์ flask๋ฅผ ์ฌ์ฉํ ๋ก๊ทธ์๋ ๋ค๋ฅด๊ฒ request์์์ธ start-time์ด 10๊ฐ๊ฐ ์ฐํ ํ finish-time 10๊ฐ๊ฐ ์์๋๋ก ์ฐํ ๊ฒ์ ๋ณผ ์ ์๋ค.
CPU bound test
์์ I/O bound ํ ์คํธ์๋ ๋ค๋ฅด๊ฒ ๊ณ์ฐ์์ ๋ฃ์ด์ ์ฐ์ฐ ์์ ์ ํ๋ ํ ์คํธ ์ฝ๋๋ฅผ ์งฐ๋ค.
flask ์ฝ๋
from flask import Flask
import os
import datetime
@app.route('/test-flask', methods=['GET'])
def test():
print(f'start-time - {datetime.datetime.now()}')
num_list = list(range(10000000))
square_sum = sum(x**2 for x in num_list)
print(f'finish-time - {datetime.datetime.now()}')
return 'Done!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=os.environ.get('PORT', 8080)))
fastAPI ์ฝ๋
from fastapi import FastAPI
import uvicorn
import os
import datetime
@app.get('/test-fast')
def test():
print(f'start-time - {datetime.datetime.now()}')
num_list = list(range(10000000))
square_sum = sum(x**2 for x in num_list)
print(f'finish-time - {datetime.datetime.now()}')
return 'Done!'
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
* Flask - gunicorn ์ค์ : worker 1, thread 10 ๊ฐ์ธ ๊ฒฝ์ฐ *
25์ด๊ฐ ์์๋์๋ค. thread๊ฐ 10๊ฐ์ด๊ธฐ ๋๋ฌธ์ 10๊ฐ์ request๋ฅผ ๋์์ ์ฒ๋ฆฌํ๋ค.
* Flask - gunicorn ์ค์ : worker 2, thread 5 ๊ฐ์ธ ๊ฒฝ์ฐ *
worker์ ๊ฐ์๋ฅผ ๋๋ฆฌ๋ thread๋ฅผ ์ค์ฌ๋ ์๊ฐ์ด 14์ด๋ก ์ค์๋ค. ์ฆ ์ฐ์ฐ ์์ ์ธ CPU Bound ์์ ์์๋ worker์ ๊ฐ์๋ฅผ ๋๋ ค์ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ์งํํ ์ ์๋ ๊ฒ์ด๋ค. ์์ ๋ก๊ทธ์๋ ๋ค๋ฅด๊ฒ start-time๊ณผ finsih-time์ด ๋ฒ๊ฐ์ ์ฐํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. worker process 2๊ฐ๊ฐ ๋์์ ์คํ๋๊ณ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋ค์์ผ๋ก๋ fastAPI๋ฅผ ์ด์ฉํ์ฌ ์งํํด๋ณด๊ฒ ๋ค.
*FastAPI - gunicorn with uvicorn ์ค์ : worker 1, thread 10๊ฐ์ธ ๊ฒฝ์ฐ *
27์ด๊ฐ ์์๋์๋ค. ๋น๋๊ธฐ ์์ ์ CPU ์ฐ์ฐ ์์ฃผ์ ์์ ์ ํ์ง ์๊ธฐ ๋๋ฌธ์ FastAPI๋ฅผ ์ด์ฉํด๋ Flask์ ๋ค๋ฅผ ๋ฐ๊ฐ ์๋ค.
*FastAPI - gunicorn with uvicorn ์ค์ : worker 2, thread 5๊ฐ์ธ ๊ฒฝ์ฐ *
17์ด๊ฐ ์์๋์๋ค. ์ญ์ worker์ ๊ฐ์๋ฅผ ๋๋ฆฌ๋ ๋ณ๋ ฌ ํ๋ก๊ทธ๋๋ฐ์ ํตํด worker๊ฐ 1๊ฐ์์ ๋๋ณด๋ค ์ฒ๋ฆฌ ์๋๊ฐ ๋นจ๋ผ์ก๋ค.
๐ ์ ๋ฆฌํ๋ฉด์..
์ด๋ก์จ ํ์ด์ฌ์ ๋์์ฑ ๊ฐ๋ ์ ๋ํด ์ข ๋ ๋ช ํํด์ง ๊ฒ ๊ฐ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ด์ฌ์ GIL(Global Interpreter Lock) ์ ์ฑ ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํ๋์ ์ค๋ ๋๋ง ์ฌ์ฉํ ์ ์๋ค. ๊ทธ๋ฌ๋ ์ด๊ฒ์ CPU Bound ์์ ์๋ง ํด๋น๋๊ณ I/O Bound ์์ ์์๋ ๋ด๋ถ์ ์ผ๋ก GIL์ ํด์ ํ๋ค๊ณ ํ๋ค. ๋ฐ๋ผ์ ํ์ด์ฌ์์ ์ฐ์ฐ์ ์ด์ฉํด์ ๋์์ฑ์ ํจ๊ณผ๋ฅผ ๋ณด๊ธฐ ์ํด์๋ Multiprocessing ์ ์ด์ฉํด์ผ ํ๋ค.
๋ค์์๋ CPU bound์ธ ์ฐ์ฐ ์์ฃผ์ ์์ ์์ Multiprocessing ์ ์ด์ฉํ์ฌ ์ฐ์ฐ์ ์๋๋ฅผ ๋์ธ ์์ ๋ฅผ ์ ์ด๋ด์ผ๊ฒ ๋ค. ์ฌ์ค ๋์ผํ ํ ์คํธ๋ฅผ M1 mac pro ์ Intel mac pro(32GB) ๋ก Multiprocessing์ ์ด์ฉํ์ง ์๊ณ (Cpu core 1๊ฐ) ํ ์คํธํ๋๋ฐ ๋์ผํ ์ฐ์ฐ์ด M1 mac pro์์๋ 3-4๋ถ ๋ง์ ๋๋ฌ๊ณ Intel mac pro์์๋ 7-8๋ถ ๊ฑธ๋ ธ๋ค.
์ง์ง M1 ์นฉ์ด ์ผ๋ง๋ ๋๋จํ ์ง ํ ๋ฒ ๋ ๋๊ผ๋ค...
'Language > Python' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Python] ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ | LIM (0) | 2022.07.09 |
---|---|
[Python] functools ์ partial | LIM (0) | 2022.07.09 |
[Python] ์์ธ์ฒ๋ฆฌ try, except, finally and raise | LIM (0) | 2022.06.24 |
[Python] ์ถ์ ๋ฉ์๋ ABC์ ์ ์์ ์ฌ์ฉ | LIM (0) | 2022.06.19 |
[Python] Thread & Lock ( ์ฐ๋ ๋์ ๋ฝ ) | LIM (0) | 2022.06.15 |
๋๊ธ