Hướng dẫn python chạy chậm

Mình tin rằng bạn đã nghe nhiều người phàn nàn rằng Python quá chậm. Mình thấy mọi người chỉ so sánh Python với C về hiệu năng - performance, nhưng không mấy ai so sánh về thời gian phát triển. Thời gian phát triển Python quá nhanh vì Python có vẻ "dễ code" hơn một vài ngôn ngữ khác và bạn không phải đối mặt với con trỏ hay quản lý bộ nhớ, v.v.

Một trong những lý do mình sử dụng Python rất nhiều mặc dù mình cũng biết cơ bản một số ngôn ngữ khác (chẳng hạn C/C++) là vì trong hầu hết các trường hợp, thời gian phát triển quan trọng hơn hiệu suất. Nếu bạn làm nhanh sẽ làm được nhiều và có lẽ chỉ ở bước đưa ra sản phẩm bạn mới thực sự cần đến performance. Hơn nữa Python có một cộng đồng rất lớn và các thư viện hỗ trợ nhiều vô kể nên bạn có thể làm rất nhiều thứ.

Trong bài viết này mình sẽ nói về Cython thứ giúp cho code Python của bạn nhanh hơn nhiều lần để hạn chế điểm yếu là chạy chậm của Python thuần. Theo định nghĩa từ infoworld,  "Cython is a superset of Python that compiles to C, Cython combines the ease of Python and the speed of native code". Tạm dịch Cython là một thay thế của python có thể biên dịch thành C, Cython kết hợp sự đơn giản của Python và tốc độ của C.

Bài viết sẽ chia làm 2 phần chính, ở phần đầu mình sẽ giới thiệu một số cú pháp đơn giản của Cython và phần sau đó mình sẽ thử làm một ví dụ để so sánh tốc độ với Python thuần

Cython - Phần mở rộng C cho Python

Nếu bạn muốn code cú pháp dễ dàng như Python và hiệu suất cao như C thì Cython là lựa chọn của bạn. Bạn có thể sử dụng Cython để viết các extention C cho Python. Code Python của bạn sẽ được dịch sang code C/C++ và được tối ưu hóa. Nó sẽ cung cấp cho bạn hiệu suất cao và bạn có thể sử dụng nó trong các dự án Python của mình. 

1. Cài đặt Cython

Lưu ý rằng bạn sẽ cần một trình biên dịch C dựa theo hệ điều hành bạn đang sử dụng, cái này có thể tự google nhé, mình sẽ không hướng dẫn ở đây. Sau đó, bạn cần cài đặt Cython như dưới đây.

pip install Cython

2. Lợi ích từ khai báo biến tĩnh (static)

Nếu bạn để ý ở Python sẽ rất dễ dàng, bạn chẳng cần khai báo kiểu dữ liệu cho biến nhưng ở C/C++ thì đó lại là điều bắt buộc. Điều này là một trong rất nhiều nguyên nhân khiến C/C++ có hiệu suất tốt hơn. Bạn có thể tạm hiểu theo cách này, nếu bạn định nghĩa càng rõ ràng thì máy càng đỡ mất thời gian để hiểu ý nghĩa từng dòng code của bạn điều này đồng nghĩa với việc giúp máy xử lý nhanh hơn :D. 

Với Cython bạn có thể thêm khai báo kiểu static cho các biến và hàm của bạn. Nó sẽ cung cấp cho bạn hiệu suất tốt hơn đấy. Bạn có thể sử dụng giống với code C như int, float, double, ...

3. Khai báo một biến trong Cython

cdef int i = 10

Về cơ bản câu lệnh trên có nghĩa là bạn đang khai báo một biến số nguyên i  (kiểu int)

4. Khai báo một hàm trong Cython

cdef int square (int x):

return x ** 2

Đoạn code này khai báo một hàm có tên square lấy một tham số x có kiểu int và trả về một giá trị x**2 cũng là kiểu int.

Có ba kiểu khai báo hàm là def, cdef và cpdef.

  • def sẽ chỉ gọi từ code Python
  • cdef chỉ gọi từ code Cython
  • cpdef thì có thể được gọi cả từ C và Python

Ở phần tiếp theo ta sẽ thử đi làm ví dụ để so sánh nhé @@

Làm code Python nhanh hơn với Cython

1. Tính giai thừa trong Python

Thử code với python thuần như sau nhé

def factorial(n):

    if  n >= 1:

        return n * factorial(n - 1)

    return 1

>>> Hãy làm nó nhanh hơn theo cách của Cython nhé :D 

2. Tạo một file .pyx

Chẳng hạn tạo 1 file fastfac.pyx với nội dung như sau:

cpdef long fastfactorial(long n):

    if  n >= 1:

        return n * fastfactorial(n - 1)

    return 1

Hàm này sẽ trả về một giá trị có kiểu dữ liệu long và tham số n truyền vào cũng là kiểu long.

3. Tạo một file setup

Bây giờ chúng ta cần tạo một file setup.py để dịch Cython sang C với nội dung như sau:

from distutils.core import setup

from Cython.Build import cythonize

setup(ext_modules = cythonize('fastfac.pyx'))

4. Biên dịch code (compile)

Để thực hiện việc biên dịch ta chạy dòng lệnh sau,

python3 setup.py build_ext --inplace

trong đó, --inplace là tham số tạo một file đối tượng (.so) được hiện ra trong thư mục đang làm việc.

5. So sánh tốc độ nào

Bước cuối cùng là so sánh tốc độ =))

>>>from fastfac import fastfactorial

>>>from fac import factorial

>>>from timeit import timeit

>>>print(timeit('fastfactorial(20)', globals=globals(), number=10000))  

#output: 0.005239142999926116

>>>print(timeit('factorial(20)', globals=globals(), number=10000))  

#output: 0.05793850899863173

=> Bạn thấy nó nhanh thế nào rồi chứ, hãy thử chạy trên máy mình đi nhé. Lưu ý là code này mình đang sử dụng python 3 nhé, nếu có lỗi chắc do bạn đang xài python 2x đó :D 

Kết luận

Với một vài dòng lệnh bạn đã làm cho code Python của mình chạy nhanh hơn nhiều rồi đấy. Bài viết là một giới thiệu cơ bản kèm theo ví dụ, bạn có thể làm nhiều thứ hơn thế tùy theo nhu cầu của bạn. Nếu muốn làm nhiều thứ hơn hãy thử đọc docs của nó, link mình để ở dưới phần reference nhé.

Nếu bài viết hữu ích với bạn thì cho mình một đánh giá tốt nhé. Cảm ơn bạn quan tâm. Chúc bạn học tốt!

References:

https://cython.readthedocs.io/en/latest/src/tutorial/cython_tutorial.html

https://medium.com/mathematicallygifted/cython-use-it-to-speed-up-python-code-with-examples-cd5a90b32fc7