ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] Thread 설계 의도를 파악한 성능 개선. 7초에서 3초로
    TIL 2023. 4. 11. 14:42

     

    💻 Crawling Application Code에서 I/O Bound 작업 처리 시, threading Module과 concurrent.futures Module를 사용했습니다.

    - 두 Module의 장단점을 알지 못한 채 사용해 이번 포스팅에서 깊게 다뤄보겠습니다.

     

    🍎threading Module과 concurrent.futures Module을 사용하는 이유는 해당 포스팅에서 확인할 수 있습니다.

     

    🍎 thread.Thread와 concurrent.futures.ThreadPoolExecutor의 특징을 알아봅시다.

    🍏thread.Thread의 특징

    - Process안에 존재해 작업 처리 시 사용됩니다. Thread를 사용하는 두 가지 이유는 아래와 같습니다.

     

    1️⃣ Target Method를 실행할 수 있습니다.

    # a target function that does something
    def work()
    	# do something...
     
    # create a thread to execute the work() function
    thread = Thread(target=work)
    # start the thread
    thread.start()

    - Target Method는 전달된 Parameter 이외의 외부 상태와 상호 작용하지 않고 값을 반환하지 않는 일회성 작업을 실행하는 데 유용합니다.

     

    2️⃣ 클래스 확장과 thread.run()을 확장할 수 있습니다.

    # define a custom thread
    class CustomThread(Thread):
    	# custom run function
    	def run():
    		# do something...
     
     
    # create the custom thread
    thread = CustomThread()
    # start the thread
    thread.start()

    - Thread를 재정의 하거나 확장한다면 대상 함수를 호출하는 것보다 더 많은 유연성을 얻을 수 있습니다.

    - Thread 클래스를 확장하는 것은 수명이 긴 작업과 Application 내의 서비스에 적합합니다.

     

    🍏concurrent.futures.ThreadPoolExecutor의 특징

    - Max_workers를 통해 Thread의 수를 지정해 Thread Pool을 만들 수 있습니다.

    executor = ThreadPoolExecutor(max_workers=10)

     

    - 또한, 제공하는 map()을 통해 iterable Data를 처리할 수 있습니다. target_method(아래 코드에선 task)에 iterable Data가 인자 값으로 들어갈 때, Pool에 존재하는 Thread가 각 item을 처리합니다.

    for result in executor.map(task, items):
    	# process result...

     

    🍎 thread.Thread와 concurrent.futures.ThreadPoolExecutor의 유사점과 차이점을 알아봅시다.

    🍏 유사점

    1️⃣ 모두 python Thread를 사용합니다.

     

    2️⃣ 모두 특정 상황에 대응하여 단기적으로 수행하는 일시적인 작업에 적합합니다.(Ad hos task)

    - 시스템 전반에 거친 작업이 아닌 일시적인 작업이 필요할 때 사용하면 좋습니다.

     

    3️⃣ 모두 python GIL의 적용을 받습니다.

    - 따라서, 모두 I/O Bound 작업을 처리하는데 적합합니다.

     

    🍏 차이점

    1️⃣ 이기종 작업과 동종 작업

    - Thread는 동종 작업을 실행하도록 설계되었습니다. 확장된 Thread의 단일 작업 유형만 지원합니다.

    - ThreadPoolExecutor는 이기종 작업, 즉 서로 유사하지 않은 작업을 실행하도록 설계되었습니다. 예를 들어, Thread Pool에 다루는 작업이 각각 다른 대상의 함수일 수 있습니다.

     

    2️⃣ 재사용과 일회용

     

    - Thread는 일회용으로 설계되었습니다. 확장과 관계없이 작업 실행 후 새 인스턴스를 만들어야 합니다. (Java

    Thread run() 역시 마찬가지입니다.)

    - ThreadPoolExecutor는 Pool이 동작할 동안 Thread는 활성 상태로 유지되며 Pool이 종료될 때까지 작업을 실행할 수 있습니다.

     

    3️⃣ 여러 작업 대 단일 작업

     

    - Thread는 Parameter 또는 클래스의 확장하여 단일 작업을 실행하도록 설계되었습니다. 동시 작업을 관리하기 위한 기본 제공 도구는 존재하지 않습니다.

    - ThreadPoolExecutor는 여러 작업을 실행하도록 설계되었습니다. 예를 들어 map()의 경우 여러 함수 호출을 동시에 수행할 수 있습니다.

     

    🍎 Python Applicaion Code Refactoring

    - 아래 코드는 JBLY 프로젝트 일부입니다.

     

    🛠️ Refactoring 전 : threading module 사용

    - Thread를 설계의 의도에 어긋나게 사용하고 있다는 것을 알 수 있습니다.

    - 이기종의 작업을 처리하고 있을뿐더러 일회용으로 사용되는 모습을 볼 수 있습니다.

     

    🛠️ Refactoring 후 : concurrent.futures module 사용

    - Thread Pool에 있는 Thread를 재사용하며 서로 다른 작업을 처리할 수 있게 변경했습니다.!

     


    7초 걸린 URL Parsing 작업을 3초대로 줄일 수 있었습니다.

    - Thread 재사용을 하지 않고 작업을 진행했을 때 이미지입니다.

     

    - Thread Pool을 통해 Thread를 재사용하며 작업을 처리했을 때 이미지입니다.

     

    ✅ 어떤 기술을 프로젝트에 적용할 때, Publisher의 설계 의도를 알고 사용하는 것이 중요하다는 것을 깨달았습니다.

     


    📚 참고 사이트

    concurrent.futures Module 공식 문서

    threading 공식 문서

    context manager 공식 문서

    ThreadPoolExecutor_vs_Thread


    🍎 PR에서 변경 사항을 확인할 수 있습니다.

    🍎 Github 주소 :친환경 사과

     

    EcoFriendlyAppleSu - Overview

    EcoFriendlyAppleSu has 24 repositories available. Follow their code on GitHub.

    github.com

     

    댓글

Designed by Tistory.