F-Lab
🚀
취업/이직이 고민이신가요? 합격에 필요한 모든 것을 도와드립니다.
🚀
취업/이직이 고민이신가요? 합격에 필요한 모든 것을 도와드립니다.

Gevent로 Python 어플리케이션 성능 개선하기

writer_thumbnail

F-Lab : 상위 1% 개발자들의 멘토링

📌 글 작성

F-Lab 백엔드 멘토 Jacob
팀 문화와 엔지니어 생산성에 관심이 많은 실리콘밸리 백엔드 개발자
 

 


 

Python 은 다양한 영역에서 사용되는 언어이지만 다른 언어에 비해 느리다는 인식이 퍼져 있습니다. 그 이유로는 아래와 같은 특징들을 들 수 있습니다.

 

  • 인터프리터 언어이기 때문에 런타임에 bytecode를 생성합니다. 컴파일러 언어에 비해서 런타임에 동일한 행위를 하기 위해서 처리해야 하는 일이 더 많습니다.
  • 가비지 콜렉터의 존재로 인해 CPU 사이클을 소모하게 됩니다. 또 가비지 콜렉터는 언제 실행될지 알기 어렵습니다.
  • 동적 타이핑으로 인해서 Python 인터프리터가 namespace lookup을 하는데 더 많은 노력이 들어갑니다.

 

위와 같은 이유 때문에 Python으로 작성한 프로그램은 C 나 C++로 짜여진 동일한 행위를 하는 프로그램에 비해서 10배, 심지어는 100배가량도 느릴 수 있습니다. 하지만 그것이 Python 이 충분히 빠르지 않다는 의미는 아닙니다.

 

Python 이 느리고 Python 프로그램을 프로덕션 환경에서 사용하기 위해서는 많은 최적화를 필요로 할 것이라고 생각하셨다면 이 블로그 글을 일독하시는 걸 권해드립니다. 이 글은 파이썬의 성능상의 제약에도 불구하고 그것이 서비스의 주요 병목이 될 확률은 몹시 적다고 말하고 있습니다. 아래는 제가 좋아하는 Coding Horror 블로그의 글 The Infinite Space Between Words에서 발췌한 표입니다.

 

 

1 CPU 사이클에 필요한 시간을 1초라고 가정했을 때 메인 메모리에 접근하는데 필요한 시간은 6분 남짓, 디스크에 접근하는데 필요한 시간은 일에서 월 단위로 늘어납니다. 네트워크를 통해 자원에 접근하는 것은 심지어 더 오래 걸립니다. 앞서 가정한 CPU 사이클당 1초가 걸린다는 가정을 기반으로 계산하면 샌프란시스코에서 뉴욕에 있는 데이터센터에 접근하는 데 필요한 시간은 4년이나 걸립니다. 한 번의 네트워크 왕복은 2억 5천만 건의 CPU cycle에 맞먹습니다.

 

그러면 우리가 작성하는 Python 프로그램을 빠르게 할 수 있는 가장 쉬운 방법은 무엇일까요? 디스크/네트워크에 읽고 쓰는 행위에 드는 시간을 줄이면 프로그램이 명백하게 빨라질 것 같습니다. 여기에서 MDN에서 발췌한 비동기 프로그래밍에 대한 설명을 확인하도록 하겠습니다.

비동기 프로그래밍은 작업이 완료될 때까지 기다리지 않고 잠재적으로 오래 실행되는 작업을 시작하여 해당 작업이 실행되는 동안에도 다른 이벤트에 응답할 수 있게 하는 기술입니다. 작업이 완료되면 프로그램이 결과를 제공합니다.

 

디스크/네트워크에 읽고 쓰는 동안 Python 인터프리터가 기다리지 않고 다른 동작을 할 수 있다면 디스크/네트워크에 읽고 쓰는 행위에 드는 시간을 줄일 수 있습니다. 파이썬은 비동기 프로그래밍을 위한 여러가지 라이브러리를 제공합니다. 그중 오늘 소개해 드릴 라이브러리는 Gevent 입니다.

Gevent는 코루틴 기반의 Python 네트워킹 라이브러리입니다. Gevent는 libev 또는 libuv 이벤트 루프 위에서 동기적인 API를 제공하기 위해서 Greenlet을 사용합니다.

 

위의 설명에서 나온 핵심 단어들을 전부 이해하지 못해도 괜찮습니다. 핵심은 Gevent가 어떤 일을 하는지 이해하는 것입니다. Gevent는 우리가 일상적으로 사용하는 동기적인 API를 제공하고 내부 구현체가 요청들을 비동기적으로 처리합니다. 다시 말해서 Gevent 라이브러리에서 제공되는 socket() 과 같은 함수들을 이용하면 Python 인터프리터가 네트워크 요청을 보내고 기다릴 필요 없이 다음 명령을 실행할 수 있게 됩니다.

 

import gevent
import socket

def arbitrary_socket_call():
    _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    _socket.connect(('www.google.com', 80))
    _socket.send(b"GET / HTTP/1.1\r\nHost:www.google.com\r\n\r\n")
    response = _socket.recv(4096)
    print(response)

def run_many_socket_calls():
    for _ in 1000:
        arbitrary_socket_call()


# gevent.socket 을 사용함으로써 동일한 결과를 내는 코드가 더 빠르게 실행된다 
# gevent.socket 은 blocking I/O가 발생하면 그것을 기다리는 동안 다른 작업을 처리할 수 있게 해준다.
from gevent import socket

def arbitrary_socket_call():
    _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    _socket.connect(('www.google.com', 80))
    _socket.send(b"GET / HTTP/1.1\r\nHost:www.google.com\r\n\r\n")
    response = _socket.recv(4096)
    print(response)

def run_many_socket_calls():
    jobs = [gevent.spawn(arbitrary_socket_call) _ for 1000]
    gevent.joinall(jobs)

 

 

그렇다면 Gevent를 쓰지 않는 서드 파티 라이브러리들의 경우에는 성능 향상을 누리기 어려울까요? Gevent는 monkey patch라는 기능을 통해서 Python 표준 라이브러리들을 Gevent 라이브러리가 제공하는 구현체로 대체 할 수 있습니다. 이 기능을 이용하면 여러분이 사용하는 서드 파티 라이브러리의 코드도 성능 향상이 가능합니다.

 

import gevent
from gevent import monkey

monkey.patch_all() # 이 함수 호출 이후로 import 되는 모듈에서 gevent가 지원하는 I/O 관련 함수는 모두 gevent 모듈의 비동기 함수를 사용하게 된다

import requests

TARGET_URL_LIST = [
'www.google.com',
'www.youtube.com',
'www.facebook.com',
'www.twitter.com',
'www.instagram.com',
'www.wikipedia.com',
'www.yahoo.com',
'www.live.com',
'www.reddit.com',
'www.netflix.com',
'www.linkedin.com',
'www.office.com',
'www.bing.com',
'www.quora.com',
'www.ebay.com',
]

def send_request(url: str):
    content = requests.get(url).content
    print(content)

def run_concurrent():
    jobs = [gevent.spawn(send_request, url) for url in TARGET_URL_LIST]
    gevent.joinall(jobs)

 

 

물론 Gevent도 만능이 아닙니다. 우선 위에서 언급한 monkey patch는 순수하게 Python으로 작성된 코드에 한해서 지원됩니다. 만약 서드 파티 모듈이 다른 언어로 작성되었다면 성능 향상을 누리기 위해서는 Gevent를 지원하는지 확인하는 것이 필요합니다. 

 

또 Gevent 라이브러리를 사용하는 경우 thread-safe 한 코드 작성이 필요합니다. 하지만 그럼에도 불구하고 Gevent를 현업에서 도입하는 것은 만족스러운 경험이었습니다. 

 

저의 경우에는 Python으로 작성된 레거시 백엔드 어플리케이션에서 스케일링 문제가 발생해서 그것을 해결하는 과정에서 Gevent를 도입한 경험이 있는데 어플리케이션 서버의 성능을 10배까지 끌어올릴 수 있었고 클라우드 비용을 유의미하게 절감하는 성과를 낼 수 있었습니다. 

 

동시에 Gevent가 제공하는 monkey patch를 이용해서 코드 변경을 최소화하고 함께 일하는 엔지니어들이 새로운 기술 도입 과정에서 들이는 노력도 많이 아낄 수 있었습니다. 여러분도 Gevent , 또는 다른 비동기 프로그래밍 라이브러리를 이용해서 Python 어플리케이션 성능을 개선해 보시기 바랍니다.

ⓒ F-Lab & Company

이 컨텐츠는 F-Lab의 고유 자산으로 상업적인 목적의 복사 및 배포를 금합니다.

조회수

지금 진행중인 멘토링 코스

궁금한 포지션을 알아보세요

  • 코스 이미지자바 백엔드 + AI

    아키텍처 설계와 대용량 트래픽 처리 능력을 깊이있게 기르는 개발자 성장 과정

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지노드 백엔드 + AI

    아키텍처 설계와 대용량 트래픽 처리 능력을 깊이있게 기르는 개발자 성장 과정

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지파이썬 백엔드 + AI

    파이썬과 백엔드 분야를 깊게 파면서 대규모 서비스를 튜닝하는 개발자로 성장

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지MSA 백엔드 + AI

    MSA를 겉핥기로 사용만 해보는 것이 아니라 깊게 이해하고 활용할 수 있는 개발자로 성장

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지프론트엔드 + AI

    언어와 프레임워크, 브라우저에 대해 탄탄한 이해도를 깊이있게 기르는 성장 과정

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지안드로이드 + AI

    아키텍처 설계 능력과 성능에 대한 경험을 갖추기 위해 깊이있게 학습하는 성장 과정

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지iOS + AI

    언어와 프레임워크, 모바일 환경 자체에 대한 탄탄한 이해도를 갖춘 개발자로 성장

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지플러터 + AI

    의존성 관리와 네이티브 레벨까지의 깊이 있는 지식을 학습하는 과정

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지리액트 네이티브 + AI

    의존성 관리와 네이티브 레벨까지의 깊이 있는 지식을 학습하는 과정

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지데브옵스

    단순 시스템 관리자가 아닌, 대규모 환경에서 인프라를 운영할 수 있는 엔지니어로 성장

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지데이터 엔지니어 + AI

    대규모 데이터를 다룰 수 있는 고급 데이터 엔지니어로 성장하는 과정

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지머신러닝 엔지니어

    ML 모델을 개발하고, 대규모 환경에서 서빙할 수 있는 고급 ML엔지니어로 성장

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지게임서버

    고성능 게임 서버를 구축할 수 있는 개발자로 성장할 수 있는 코스

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지게임 클라이언트

    대규모 고성능 게임을 만들어낼 수 있는 고급 C++ 게임 엔지니어로 성장하는 과정

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
  • 코스 이미지해외취업

    막연한 해외 취업의 꿈을 해외 재직 중인 빅테크 출신 개발자와 현실로 만드는 과정

    • 1:1 멘토링
    • 3~4개월
    • 연장 가능
logo
copyright © F-Lab & Company 2025