융무의 기술블로그
article thumbnail
Published 2023. 3. 5. 21:34
python Async Data Engeneering/python

실무에 사용한 데이터 엔지니어링 스킬에 대한 정리내용입니다.

개인적인 기록을 위해 작성하였습니다.

https://github.com/mjs1995/muse-data-engineer/blob/main/doc/Programming%20Language/python_Async.md

 

GitHub - mjs1995/muse-data-engineer: 데이터 엔지니어로 성장하기

데이터 엔지니어로 성장하기. Contribute to mjs1995/muse-data-engineer development by creating an account on GitHub.

github.com


비동기 I/O

  • blocking IO vs Non-blocking
    • blocking IO
      • 시스템 콜 요청 시 -> 커널 IO 작업 완료 시까지 응답 대기
      • 제어권(IO작업) -> 커널 소유 -> 응답(Response) 전까지 대기(Block) -> 다른 작업 수행 불가(대기)
    • Non-blocking
      • 시스템 콜 요청 시 -> 커널 IO 작업 완료 여부 상관없이 즉시 응답
      • 제어권(IO작업) -> 유저 프로세스 전달 -> 다른 작업 수행 가능(지속) -> 주기적 시스템 콜 통해서 IO 작업 완료 여부 확인
  • Async(비동기) vs Sync(동기)
    • Async : 코드가 작성된 순서대로 수행하지 않음, IO 작업 완료 여부에 대한 Noty(Notifation)는 커널(호출되는 함수) -> 유저프로세스(호출하는 함수) (작업 되면 연락 주세요 할 거하고 있다가 나중에 연락받음)
      •  
    • Sync : 코드가 작성된 순서대로 수행, IO 작업 완료 여부에 대한 Noty는 유저프로세스(호출하는 함수) -> 커널(호출되는 함수) (작업 완료됐나요? 계속 물어보면서 호출해서 되면 시작함)
  • 실제 코드 자체보다는 코드에 필요한 데이터를 얻어오는 작업이 병목이 될 수도 있습니다. 프로그램이 I/O위주(I/O bound)
  • 비동기 프로그래밍 소개
    • 일반적으로 프로그램이 I/O 대기(I/O wait, 일시 정지된 상태를 일컬음)에 들어가면, 실행이 멈추고 커널이 I/O 요청과 관련된 저수준 연산을 처리하며(이를 컨테스트 스위칭으로 상당히 비싼 연산), I/O 연산이 끝날 때까지 프로그램은 재개되지 않습니다.
    • 동시성 프로그램은 보통 실행할 대상과 시점을 관리하는 이벤트 루프(실행할 함수의 목록에 지나지 않음)를 사용합니다.
    • 파이썬에서 비동기 프로그램을 만들 수 있습니다. 즉 여러 코루틴이 특정 순서로 동작하도록 스케줄링을 할 수 있으며, 일시 정지된 yield from 코루틴과 통신할 수 있습니다.
    • 가장 큰 장점 : 논블로킹 방식으로 병렬 I/O 작업을 할 수 있다는 것으로 이때 필요한 것은 보통 서드파티 라이브러리에서 구현한 저수준의 제너레이터입니다. 이 라이브러리들 코루틴이 일시 중단된 동안 실제 I/O 처리를 합니다. 코루틴이 정지된 동안 프로그램은 다른 작업을 할 수 있어 효율적입니다.
    • 효율적인 반복을 원할 때는 제너레이터를 사용하고, 논블로킹 I/O 작업을 원할 때는 코루틴을 사용합니다. 차이점은 분명하지만 파이썬의 동적 특성으로 인해 이러한 객체를 혼합해서 사용하다가 개발 마지막 단계에서 런타임 오류가 발생하기 도 합니다.
    • await는 yield from을 대신하기 위한 용도로 사용되고, 오직 awaitable 객체에 대해서만 동작합니다. 코루틴 또한 awaitable 객체로 awaitable 인터페이스를 따르지 않는 객체에 await를 호출하면 예외가 발생합니다. 이것은 인터페이스가 런타임 오류를 방지하고 보다 견고한 디자인을 달성하는 데 어떻게 도움이 될 수 있는지를 보여주는 좋은 예입니다.

  • async/await의 동작 방식
    • async 함수(async def)는 코루틴(coroutine)이라 불립니다.
    • 파이썬에서 코루틴은 제너레이터와 같은 철학으로 구현되며 제너레이터에 이미 실행을 일시 중단하고 나중에 계속 실행할 수 있는 장치가 있으므로 구현이 편리합니다. 이 패러다임을 사용하면 await 문은 함수의 yield문과 기능 면에서 비슷해집니다.
  • gevent
    • 굉장히 단순한 비동기 라이브러리로 비동기 함수가 퓨처를 반환한다는 패러다임을 따릅니다. 코드의 로직 대부분을 동시에 실행할 수 있다는 뜻입니다.
    • gevent는 표준 I/O 함수를 몽키패치(monkey patch)해서 비동기적으로 만듭니다. 보통 표준 I/O 패키지를 사용하기만 해도 비동기적 동작의 이점을 살릴 수 있습니다.
    • 그린렛(greenlet)은 코루틴의 일종으로 스레드와 같다고 생각할 수 있습니다.
  • tornado : 파이썬 비동기 I/O에 자주 사용함, HTTP 클라이언트와 서버를 위해 페이스북에서 개발한 패키지
  • 파이프라이닝 : 결과를 일괄 처리하는 방식으로 I/O 작업의 부하를 낮추고 싶을 때 큰 도움이 됩니다. 파이프라이닝은 비동기 I/O의 속도와 순차 프로그램의 작성 용이성을 잘 절충한 방식으로 파이프라이닝 시 사용할 적절한 묶음의 크기는 상황에 따라 달라지므로 최선의 결과를 얻으려면 프로파일링과 튜닝이 필요합니다.
  • gevent는 비동기 I/O를 위한 가장 높은 수준의 인터페이스를 제공합니다. tornado와 aiohttp를 사용하면 비동기 I/O 스택을 직접 제어할 수 있으며 서로 다른 수준의 추상화와 더불어, 각 라이브러리는 서로 다른 문법 패러다임을 사용합니다. asyncio는 비동기 해법을 하나로 묶는 접착제이며 이 모두를 제어할 수 있는 토대를 제공합니다.

제너레이터와 코루틴

  • 제너레이터는 한 번에 하나씩 구성요소를 반환하는 이터레이터 객체를 반환하는 함수. 제너레이터를 사용하는 주요 목적은 메모리를 절약하는 것입니다.
  • 하스켈과 같은 다른 함수형 프로그래밍 언어가 제공하는 것과 비슷한 방식으로 게으른 연산을 통해 무거운 객체를 사용할 수 있도록 합니다. 게으른 연산의 특성을 가졌기 때문에 무한 시퀀스를 사용할 수도 있습니다.
def load_purchases(filename):
  with open(filename) as f:
    for line in f:
      *_, price_raw = line.partition(",")
      yield float(price_raw)

load_purchases("file")
# <generator object load_purchases at 0x...>
  • load_purchases 함수를 제너레이터 함수 또는 단순히 제너레이터라고 부름
  • yield는 Python의 generator를 만드는 데 사용됩니다.
  • 다음의 경우에 유용합니다.
    • 데이터 크기가 클 때: return은 한 번에 모든 결과를 반환합니다. 반면에 yield는 한 번에 하나의 결과만 반환하여 메모리 효율성을 향상시킵니다. 따라서 큰 데이터 집합에서 작동할 때 yield를 사용하면 메모리 부하를 줄일 수 있습니다.
    • 대용량 데이터를 빠르게 처리할 때: yield를 사용하면 처리 과정을 제어할 수 있어 실행 시간을 효율적으로 관리할 수 있습니다. 이는 대용량 데이터를 처리하는 경우 유용하게 사용할 수 있습니다.
    • 많은 값을 반환하려 할 때: 함수가 많은 수의 값을 반환해야 할 경우, yield를 사용하면 하나의 값씩 순차적으로 처리할 수 있어 처리 과정이 간단해집니다.
    • 무한한 데이터 생성: yield는 이론적으로 무한한 수의 데이터를 생성할 수 있는 능력이 있습니다. 이는 끝이 없는 데이터 스트림을 생성해야 하는 상황에서 유용하게 사용할 수 있습니다.

코루틴

  • 코루틴의 핵심은 특정 시점에 실행을 일시 중단했다가 나중에 재시작할 수 있는 함수를 만드는 것으로 이런 기능 덕분에 프로그램은 다른 코드를 디스패치(dispatch)하기 위해 기존 코드를 중지했다가, 나중에 다시 원래의 위치에서 재시작할 수 있습니다.
  • 파이썬은 코루틴을 생성하기 위해 제너레이터를 활용합니다. 제너레이터는 중지 가능한 객체이므로, 자연스럽게 코루틴이 되기 위한 좋은 성질을 가지고 있습니다.
  • 코루틴에서는 일반적으로 다음과 같은 형태로 yield 키워드를 사용합니다.
receive = yield produce
  • 이 경우 yield 키워드는 두 가지 일을 합니다. 하나는 produced 값을 호출자에게 보내고 그곳에 멈추는 것으로 호출자는 next() 메서드를 호출하여 다음 라운드가 되었을 때 값을 가져올 수 있습니다. 다른 하나는 거꾸로 호출자로부터 send() 메서드를 통해 전달된 produced 값을 받는 것으로 이렇게 입력된 값은 receive 변수에 할당됩니다.
  • 코루틴에 값을 전송하는 것은 yield 구문이 멈춘 상태에서만 가능함
  • 코루틴이 정보를 처리하고 실행을 일시 중단한다는 점에서 코루틴을 경량 스레드(lightweight thread) 또는 그린 스레드(green thread)라고 생각할 수도 있음

Reference

'Data Engeneering > python' 카테고리의 다른 글

python multiprocessing  (1) 2023.03.06
python 컴파일  (0) 2023.03.05
python 프로파일링  (0) 2023.03.05
profile

융무의 기술블로그

@융무

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!