ComBioLaw.De » Blog » เขียนโปรแกรม » เพิ่มความเร็วให้ Python ตอน 3 : f2py, numpy

เพิ่มความเร็วให้ Python ตอน 3 : f2py, numpy

imageหลังจากที่ผมเขียนเรื่อง เพิ่มความเร็วให้ Python ตอน 1 , ตอน 2 ไว้เมือนานมาแล้ว (ประมาณหนึ่งปี) ตั้งแต่นั้นเป็นต้นมาผมก็ใช้ Pyrex เขียนโปรแกรม simulation มาโดยตลอด เนื่องเพราะโปรแกรม simulation เป็นโปรแกรมที่ใช้ความสามารถในการคำนวนที่สูง ความเร็วของโปรแกรมจึงเป็นสิ่งสำคัญมาก เมื่ออาทิตย์ที่แล้วผมนึกสนุก นั่งวัดความเร็วโปรแกรมที่เขียนด้วย Pyrex เปรียบเทียบกับโปรแกรมที่เขียนด้วย FORTRAN จากที่เคยวัดไว้นั้น โปรแกรมที่เขียนด้วย Pyrex ทำงานเร็วกกว่าพอสมควร ผมเลยตัดสินใจใช้ Pyrex มาโดยตลอด แต่ครั้งนี้ผมกลับพบกับความผิดหวัง เพราะ โปรแกรมที่เขียนด้วย Pyrex ทำงานช้ากว่าเป็นสิบเท่า

สาเหตุที่โปรแกรมที่เขียนด้วย Pyrex ทำงานช้าก็เพราะจำนวนเชิงซ้อนครับ และจำนวนเชิงซ้อนเป็นสิ่งที่จำเป็นมากมายใน simulation เกี่ยวกับการส่งสัญญาณที่ผมพัฒนาอยู่ ที่จำนวนเชิงซ้อนทำให้โปรแกรมทำงานช้าก็เพราะว่า จำนวนเชิงซ้อนไม่สามารถถูกแปลงเป็นตัวแปรภาษา C โดยผ่าน cdef ได้ ความเร็วการคำนวนเชิงซ้อนใน Pyrex จึงไม่ต่างจากการคำนวนจำนวนเชิงซ้อนใน Python มากนัก

ผมก็เลยต้องหาวิธีเพิ่มความเร็วให้ Python วิธีอื่น ๆ เพิ่มเติม เดิมทีนั้นผมรู้จัก f2py อยู่แล้ว f2py เป็นเครื่องมือที่ใช้คอมไพล์ Subroutine + Function ภาษา FORTRAN ให้เป็น library สำหรับ Python แต่...

เขียนโปรแกรม เขียนโปรแกรม

bow_der_kleine bow_der_kleine

ผมไม่ชอบใช้ f2py เท่าใดนักเนื่องเพราะ

  • ปัญหาเรื่องรูรั่วในหน่วยความจำของ FORTRAN ยังคงมีอยู่ ซึ่งเป็นปัญหาที่แก้ยากมาก
  • Array จำนวนเชิงซ้อนของ FORTRAN มีความยาวสูงสุดที่ 131072 ซึ่งมันไม่พอสำหรับ Simulation ขนาดใหญ่
  • มีปัญหาในการ include การเรียกใช้ subroutine ใน subroutine ซึ่งอยู่คนละไฟล์กัน f2py มันจะงง แล้วไม่คอมไพล์ให้ ทางแก้คือ เขียนทุก subroutine ให้อยู่ในไฟล์เดียวกัน แล้วเราก็จะเจอปัญหาการบริหารโค้ดแทน
  • subroutine ใน FORTRAN มันไม่คืนค่า ส่วน function คืนค่าที่เป็น Array ไม่ได้ ต้องมีการแก้โค้ดให้ f2py ถึงจะได้ subroutine ที่คืนค่าเป็น array ได้ ซึ่งยุ่งยากใช้ได้เลย
  • ในโค้ดของ FORTRAN ไม่สามารถใช้ library ของ Python ได้

  • FORTRAN มันห่วย
ซึ่งปัญหาเหล่าผมแก้ได้บางจุด แต่จะเล่าให้ฟังตอนท้ายครับ

ด้วยปัญหาต่าง ๆ เหล่านี้ ทำให้ผมต้องหาทางออกเพิ่มเติม ซึ่งก็ไปเจอของดีเข้า มันคือ numpy จริง ๆ แล้วแนวคิดของ numpy ไม่ใหม่ครับ มันก็คือ Numeric Python กับ numarray ที่เขียนใหม่ ซึ่งสองโมดูลที่ว่า ถูกสร้างขึ้นเพื่อให้ Python สามารถใช้แทน MATLAB ได้ แต่ด้วยปัญหาบางอย่าง เลยต้องพัฒนาใหม่จนกลายมาเป็น numpy

สิ่งที่ผมคิดว่าพิเศษมาก ๆ ใน numpy นอกจากการใช้งานที่ง่ายเหมือ MATLAB แล้ว คือ ความเร็วที่สูงมาก เทียบเท่าโปรแกรมที่เขียนด้วยภาษา C (อีกแล้ว) แต่เราจะได้ความเร็วดังกล่าวก็ต่อเมื่อ เราใช้ operation ระหว่าง array โดยตรงเท่านั้น ไม่ใช่ operation ระหว่างสมาชิกใน array โดยผ่าน for loop ยกตัวอย่างเช่น

from numpy import *
 
a = arange(0.0,20.0,0.01, dtype=complex64) + 1.0j*arange(0.0,20.0,0.01, dtype=complex64)
b = a[-1::-1]
c = a+b
 
# จะเร็วกว่าการใช้ for loop ประมาณ 50 เท่า
for i in range(len(a)):
	c[i] = a[i]+b[i]
plain code

ผมคิดว่าการใช้ operation ระหว่าง array นั้น แม้จะคลอบคลุมการใช้งานไม่ได้ทั้งหมด แต่ก็คลอบคลุมได้กว่า 80% เลยทีเดียว ในตัว numpy เองก็มีโมดูลต่าง ๆ ให้เลือกใช้งานมากมาย สามารถดูตัวอย่างได้ที่ Numpy Example List

ปัญหาอย่างหนึ่งที่ผมแก้ไม่ตกคือ simulation วงจรที่มี feed back ซึ่งวงจรเหล่านี้คือวงจรที่ข้อมูลขาออกมีผลต่อข้อมูลขาเข้า หรือ recursive นั่นเองครับ ซึ่งการ simulation วงจรเหล่านี้จำเป็นอย่างยิ่งที่ต้องใช้ for loop (คนใช้ Simulink อาจบอกว่าไม่จำเป็น) หรือหากไม่ใช้ for loop ก็ต้องนั่ง คำนวนโดยใช้ Laplace หรือ FFT กันจนปวดหัว

การใช้ for loop ทำให้ simulation ช้าลงประมาณ 50 เท่า ซึ่งเป็นตัวเลขที่ไม่น่าพอใจเอาเสียเลย ผมเลยจำเป็นต้องกลับไปใช้ f2py ในกรณีที่ต้องใช้ for loop และเพื่อไม่ให้ปัญหาที่ผมเคยพบใน f2py กลับมาหาผมอีก ผมเลยต้องตั้งเงื่อนไขส่วนตัวในการใช้งาน f2py เพิ่มเติมดังนี้ครับ

  • จะใช้ f2py ก็ต่อเมื่อเป็นโมดูลที่มี for loop เท่านั้น
  • ใช้ f2py ให้น้อยที่สุด
  • ใช้ syntax ของ FORTRAN 90/95 แทน FORTRAN 77 จะช่วยแก้ปัญหาเรื่องรูรั่วหน่วยความจำและขนาด Array ได้
นอกจากนี้ผมยังเขียนเครื่องมือขึ้นมาใช้อีกหนึ่งโปรแกรม และตั้งชื่อมันว่า p2p ซึ่งเป็นเครื่องมือที่ใช้คอมไพล์โมดูลที่เขียนด้วย Pyrex หรือ f2py โดยอัตโนมัติ ซึ่งมันมีความสามารถต่าง ๆ ต่อไปนี้ครับ

  • ค้นหาไฟล์ที่ลงท้ายด้วย .pyx ที่อยู่ในแฟ้มแล้วคอมไพล์ด้วย Pyrex โดยอัตโนมัติ
  • ค้นหาไฟล์ที่ลงท้ายด้วย .f ที่อยู่ในแฟ้มแล้วคอมไพล์ด้วย f2py โดยอัตโนมัติ
  • incremental compiling โปรแกรม p2p จะคอมไพล์เฉพาะไฟล์ที่ได้รับการแก้ไขหลังจากการคอมไพล์ครั้งที่แล้วเท่านั้น
  • สามารถเรียงลำดับการคอมไพล์ไฟล์ .pyx ได้ โดยกำหนดในไฟล์ compile.list
  • รวมโค้ดที่อยู่ในไฟล์ .f ทั้งหมด ไว้ในไฟล์ไฟล์เดียว แล้วคอมไพล์เป็นโมดูลโมดูลเดียวโดยอัตโนมัติ เพื่อแก้ปัญหาการเรียกใช้ subroutine ใน subroutine
การใช้งานนั้นสามารถใช้งานได้สองรูปแบบคือ ผ่าน shell หรือ เรียกใช้เป็นโมดูล การเรียกใช้งานผ่าน shell นั้นสามารถใช้งานได้ดังนี้ครับ

python p2p.py # แยกคอมไพล์ไฟล์ .f
python p2p.py in_one modulename #รวมไฟล์ .f ทั้งหมดเป็นไฟล์เดียว แล้วคอมไพล์เป็นโมดูลชื่อ modulename
plain code

ส่วนการเรียกใช้เป็น module สามารถใช้งานได้ดังนี้ครับ

import p2p
p2p.p2p() # เหมือนคำสั่ง python p2p.py
p2p.p2p("modulename") # เหมือนคำสั่ง python p2p.py in_one modulename
plain code

ซึ่งใน simulation ที่ผมพัฒนาอยู่นั้น ผมใช้เทคนิคทุกอย่างที่ผมได้เขียนมาครับ ไม่ว่าจะเป็น Pyrex, Psyco, numpy, f2py ส่วนผู้ร่วมงานของผมคนอื่น ๆ ใช้ FORTRAN 77 กัน แต่การทำงานร่วมกันก็ไม่เคยเกิดปัญหาใด ๆ หรือหากจะมีใครต้องการจะใช้ภาษา C ก็ไม่ใช่เรื่องยากครับ ที่จะทำงานร่วมกัน เพราะเรายังมี SWIG ให้ใช้งานอยู่ ผมถือว่านี่เป็นเสน่ห์อย่างหนึ่งของ OpenSource และ Python ครับ

ที่ทุกคนสามารถทำงานร่วมกันได้ โดยที่มีความถนัดไม่เหมือนกัน ใช้เครื่องมือไม่เหมือนกัน

04 Mar 07 | by | tags เขียนโปรแกรม Python f2py numpy

read 2485

<<ชราธิปไตย !? || สายลม แสงแดด แต่ไม่มีหาดทราย>>

ความคิดเห็น (click here to comment)

Search

Navigation

รวมลิงก์น่าสนใจ

ความเคลื่อนไหว

Login

name password

ลืมรหัสผ่าน