Python là ngôn ngữ kịch bản trong khi C là ngôn ngữ lập trình. C/C++ tương đối nhanh so với Python vì khi bạn chạy tập lệnh Python, trình thông dịch của nó sẽ diễn giải từng dòng tập lệnh và tạo đầu ra nhưng trong C, trình biên dịch trước tiên sẽ biên dịch nó và tạo đầu ra được tối ưu hóa đối với . Trong trường hợp các ngôn ngữ khác như Java và. NET, mã byte Java và. NET bytecode tương ứng chạy nhanh hơn Python vì trình biên dịch JIT biên dịch bytecode thành mã gốc khi chạy. CPython không thể có trình biên dịch JIT vì bản chất động của Python gây khó khăn cho việc viết một trình biên dịch Show
Sự khác biệtNhư chúng ta đã biết, Python là ngôn ngữ thông dịch, trong khi C là ngôn ngữ biên dịch. Mã được giải thích luôn chậm hơn mã máy trực tiếp vì cần nhiều hướng dẫn hơn để thực hiện lệnh được giải thích hơn là thực hiện lệnh máy thực tế. Trước khi chương trình có thể thực hiện công việc thực tế, CPU phải hiểu các hướng dẫn Python. Vì vậy, trình thông dịch Python kiểm tra từng câu lệnh theo các quy tắc của ngôn ngữ Python, chẳng hạn như phân bổ bộ nhớ để lưu trữ các biến, loại bỏ các khoảng trống và nhận xét từ chương trình và các tác vụ liên quan khác. Quá trình này được lặp lại cho mỗi dòng của chương trình và làm tăng đáng kể chi phí thực thi chương trình. C nhanhMặt khác, C không mất nhiều thời gian kiểm tra lại mã nguồn và nhanh chóng chuyển thành các lệnh của CPU. Trước khi thực thi, một trình biên dịch riêng biệt sẽ chuyển đổi chương trình ngôn ngữ của con người thành các lệnh của CPU, kiểm tra lỗi, phân bổ bộ nhớ và các biến, loại bỏ các nhận xét và khoảng trống, đồng thời tối ưu hóa các lệnh kết quả. Kết quả của mã được biên dịch được liên kết với các mã được tạo sẵn khác và kết quả là bạn sẽ nhận được các lệnh CPU vững chắc, sẵn sàng thực hiện công việc được giao mà không cần chuẩn bị nhiều. Trong nội bộ, lý do khiến mã Python thực thi chậm hơn là do mã được diễn giải trong thời gian chạy thay vì được biên dịch thành mã gốc tại thời điểm biên dịch Python chậm và các ngôn ngữ được biên dịch như Rust, C hoặc C++ thì nhanh. Vì vậy, khi ứng dụng của bạn quá chậm, việc viết lại một số mã của bạn trong tiện ích mở rộng đã biên dịch có vẻ như là cách tiếp cận tự nhiên để tăng tốc mọi thứ Thật không may, các tiện ích mở rộng được biên dịch đôi khi thực sự chậm hơn mã Python tương đương. Và ngay cả khi chúng nhanh hơn, cải thiện hiệu suất có thể ít hơn nhiều so với bạn tưởng tượng, do chi phí ẩn gây ra bởi hai yếu tố
Hãy xem những chi phí hoạt động ẩn này đến từ đâu và sau đó xem một số giải pháp để khắc phục chúng Vấn đề #1. Chi phí cuộc gọiChi phí hoạt động đầu tiên mà chúng ta sẽ đối mặt là các lệnh gọi hàm. Hãy viết một hàm bằng Cython, một ngôn ngữ biến thể của Python biên dịch thành C và xem mất bao lâu để chạy nó Đây là hàm Cython
Chúng ta có thể sử dụng chức năng ma thuật IPython 1 để đo lường hiệu suất
Gọi hàm Cython nhanh hơn gọi hàm Python, đó là sự thật. Nhưng thậm chí 30 nano giây cũng khá chậm so với tiêu chuẩn của các ngôn ngữ được biên dịch. để so sánh, một hàm C được gọi bởi một hàm C khác có thể chỉ mất 3 nano giây hoặc ít hơn nhiều nếu nó được nội tuyến Vấn đề #2. (De)chi phí xê-ri hóaNgoài chi phí gọi tiện ích mở rộng, còn có chi phí đưa các đối số vào hàm và nhận lại kết quả. Cách Python đại diện cho các đối tượng khác với cách C hoặc Rust đại diện cho nó và do đó cần có quá trình chuyển đổi. Và mã chuyển đổi cũng cần xử lý lỗi trong trường hợp không thành công Kết quả là nhiều chi phí hơn. Ví dụ: đây là một hàm Cython lấy danh sách các số nguyên trong Python, tính tổng và trả về kết quả
Chúng ta có thể so sánh hiệu suất với Python
Mã Cython được biên dịch không nhanh hơn 2 tích hợp sẵn của Python. Và điều đó không có gì đáng ngạc nhiên. 2 được viết bằng C và toán học thực tế khá nhanh như chúng ta sẽ thấy bên dưới. Tất cả thời gian chạy được dành để chuyển đổi các đối tượng Python thành số nguyên CVậy làm thế nào để chúng ta giải quyết hai hình thức chi phí này? Giải pháp số 1. Quản lý dữ liệu bên trong tiện ích mở rộngGiải pháp đầu tiên là quản lý tất cả dữ liệu của chúng tôi bằng tiện ích mở rộng Rust hoặc C, điều đó có nghĩa là chúng tôi không có tất cả chi phí tuần tự hóa Đây là những gì NumPy làm với mảng. về lý thuyết, nó có thể đã sử dụng các danh sách Python, nhưng sau đó nó sẽ bị (khử) tuần tự hóa. Vì vậy, thay vào đó, các mảng NumPy lưu trữ các số bên trong không phải dưới dạng danh sách Python của các số nguyên Python, mà là các mảng C của các số nguyên C. Do đó, các thao tác trên mảng NumPy không yêu cầu giải tuần tự hóa mọi mục nhập trong mảng Ví dụ: chúng ta có thể tạo một mảng NumPy là một dãy số. Điều đó sẽ liên quan đến việc tạo một mảng C với các số nguyên C, ít công việc hơn (và ít bộ nhớ hơn. ) so với danh sách Python của các số nguyên Python. Sau đó, chúng ta có thể tính tổng và tất cả logic đó sẽ ở trong C, không cần giải tuần tự hóa ngoại trừ tổng cuối cùng
Mã NumPy mất ít hơn một phần nghìn giây. nó nhanh hơn 12 lần so với Python. Sự thành công Giải pháp số 2. Chuyển nhiều công việc hơn vào tiện ích mở rộngMột cách tiếp cận khác mà chúng ta có thể thực hiện là chuyển nhiều tính toán hơn vào phần mở rộng. Trong tất cả các ví dụ của chúng tôi, chúng tôi đã tính tổng một dãy số, thường là từ 0 đến 999.999. Nếu đó là tất cả những gì chúng ta cần làm, chúng ta không cần phải tạo toàn bộ danh sách các số trong Python, chúng ta chỉ cần viết một hàm Cython tính tổng một dãy số nguyên ________số 8_______ Chúng ta có thể đo lường hiệu suất
Điều đó khá tốt, thậm chí còn nhanh hơn so với triển khai của NumPy, ngoài ra chúng tôi không có chi phí tạo toàn bộ mảng nhưng chúng ta có thể làm tốt hơn Hãy chuyển đổi ngôn ngữ và dùng thử Rust. Thật không may, các ràng buộc Rust Python thông qua thư viện PyO3 có chi phí gọi và (khử) tuần tự hóa cao hơn Cython, vì PyO3 là một dự án mới hơn. Điều này đang được cải thiện theo thời gian. PyO3 v0. Ví dụ, bản phát hành 14 đã giảm đáng kể chi phí cuộc gọi chức năng. Về mặt tích cực, Rust có tính năng an toàn bộ nhớ, an toàn luồng và ngôn ngữ cấp cao hơn nhiều mà vẫn có hiệu năng tương tự như C/C++ Trong Rust, chúng ta có thể sử dụng một đối tượng phạm vi không khác nhiều so với các lát cắt của Python
Sau đó chúng ta có thể đo lường hiệu suất
Lưu ý kết quả tính bằng nano giây. Rust nhanh hơn 1.800 lần so với Cython–làm thế nào điều này có thể xảy ra? Giải pháp tốt nhất. làm ít việc hơnHóa ra là khi chúng tôi chuyển nhiều công việc hơn sang một công trình cấp cao như Rust's Ranges, chúng tôi đã có được một giải pháp thông minh hơn nhiều. Tính tổng một mảng số nguyên tùy ý yêu cầu lặp qua tất cả các giá trị và do đó, nó là 4. càng nhiều giá trị trong mảng thì càng mất nhiều thời gian. Và tính tổng một dãy số nguyên liên tiếp có thể được thực hiện theo cách tương tựHoặc chúng ta có thể tận dụng thực tế là nó diễn ra liên tiếp, trong trường hợp đó, nó có thể được thực hiện trong một khoảng thời gian cố định, ví dụ như sử dụng 5. Chuỗi công cụ biên dịch LLVM mà Rust sử dụng đủ thông minh để sử dụng phép tính thời gian cố định (hơi khác một chút). Bạn có thể xem kết quả lắp ráp tại đây, nếu bạn quan tâm. Hãy nhớ rằng điều này không thực sự cụ thể cho Rust; Với sự tối ưu hóa này, không giống như triển khai tính tổng trong vòng lặp, kích thước của phạm vi không quan trọng lắm
Chúng ta cũng có thể triển khai thuật toán nhanh hơn trong Python 0Mã Rust của chúng tôi nhanh hơn triển khai Python này, nhưng chỉ một chút. Và việc triển khai Python của chúng tôi vẫn nhanh hơn rất nhiều so với các giải pháp Cython hoặc NumPy mà chúng tôi đã sử dụng ở trên. Bằng cách hạn chế vấn đề, chúng tôi đã đưa ra một giải pháp vượt trội về cơ bản và rất có thể nó đủ nhanh để không cần một tiện ích mở rộng đã biên dịch nào cả
Giảm chi phí mở rộngChúng ta đã học được gì về việc tối ưu hóa mã Python? Bắt đầu bằng cách cố gắng xác định lại vấn đề theo cách ít tốn công sức hơn. Bạn có thể tăng tốc đáng kể bằng cách sử dụng Python cũ đơn giản Nếu bạn cần phải viết một phần mở rộng
Bài viết tiếp theo. Cython, Rust, v.v. chọn ngôn ngữ cho tiện ích mở rộng Python Tìm các tắc nghẽn về hiệu suất và bộ nhớ trong mã xử lý dữ liệu của bạn với trình cấu hình SciagraphCác công việc chạy chậm sẽ lãng phí thời gian của bạn trong quá trình phát triển, cản trở người dùng của bạn và tăng chi phí tính toán của bạn. Tăng tốc mã của bạn và bạn sẽ lặp lại nhanh hơn, có người dùng hài lòng hơn và bám sát ngân sách của mình—nhưng trước tiên, bạn cần xác định nguyên nhân của sự cố Tìm các nút thắt cổ chai về hiệu suất và bộ nhớ trong khoa học dữ liệu của bạn Công việc Python với trình hồ sơ Sciagraph. Cấu hình trong quá trình phát triển và sản xuất, với sự hỗ trợ đa xử lý và hơn thế nữa Tìm hiểu các kỹ năng kỹ thuật phần mềm Python thực tế mà bạn có thể sử dụng trong công việc của mìnhĐăng ký nhận bản tin của tôi và tham gia cùng hơn 6700 nhà phát triển Python và nhà khoa học dữ liệu học các công cụ và kỹ thuật thực tế, từ hiệu suất Python đến đóng gói Docker, với một bài viết mới miễn phí trong hộp thư đến của bạn mỗi tuần Python được biên dịch nhanh hơn C?Đó là 450 triệu vòng lặp trong một giây, nhanh hơn Python 45 lần . Hơn nữa, C có thể được biên dịch ở chế độ tối ưu hóa để có hiệu suất tốt hơn.
Python có chạy nhanh hơn khi được biên dịch không?Biên dịch Python
Mã máy mà bạn tạo khi biên dịch mã sẽ luôn chạy nhanh hơn mã byte được giải thích . Bạn sẽ tìm thấy một số trình biên dịch Python có sẵn bao gồm Numpa, Nuitka, pypi và Cython.
Python có thể hiệu quả như C không?Do là ngôn ngữ được thông dịch và nhập động, Python cho phép tốc độ tạo mẫu cực nhanh nhưng không thể cạnh tranh với thời gian chạy của C++, C, Fortran, cũng như . .
Cython có nhanh bằng C không?Mã Cython chạy nhanh nhất khi ở dạng 'C thuần túy'
. |