เร่งสปีด Python ด้วย JIT Numba ช่วยได้เยอะนะครับ

สวัสดีครับ

วันนี้ผมมีเรื่องดีๆ มาฝากเพื่อนๆ โปรแกรมเมอร์ ที่อยากให้โค้ด Python ของเราทำงานเร็วขึ้นแบบก้าวกระโดดนะครับ นั่นก็คือเรื่องของ Just-In-Time (JIT) Compiler โดยเฉพาะตัว Numba ที่ใช้กันบ่อยๆ เลย

หลายคนอาจจะเคยเจอปัญหาโค้ด Python ทำงานช้า โดยเฉพาะพวกที่ต้องคำนวณเยอะๆ หรือวนลูปซับซ้อนใช่ไหมครับ Numba นี่แหละครับ ตัวช่วยสำคัญ

Numba คืออะไร?

อธิบายง่ายๆ นะครับ Numba เนี่ย มันคือ JIT Compiler ที่จะแปลงโค้ด Python ของเรา ให้กลายเป็นโค้ดเครื่อง (machine code) ตอนที่โค้ดรันครั้งแรก

จากนั้นเวลาเราเรียกใช้ฟังก์ชันเดิมอีก โค้ดมันก็จะทำงานเร็วขึ้นมากๆ เลย เพราะไม่ต้องแปลใหม่แล้วไงครับ เจ๋งไหม

มาดูตัวอย่างโค้ดกันเลยดีกว่า

ผมมีตัวอย่างง่ายๆ มาให้ดูนะครับ เราจะลองเขียนฟังก์ชันคำนวณ Fibonacci แบบปกติ กับแบบใช้ Numba

import time
from numba import jit

# ฟังก์ชัน Fibonacci แบบธรรมดา
def fib_normal(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a

# ฟังก์ชัน Fibonacci แบบใช้ Numba JIT
@jit(nopython=True)
def fib_numba(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a

# ลองวัดเวลาดูครับ
print("ทดสอบ Fibonacci แบบธรรมดา")
start_time = time.time()
result_normal = fib_normal(100000)
end_time = time.time()
print(f"ผลลัพธ์: {result_normal}")
print(f"ใช้เวลา: {end_time - start_time:.4f} วินาที\
")

print("ทดสอบ Fibonacci แบบ Numba JIT")
start_time = time.time()
# รันครั้งแรก Numba จะ compile
result_numba = fib_numba(100000)
end_time = time.time()
print(f"ผลลัพธ์: {result_numba}")
print(f"ใช้เวลา: {end_time - start_time:.4f} วินาที (ครั้งแรก)\
")

print("ทดสอบ Fibonacci แบบ Numba JIT อีกครั้ง (หลังจาก compile แล้ว)")
start_time = time.time()
result_numba_again = fib_numba(100000)
end_time = time.time()
print(f"ผลลัพธ์: {result_numba_again}")
print(f"ใช้เวลา: {end_time - start_time:.4f} วินาที (ครั้งที่สอง)\
")

ผลลัพธ์ที่ได้อาจแตกต่างกันไปตามเครื่องนะครับ แต่ปกติแล้ว Numba จะเร็วกว่าเยอะเลย

จะเห็นว่าแค่เพิ่ม @jit(nopython=True) เข้าไปตรงหัวฟังก์ชัน โค้ดเราก็เร็วขึ้นแบบเห็นได้ชัดเลยใช่ไหมครับ ตรง nopython=True เนี่ย มันจะบังคับให้ Numba แปลงโค้ดเป็น machine code ทั้งหมดนะครับ ถ้ามีอะไรแปลไม่ได้มันจะฟ้อง error เลย เพื่อให้มั่นใจว่าโค้ดเราจะรันได้เร็วที่สุด

เรื่องของ Resilience นิดหน่อย

การใช้ JIT Compiler อย่าง Numba เนี่ยครับ มันช่วยให้โค้ดเรา optimization ได้ดีก็จริง แต่บางทีก็ต้องระวังนิดนึงนะครับ ถ้าโค้ดเรามีความซับซ้อนมากๆ เช่น ใช้โครงสร้างข้อมูลที่ไม่รองรับ หรือไปเรียกใช้ฟังก์ชัน Python ปกติเยอะเกินไป Numba อาจจะ compile ไม่ได้ หรือทำงานได้ไม่เต็มประสิทธิภาพนะครับ

เราก็ต้องเขียนโค้ดของเราให้ resilience คือพร้อมรับมือกับพวกสถานการณ์แบบนี้ด้วย

เช่น ถ้าเราไม่แน่ใจว่า Numba จะทำงานกับโค้ดเราได้ดีแค่ไหน เราอาจจะทำเป็น Fallback Mechanism ไว้ก็ได้ครับ คือถ้า Numba มันมีปัญหา หรือทำงานช้ากว่าที่คาด เราก็ยังสามารถสลับไปใช้เวอร์ชันที่ไม่ใช้ Numba ได้ เพื่อให้ระบบของเรายังทำงานต่อได้ ไม่ล่มง่ายๆ นะครับ

อีกเรื่องคือการ error handling นะครับ เวลาใช้ Numba ก็ควรจะมี try-except บล็อกดีๆ เผื่อกรณีที่ Numba มัน compile ไม่ผ่าน หรือเกิด runtime error ที่ไม่คาดคิดนะครับ จะได้ไม่ทำให้โปรแกรมเรา crash ไปเลย

from numba import jit
from numba.core import errors

@jit(nopython=True)
def calculate_complex_data(data_list):
    # สมมติว่ามีบางอย่างที่ Numba อาจจะไม่ชอบ หรือข้อมูลมีปัญหา
    total = 0
    for item in data_list:
        total += item * 2 # อาจจะมีปัญหาถ้า item ไม่ใช่ตัวเลข
    return total

def safe_calculate(data_list):
    try:
        # ลองใช้ Numba ก่อน
        print("ลองใช้ Numba JIT...")
        return calculate_complex_data(data_list)
    except errors.TypingError as e:
        # ถ้า Numba มีปัญหาในการแปลงโค้ด
        print(f"Numba Compile Error: {e} - ใช้ฟังก์ชัน Python ปกติแทน")
        # Fallback ไปใช้เวอร์ชันที่ไม่ใช้ Numba
        total = 0
        for item in data_list:
            total += item * 2
        return total
    except Exception as e:
        # ข้อผิดพลาดอื่นๆ
        print(f"เกิดข้อผิดพลาดอื่น: {e} - ใช้ฟังก์ชัน Python ปกติแทน")
        total = 0
        for item in data_list:
            total += item * 2
        return total


# ทดสอบแบบที่ข้อมูลปกติ
print("--- ทดสอบข้อมูลปกติ ---")
result1 = safe_calculate([1, 2, 3, 4, 5])
print(f"ผลลัพธ์: {result1}\
")

# ทดสอบแบบที่ Numba อาจจะมีปัญหา (เช่น ข้อมูลปนกัน)
print("--- ทดสอบข้อมูลที่มีปัญหา (Numba อาจจะฟ้อง error) ---")
# Numba nopython=True จะรับไม่ได้ถ้ามี string ปน
result2 = safe_calculate([1, 2, 'a', 4, 5])
print(f"ผลลัพธ์: {result2}\
")

เห็นไหมครับ การมี resilience ที่ดี ก็ช่วยให้โปรแกรมเราแข็งแรงขึ้นเยอะเลยนะครับ แม้จะใช้เทคนิค optimization ที่ซับซ้อนอย่าง jit compiler ก็ตาม

ผมหวังว่าบทความนี้จะเป็นประโยชน์กับเพื่อนๆ นะครับ ลองเอา Numba ไปเล่นดูได้เลย แล้วจะเห็นความต่างเลยครับ

สวัสดีครับ cii3.net

Sources/References: - Numba Documentation: https://numba.pydata.org/

Read more

คิวบา: มนต์เสน่ห์บนเส้นทางแห่งความท้าทาย – วิกฤตพลังงานและแรงกดดันจากสหรัฐฯ

คิวบา: มนต์เสน่ห์บนเส้นทางแห่งความท้าทาย – วิกฤตพลังงานและแรงกดดันจากสหรัฐฯ

เจาะลึกสถานการณ์ล่าสุดของคิวบา ทั้งวิกฤตไฟฟ้าดับครั้งใหญ่จากปัญหาพลังงาน และแรงกดดันจากสหรัฐฯ ภายใต้การนำของทรัมป์ อนาคตของเกาะปฏิวัติแห่งนี้จะเป็นอย่างไร?

By ทีมงาน devdog
ละครไทย: ถอดรหัสเสน่ห์ "พลอยน้ำเพชร" และปรากฏการณ์บันเทิงที่ไม่เคยจางหาย

ละครไทย: ถอดรหัสเสน่ห์ "พลอยน้ำเพชร" และปรากฏการณ์บันเทิงที่ไม่เคยจางหาย

สำรวจความเข้มข้นของละคร "พลอยน้ำเพชร" จากช่องวัน 31 พร้อมเจาะลึกตอนที่ 17-20 และเสน่ห์ของละครไทยที่ครองใจผู้ชมทั่วโลก

By ทีมงาน devdog
ชนนพัฒฐ์ นาคสั้ว: สส.สงขลา กับประเด็นร้อนคดีเว็บพนันออนไลน์ที่ DSI กำลังจับตา

ชนนพัฒฐ์ นาคสั้ว: สส.สงขลา กับประเด็นร้อนคดีเว็บพนันออนไลน์ที่ DSI กำลังจับตา

เจาะลึกประเด็นร้อน ชนนพัฒฐ์ นาคสั้ว สส.สงขลา พรรคกล้าธรรม กับกระแสข่าวพาดพิงถึงเครือข่ายเว็บพนันออนไลน์ที่ DSI กำลังสอบสวน เปิดความท้าทายต่อบทบาทผู้แทนราษฎร

By ทีมงาน devdog
เจาะลึก "ณัฐธิดา เล็กอุดากร" หลานเนวินชิดชอบ สส. อายุน้อยสุด ผู้พร้อมสร้างอนาคตใหม่ให้บุรีรัมย์

เจาะลึก "ณัฐธิดา เล็กอุดากร" หลานเนวินชิดชอบ สส. อายุน้อยสุด ผู้พร้อมสร้างอนาคตใหม่ให้บุรีรัมย์

ทำความรู้จักกับ พลอย ณัฐธิดา เล็กอุดากร สส. บุรีรัมย์ เขต 2 หลานสาว เนวิน ชิดชอบ ผู้สร้างประวัติศาสตร์ สส. อายุน้อยที่สุดในสภา พร้อมบทบาทการเมืองและวิสัยทัศน์คนรุ่นใหม่

By ทีมงาน devdog