วันอังคารที่ 7 พฤษภาคม พ.ศ. 2562

หลักการทำงานเบื้องต้นของ Python Multiprocessing

บทความนี้จะเป็นการศึกษาการทำงานเบื้องต้นของ Multiprocessing โดยจะเริ่มจากการเรียกใช้งานแบบพื้นฐานดังนี้

เริ่มจากการ import library multiprocessing มาก่อนโดยส่วนที่จะใช้คือ

from multiprocessing import Process, current_process, cpu_count
import threading
import os
import time
from timeit import default_timer as timer
  • Process สำหรับสร้าง process
  • current_process สำหรับแสดงชื่อของ process ที่ทำงานอยู่ขณะนั้น
  • cpu_count สำหรับเก็บจำนวน core ของ CPU ซึ่งในที่นี้จะทำการรันฟังค์ชัน ตามจำนวนของ core ที่มี
รวมไปถึง library อื่นๆ ได้แก่

  • threading สำหรับตรวจสอบชื่อของ thread ที่ทำงานขณะนั้น
  • os สำหรับตรวจสอบ pid ที่ทำงานขณะนั้น
  • timeit สำหรับจับเวลาการทำงานของฟังค์ชันหรือ process
  • time สำหรับใช้หน่วงเวลาการทำงานของฟังค์ชันโดยใช้ time.sleep


จากนั้นทำการสร้างฟังค์ชันที่ใช้สำหรับทดสอบ โดยจะแบ่งเป็น 2 ฟังค์ชันคือ

1. ฟังค์ชันที่ใช้ sleep เพื่อหน่วงเวลาการทำงาน 1 วินาที

def only_sleep():
# Do nothing, wait for a timer to expire
     print("PID: %s, Process Name: %s, Thread Name: %s" % (
       os.getpid(),
       current_process().name,
       threading.current_thread().name))
     time.sleep(1)

2. ฟังค์ชั้น loop ทั่วไป โดยภายใน loop จะเพิ่มค่าที 1 จนกว่าจะถึงเงื่อนไขที่ตั้งไว้ในการหน่วงเวลาแทนการใช้ time.sleep

def crunch_numbers():
# Do some computations
     print("PID: %s, Process Name: %s, Thread Name: %s" % ( os.getpid(),current_process().name,threading.current_thread().name))
     x = 0
     while x < 10000000:
          x += 1

โดยวิธีการเรียกใช้จะแบ่งเป็น 2 วิธี คือ

1. การเรียกใช้ฟังค์ชันแบบเป็นลำดับ โดย NUM_WORKERS จะเก็บจำนวน core ของ cpu มาใช้ ในที่นี้คือ 8 cores

if __name__ == "__main__":

     NUM_WORKERS = cpu_count()

     # Run tasks serially
     start_time = timer()
     for _ in range(NUM_WORKERS):
          only_sleep()
     end_time = timer()

     print("Serial time = %s" %(end_time - start_time))

2. การเรียกใช้ฟังค์ชันแบบ Multiprocessing

     # Run tasks using processes
     start_time = timer()
     processes = [Process(target=only_sleep) for _ in range(NUM_WORKERS)]
     [process.start() for process in processes]
     [process.join() for process in processes]
     end_time = timer()

     print("Parallel time = %s" %(end_time - start_time))


โดยฟังค์ชันของ Multiprocessing นั้นจะแบ่งเป็นหลักๆคือ

  • Process ใช้สำหรับสร้าง process โดยภายในจะมีตัวแปรหลักๆ 2 ตัวคือ
    • target สำหรับใส่ฟังค์ชันที่เราต้องการให้ process ใช้ทำงาน
    • args สำหรับป้อนตัวแปรที่จะส่งเข้าไปให้ฟังค์ชันใน target ทำงาน หากไม่มีตัวแปรที่ต้องส่งสามารถใช้ args=() หรือ ไม่ใส่ก็ได้
  • start() ใช้สั่งให้ process นั้นๆทำงาน
  • join() ใช้สำหรับบังคับให้รอจนกว่า process ตัวที่ทำงานอยู่นั้นเสร็จสิ้นก่อน จึงจะทำงานอื่นต่อไป
เริ่มจากการทดสอบฟังค์ชัน only_sleep ก่อน ได้ผลดังนี้


จากภาพผลการทำงานแบบลำดับนั้นจะใช้เวลา 8 วินาที แต่การทำงานแบบ Multiprocessing จะใช้เวลาเพียง 1 วินาที เนื่องจากทั้ง 8 processes นั้นทำงานในเวลา

* ข้อสังเกต : process นั้นมีความเป็นไปได้ที่จะมีตัวใดตัวหนึ่งทำงานเสร็จก่อน จึงทำให้ลำดับการทำงานไม่ออกมาตรงตามที่เราต้องการเสมอไป ดังรูปจะสังเกตได้ว่า process 6 ทำงานเสร็จก่อน process 5 เป็นต้น

การทดลองในส่วนถัดไปจะใช้ฟังค์ชัน crunch_number แทน only_sleep ได้ผลดังนี้


จากภาพเวลาการทำงานแบบลำดับนั้นคือ 2.8 วินาที และเวลาการทำงานแบบ Multiprocessing นั้นใช้เวลาการทำงานเพียง 0.7 วินาที ซึ่งเป็น 1/4 ของเวลาที่ใช้ไปในแบบลำดับ

** ข้อสังเกต : เพื่อให้เห็นภาพที่ชัดเจนเกี่ยวกับ Multiprocess มากขึ้น จึงได้ทำการปรับโค้ด crunch_number จากให้ค่าของ loop เพิ่มทีละ 1 เป็น 0.1 
def crunch_numbers():
     # Do some computations
     print("PID: %s, Process Name: %s, Thread Name: %s" % (
       os.getpid(),
       current_process().name,
       threading.current_thread().name))
     x = 0
     while x < 10000000:
         # x += 1
          x += 0.1

ซึ่งจะได้ผลตามภาพดังนี้


ภาพการทำงานของ CPU core ช่วงการทำงานแบบลำดับ


ภาพการทำงานของ CPU core ช่วงการทำงานแบบ Multiprocessing

จะพบว่าในช่วงการทำงานของ Multiprocessing นั้น CPU core จะทำงานพร้อมกันทั้ง 8 cores ซึ่งต่างจากการทำงานแบบลำดับที่การประมวลผลทั้งหมดจะทำงานอยู่ที่ 1 core เท่านั้น

ไม่มีความคิดเห็น:

แสดงความคิดเห็น