Một siêu bộ Python biên dịch thành C, Cython kết hợp sự dễ dàng của Python và tốc độ của mã gốc
Nhà văn cao cấp, InfoWorld.
suy nghĩ
Mục lục
Cho xem nhiều hơn
Python nổi tiếng là một trong những ngôn ngữ lập trình tiện lợi nhất, được trang bị phong phú và hết sức hữu ích. Tốc độ thực hiện?
Nhập Cython. Ngôn ngữ Cython là một siêu tập hợp của Python biên dịch thành C, mang lại mức tăng hiệu suất có thể từ vài phần trăm đến vài bậc độ lớn, tùy thuộc vào tác vụ hiện tại. Đối với công việc bị ràng buộc bởi các loại đối tượng gốc của Python, tốc độ tăng tốc sẽ không lớn. Nhưng đối với các hoạt động số hoặc bất kỳ hoạt động nào không liên quan đến nội bộ của Python, lợi nhuận có thể rất lớn.
[ Cũng trên InfoWorld. Cách sử dụng asyncio trong Python]
Với Cython, bạn có thể bỏ qua nhiều giới hạn bản địa của Python hoặc vượt qua chúng hoàn toàn—mà không phải từ bỏ sự dễ dàng và tiện lợi của Python. Trong bài viết này, chúng ta sẽ tìm hiểu các khái niệm cơ bản đằng sau Cython và tạo một ứng dụng Python đơn giản sử dụng Cython để tăng tốc một trong các chức năng của nó
video liên quan. Sử dụng Cython để tăng tốc Python
Biên dịch Python sang C
Mã Python có thể thực hiện cuộc gọi trực tiếp vào các mô-đun C. Các mô-đun C đó có thể là thư viện C chung hoặc thư viện được xây dựng riêng để hoạt động với Python. Cython tạo ra loại mô-đun thứ hai. Thư viện C nói chuyện với nội bộ của Python và có thể đi kèm với mã Python hiện có
Mã Cython trông rất giống mã Python, theo thiết kế. Nếu bạn cung cấp cho trình biên dịch Cython một chương trình Python [Python 2. x và Python3. x đều được hỗ trợ], Cython sẽ chấp nhận nó như hiện tại, nhưng không có khả năng tăng tốc riêng nào của Cython sẽ phát huy tác dụng. Nhưng nếu bạn trang trí mã Python bằng các chú thích loại theo cú pháp đặc biệt của Cython, thì Cython sẽ có thể thay thế các đối tượng Python chậm tương đương với C nhanh
Lưu ý rằng cách tiếp cận của Cython là gia tăng. Điều đó có nghĩa là nhà phát triển có thể bắt đầu với ứng dụng Python hiện có và tăng tốc ứng dụng đó bằng cách thực hiện các thay đổi tại chỗ đối với mã, thay vì viết lại toàn bộ ứng dụng từ đầu
Cách tiếp cận này phù hợp với bản chất của các vấn đề về hiệu suất phần mềm nói chung. Trong hầu hết các chương trình, phần lớn mã sử dụng nhiều CPU tập trung ở một số điểm nóng—một phiên bản của nguyên tắc Pareto, còn được gọi là quy tắc “80/20”. Do đó, hầu hết mã trong ứng dụng Python không cần phải được tối ưu hóa hiệu suất, chỉ cần một số phần quan trọng. Bạn có thể dịch từng bước các điểm nóng đó sang Cython và do đó đạt được mức tăng hiệu suất mà bạn cần ở nơi quan trọng nhất. Phần còn lại của chương trình có thể vẫn ở dạng Python để thuận tiện cho các nhà phát triển
Cách sử dụng Cyton
Hãy xem xét đoạn mã sau, được lấy từ tài liệu của Cython
def f[x]:
return x**2-x
def integrate_f[a, b, N]:
s = 0
dx = [b-a]/N
for i in range[N]:
s += f[a+i*dx]
return s * dx
Đây là một ví dụ về đồ chơi, một cách triển khai hàm tích phân không hiệu quả lắm. Là mã Python thuần túy, nó chậm, vì Python phải chuyển đổi qua lại giữa các loại số gốc của máy và các loại đối tượng bên trong của chính nó
Bây giờ hãy xem xét phiên bản Cython của cùng một mã, với phần bổ sung của Cython được gạch dưới
cdef double f[double x]:
return x**2-x
def integrate_f[double a, double b, int N]:
cdef int i
cdef double s, x, dx
s = 0
dx = [b-a]/N
for i in range[N]:
s += f[a+i*dx]
return s * dx
Nếu chúng ta khai báo rõ ràng các loại biến, cho cả các tham số của hàm và các biến được sử dụng trong phần thân của hàm [double
, int
, v.v. ], Cython sẽ dịch tất cả những thứ này sang C. Chúng ta cũng có thể sử dụng từ khóa cdef
để xác định các hàm được triển khai chủ yếu bằng C để tăng tốc độ, mặc dù các hàm đó chỉ có thể được gọi bởi các hàm Cython khác chứ không phải bởi các tập lệnh Python. Trong ví dụ trên, chỉ có thể gọi integrate_f
bằng một tập lệnh Python khác, bởi vì nó sử dụng
cdef double f[double x]:0;
return x**2-x
def integrate_f[double a, double b, int N]:
cdef int i
cdef double s, x, dx
s = 0
dx = [b-a]/N
for i in range[N]:
s += f[a+i*dx]
return s * dx
Lưu ý rằng mã thực tế của chúng tôi đã thay đổi rất ít. Tất cả những gì chúng tôi đã làm là thêm khai báo kiểu vào mã hiện có để tăng hiệu suất đáng kể
Ưu điểm của Cython
Ngoài khả năng tăng tốc mã bạn đã viết, Cython còn mang lại một số lợi thế khác
Làm việc với các thư viện C bên ngoài có thể nhanh hơn
Các gói Python như NumPy bọc các thư viện C trong giao diện Python để giúp chúng dễ dàng làm việc với. Tuy nhiên, qua lại giữa Python và C thông qua các trình bao bọc đó có thể làm mọi thứ chậm lại. Cython cho phép bạn nói chuyện trực tiếp với các thư viện bên dưới mà không cần Python cản trở. [Các thư viện C++ cũng được hỗ trợ. ]
Bạn có thể sử dụng cả quản lý bộ nhớ C và Python
Nếu bạn sử dụng các đối tượng Python, chúng sẽ được quản lý bộ nhớ và thu gom rác giống như trong Python thông thường. Nhưng nếu bạn muốn tạo và quản lý các cấu trúc cấp độ C của riêng mình và sử dụng
cdef double f[double x]:2/
return x**2-x
def integrate_f[double a, double b, int N]:
cdef int i
cdef double s, x, dx
s = 0
dx = [b-a]/N
for i in range[N]:
s += f[a+i*dx]
return s * dx
cdef double f[double x]:3 để làm việc với chúng, bạn có thể làm như vậy. Chỉ cần nhớ để làm sạch sau khi chính mình
return x**2-x
def integrate_f[double a, double b, int N]:
cdef int i
cdef double s, x, dx
s = 0
dx = [b-a]/N
for i in range[N]:
s += f[a+i*dx]
return s * dx
Bạn có thể lựa chọn an toàn hoặc tốc độ khi cần thiết
Cython tự động thực hiện kiểm tra thời gian chạy đối với các sự cố phổ biến xuất hiện trong C, chẳng hạn như truy cập vượt quá giới hạn trên một mảng, bằng cách trang trí và chỉ thị trình biên dịch [e. g. ,
cdef double f[double x]:0]. Do đó, mã C do Cython tạo theo mặc định an toàn hơn nhiều so với mã C cuộn thủ công, mặc dù có khả năng phải trả giá bằng hiệu năng thô.
return x**2-x
def integrate_f[double a, double b, int N]:
cdef int i
cdef double s, x, dx
s = 0
dx = [b-a]/N
for i in range[N]:
s += f[a+i*dx]
return s * dx
Nếu bạn tự tin rằng mình sẽ không cần những kiểm tra đó trong thời gian chạy, bạn có thể tắt chúng để tăng thêm tốc độ, trên toàn bộ mô-đun hoặc chỉ trên các chức năng được chọn
Cython cũng cho phép bạn truy cập nguyên bản các cấu trúc Python sử dụng giao thức bộ đệm để truy cập trực tiếp vào dữ liệu được lưu trữ trong bộ nhớ [không cần sao chép trung gian]. Chế độ xem bộ nhớ của Cython cho phép bạn làm việc với các cấu trúc đó ở tốc độ cao và với mức độ an toàn phù hợp với nhiệm vụ. Chẳng hạn, dữ liệu thô bên dưới chuỗi Python có thể được đọc theo cách này [nhanh] mà không cần phải trải qua thời gian chạy Python [chậm]
Mã Cython C có thể hưởng lợi từ việc phát hành GIL
Khóa thông dịch viên toàn cầu Python, hoặc GIRL, các luồng được đồng bộ hóa trong trình thông dịch, bảo vệ quyền truy cập vào các đối tượng Python và quản lý tranh chấp tài nguyên. Nhưng GIL đã bị chỉ trích rộng rãi như một trở ngại đối với Python hoạt động tốt hơn, đặc biệt là trên các hệ thống đa lõi
Nếu bạn có một đoạn mã không tham chiếu đến các đối tượng Python và thực hiện một thao tác kéo dài, thì bạn có thể đánh dấu đoạn mã đó bằng lệnh
cdef double f[double x]:1 để cho phép đoạn mã đó chạy mà không cần GIL. Điều này giải phóng trình thông dịch Python để tạm thời làm những việc khác và cho phép mã Cython sử dụng nhiều lõi [với công việc bổ sung]
return x**2-x
def integrate_f[double a, double b, int N]:
cdef int i
cdef double s, x, dx
s = 0
dx = [b-a]/N
for i in range[N]:
s += f[a+i*dx]
return s * dx
Cython có thể sử dụng cú pháp gợi ý kiểu Python
Python có một cú pháp gợi ý kiểu được sử dụng chủ yếu bởi những người kiểm tra mã và mã, thay vì trình thông dịch CPython. Cython có cú pháp tùy chỉnh riêng để trang trí mã, nhưng với các bản sửa đổi gần đây của Cython, bạn có thể sử dụng cú pháp gợi ý kiểu Python để cung cấp các gợi ý kiểu cơ bản cho Cython. Được gọi là "chế độ Python thuần túy", nó không cho phép đầy đủ các tính năng của Cython, nhưng đủ để bạn có thể hoàn thành nhiều tác vụ giống nhau với các ký hiệu ít khó xử hơn. Đọc thêm về cách sử dụng chế độ thuần Python trong bài viết riêng của chúng tôi
Cython có thể được sử dụng để che khuất mã Python nhạy cảm
Các mô-đun Python rất dễ dịch ngược và kiểm tra, nhưng các tệp nhị phân đã biên dịch thì không. Khi phân phối ứng dụng Python cho người dùng cuối, nếu bạn muốn bảo vệ một số mô-đun của nó khỏi bị rình mò thông thường, bạn có thể làm như vậy bằng cách biên dịch chúng bằng Cython
Tuy nhiên, xin lưu ý rằng đây là tác dụng phụ của các khả năng của Cython, không phải là một trong các chức năng dự định của nó. Ngoài ra, không thể dịch ngược hoặc thiết kế ngược một tệp nhị phân nếu một tệp được dành riêng hoặc đủ quyết tâm. Và theo nguyên tắc chung, các bí mật, chẳng hạn như mã thông báo hoặc thông tin nhạy cảm khác, không bao giờ được ẩn trong các tệp nhị phân -- chúng thường rất dễ bị lộ bằng một kết xuất hex đơn giản
hạn chế của Cython
Hãy nhớ rằng Cython không phải là cây đũa thần. Nó không tự động biến mọi phiên bản mã Python tẻ nhạt thành mã C cực nhanh. Để tận dụng tối đa Cython, bạn phải sử dụng nó một cách khôn ngoan—và hiểu những hạn chế của nó
Tăng tốc một chút cho mã Python thông thường
Khi Cython gặp mã Python, nó không thể dịch hoàn toàn sang C, nó sẽ chuyển đổi mã đó thành một loạt lệnh gọi C đến phần bên trong của Python. Điều này tương đương với việc đưa trình thông dịch của Python ra khỏi vòng lặp thực thi, giúp mã tăng tốc khiêm tốn từ 15 đến 20 phần trăm theo mặc định. Lưu ý rằng đây là trường hợp tốt nhất;
Tăng tốc một chút cho cấu trúc dữ liệu Python gốc
Python cung cấp một loạt cấu trúc dữ liệu—chuỗi, danh sách, bộ dữ liệu, từ điển, v.v. Chúng cực kỳ tiện lợi cho các nhà phát triển và chúng có tính năng quản lý bộ nhớ tự động của riêng chúng. Nhưng chúng chậm hơn C thuần túy
Cython cho phép bạn tiếp tục sử dụng tất cả các cấu trúc dữ liệu Python, mặc dù không tăng tốc nhiều. Một lần nữa, điều này là do Cython chỉ đơn giản gọi các API C trong thời gian chạy Python để tạo và thao tác các đối tượng đó. Do đó, các cấu trúc dữ liệu Python hoạt động giống như mã Python được tối ưu hóa cho Cython nói chung. Đôi khi bạn nhận được một sự thúc đẩy, nhưng chỉ một chút. Để có kết quả tốt nhất, hãy sử dụng các biến và cấu trúc C. Tin tốt là Cython giúp bạn dễ dàng làm việc với chúng
Mã Cython chạy nhanh nhất khi “thuần C”
Nếu bạn có một hàm trong C được gắn nhãn với từ khóa cdef
, với tất cả các biến của nó và lệnh gọi hàm nội tuyến tới những thứ khác thuần C, thì nó sẽ chạy nhanh nhất có thể. Nhưng nếu chức năng đó tham chiếu đến bất kỳ mã gốc Python nào, chẳng hạn như cấu trúc dữ liệu Python hoặc lệnh gọi đến API Python nội bộ, thì lệnh gọi đó sẽ là nút cổ chai hiệu năng
May mắn thay, Cython cung cấp một cách để phát hiện những tắc nghẽn này. báo cáo mã nguồn cho biết nhanh phần nào trong ứng dụng Cython của bạn là C thuần túy và phần nào tương tác với Python. Ứng dụng càng được tối ưu hóa tốt thì càng ít tương tác với Python
IDGBáo cáo mã nguồn được tạo cho ứng dụng Cython. Các khu vực màu trắng là C tinh khiết; . Một chương trình Cython được tối ưu hóa tốt sẽ có càng ít màu vàng càng tốt. Dòng cuối cùng được mở rộng hiển thị mã C làm cơ sở cho mã Cython tương ứng của nó
Cython NumPy
Cython cải thiện việc sử dụng các thư viện xử lý số của bên thứ ba dựa trên C như NumPy. Vì mã Cython biên dịch thành C nên nó có thể tương tác trực tiếp với các thư viện đó và loại bỏ các nút cổ chai của Python ra khỏi vòng lặp
Nhưng NumPy, đặc biệt, hoạt động tốt với Cython. Cython có hỗ trợ riêng cho các cấu trúc cụ thể trong NumPy và cung cấp quyền truy cập nhanh vào các mảng NumPy. Và cùng một cú pháp NumPy quen thuộc mà bạn sử dụng trong tập lệnh Python thông thường có thể được sử dụng trong Cython nguyên trạng
Tuy nhiên, nếu bạn muốn tạo các ràng buộc gần nhất có thể giữa Cython và NumPy, bạn cần trang trí thêm mã bằng cú pháp tùy chỉnh của Cython. Chẳng hạn, câu lệnh
cdef double f[double x]:3 cho phép mã Cython xem cấu trúc cấp C trong thư viện tại thời điểm biên dịch để có liên kết nhanh nhất có thể
return x**2-x
def integrate_f[double a, double b, int N]:
cdef int i
cdef double s, x, dx
s = 0
dx = [b-a]/N
for i in range[N]:
s += f[a+i*dx]
return s * dx
Vì NumPy được sử dụng rộng rãi nên Cython hỗ trợ NumPy “ngay lập tức. ” Nếu bạn đã cài đặt NumPy, bạn chỉ cần nêu
cdef double f[double x]:4 trong mã của mình, sau đó thêm phần trang trí khác để sử dụng các chức năng được hiển thị.
return x**2-x
def integrate_f[double a, double b, int N]:
cdef int i
cdef double s, x, dx
s = 0
dx = [b-a]/N
for i in range[N]:
s += f[a+i*dx]
return s * dx
Hồ sơ và hiệu suất của Cython
Bạn có được hiệu suất tốt nhất từ bất kỳ đoạn mã nào bằng cách lập hồ sơ cho nó và tận mắt nhìn thấy các điểm tắc nghẽn. Cython cung cấp các hook cho mô-đun cProfile của Python, vì vậy bạn có thể sử dụng các công cụ định hình riêng của Python, như cProfile, để xem mã Cython của bạn hoạt động như thế nào.
Điều này giúp ghi nhớ trong mọi trường hợp rằng Cython không phải là ma thuật—các phương pháp hiệu suất hợp lý trong thế giới thực vẫn được áp dụng. Bạn càng ít chuyển đổi qua lại giữa Python và Cython, ứng dụng của bạn sẽ chạy càng nhanh
Chẳng hạn, nếu bạn có một tập hợp các đối tượng mà bạn muốn xử lý trong Cython, đừng lặp lại nó trong Python và gọi một hàm Cython ở mỗi bước. Chuyển toàn bộ bộ sưu tập tới mô-đun Cython của bạn và lặp lại ở đó. Kỹ thuật này thường được sử dụng trong các thư viện quản lý dữ liệu, vì vậy đây là một mô hình tốt để mô phỏng trong mã của riêng bạn
Chúng tôi sử dụng Python vì nó mang lại sự thuận tiện cho lập trình viên và cho phép phát triển nhanh. Đôi khi năng suất của lập trình viên phải trả giá bằng hiệu suất. Với Cython, chỉ cần thêm một chút nỗ lực là bạn có thể đạt được điều tốt nhất của cả hai thế giới
Đọc thêm về Python
- Trăn là gì?
- PyPy là gì?
- Cython là gì?
- hướng dẫn Cython. Cách tăng tốc Python
- Cách cài đặt Python một cách thông minh
- Các tính năng mới tốt nhất trong Python 3. 8
- Quản lý dự án Python tốt hơn với Thơ
- Virtualenv và venv. Giải thích môi trường ảo Python
- Python virtualenv và venv nên và không nên làm
- Giải thích luồng Python và các quy trình con
- Cách sử dụng trình gỡ lỗi Python
- Cách sử dụng timeit để cấu hình mã Python
- Cách sử dụng cProfile để cấu hình mã Python
- Bắt đầu với async trong Python
- Cách sử dụng asyncio trong Python
- Cách chuyển đổi Python sang JavaScript [và ngược lại]
- Python 2 EOL. Làm thế nào để sống sót khi kết thúc Python 2
- 12 Pythons cho mọi nhu cầu lập trình
- 24 thư viện Python cho mọi nhà phát triển Python
- 7 IDE Python thú vị mà bạn có thể đã bỏ lỡ
- 3 thiếu sót lớn của Python — và giải pháp của chúng
- 13 khung web Python được so sánh
- 4 khung kiểm tra Python để loại bỏ lỗi của bạn
- 6 tính năng Python mới tuyệt vời mà bạn không muốn bỏ lỡ
- 5 bản phân phối Python để thành thạo máy học
- 8 thư viện Python tuyệt vời để xử lý ngôn ngữ tự nhiên
Có liên quan
- con trăn
- Phát triển phần mềm
- Công cụ phát triển
- Mã nguồn mở
Serdar Yegulalp là một nhà văn cao cấp tại InfoWorld, tập trung vào học máy, container hóa, devops, hệ sinh thái Python và đánh giá định kỳ