timeit อีกหนึ่ง module ที่ไม่ควรมองข้ามของ Python

การพัฒนาซอฟแวร์ไม่ว่าจะด้วยภาษาอะไรก็ตาม องค์ประกอบหนึ่งที่ขาดไม่ได้เลยคือเรื่องของประสิทธิภาพหรือความเร็วในการทำงานของซอฟแวร์ ในภาษา Python เองก็มีโมดูล (module) ที่ให้นักพัฒนาได้เลือกใช้เพื่อวัดประสิทธิภาพของซอฟแวร์อยู่หลากหลายเหมือนกัน ซึ่งหนึ่งในนั้นก็คือ timeit

timeit เป็นโมดูลที่ติดมา (built-in) กับภาษา Python เลย ที่ช่วยให้เราวัดประสิทธิภาพการทำงานของโค้ดด้วยการวัดเวลาที่ใช้ในการประมวลผล (execution time) สามารถใช้งานผ่าน Command-line Interface (CLI) หรือเรียกใช้งาน (callable) ในรูปแบบฟังก์ชันก็ได้

บางคนอาจจะบอกว่า ใช้โมดูล time ไปเลยจะไม่ง่ายกว่าเหรอ ลองเปิดใจแล้วศึกษา timeit ดูก่อนแล้วค่อยตัดสินใจอีกทีก็ได้ครับ ^^

มาดูตัวอย่างการใช้งานผ่าน Command-line Interface กันก่อน

$ python3 -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 22.2 usec per loop
$ python3 -m timeit '"-".join(map(str, range(100)))'
100000 loops, best of 3: 14.8 usec per loop

จากตัวอย่างจะเห็นว่าการใช้งาน map ฟังก์ชันมีประสิทธิภาพที่ดีกว่า

ตัวอย่างการใช้งานแบบฟังก์ชัน

>>> import timeit
>>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
0.2313945009955205
>>> timeit.timeit('"-".join(map(str, range(100)))', number=10000)
0.16620492900256068

กรณีที่อยากใช้กับโค้ดที่มีหลายบรรทัดการใช้งานผ่าน CLI อาจจะยากกว่าการใช้งานแบบฟังก์ชัน เพราะต้องใส่ indent ให้ถูกต้องด้วย

$ python3 -m timeit -s 's="abcdef"' 'if s.startswith("a"):' '  pass'
10000000 loops, best of 3: 0.139 usec per loop
$ python3 -m timeit -s 's="abcdef"' 'if s[0]=="a":' '  pass'
10000000 loops, best of 3: 0.0523 usec per loop

แบบเรียกฟังก์ชันจะง่ายกว่าเพราะเราจะเห็น indent ชัดเจน

>>> import timeit
>>> stmt="""
... if s.startswith('a'):
...     pass
... """
>>> timeit.timeit(stmt=stmt, setup='s="abcdefg"')
0.15442462399369106
>>> stmt="""
... if s[0] == 'a':
...     pass
... """
>>> timeit.timeit(stmt=stmt, setup='s="abcdefg"')
0.05347593000624329

การใช้งานเพื่อวัดประสิทธิภาพของฟังก์ชัน เราจะต้องเพิ่มการ import function ผ่านพารามิเตอร์ setup

def simple_func():
    ...

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("simple_func()", setup="from __main__ import simple_func"))

ข้อควรระวัง

timeit จะปิดการทำงานของ garbage collection เป็นค่าเริ่มต้น เพื่อไม่ให้การวัดผลขึ้นอยู่กับ GC ดังนั้นหากต้องการเปิดใช้งาน GC ด้วยก็สามารถทำได้โดยการเปิดใช้งานผ่านพารามิเตอร์ setup

$ python3 -m timeit -s 's="abcdef"; gc.enable()' 'if s[0]=="a":' '  pass'