Python là một ngôn ngữ tuyệt vời, rất dễ tiếp thu, phát triển rất nhanh và rất rõ ràng để đọc. Tất cả những lợi ích này đều phải trả giá. Python khá chậm so với một số ngôn ngữ khác. Tôi thực sự khuyên bạn nên đọc bài viết này trước khi tiếp tục hiểu rõ vấn đề mà chúng tôi đang cố gắng giải quyết. Mục tiêu của bài viết này là để trả lời câu hỏi này
Làm cách nào chúng ta có thể kết hợp tính dễ phát triển của Python mà không làm giảm tốc độ?
Câu trả lời mỉa mai là viết lại dự án bằng ngôn ngữ khác nhưng đó không phải là mục đích chúng tôi ở đây. Bạn là một lập trình viên Python, đã có rất nhiều chương trình viết bằng Python và chỉ muốn tăng tốc một phần nhỏ của nó. Cũng. nếu bạn đã quen viết bằng Python, việc chuyển đổi sang ngôn ngữ khác như C# hoặc Java có thể khá khó khăn
Chúng tôi sẽ kết hợp tốt nhất của hai thế giới. chúng tôi mở rộng chương trình của mình bằng một mô-đun nhỏ mà chúng tôi viết bằng C. Các lập trình viên Python chỉ cần nhập gói này, không cần biết một dòng C nào và vẫn có thể tận hưởng tốc độ tăng gấp 100 lần
Nhập mô-đun C vào chương trình Python của chúng tôi [hình ảnh của [Kat Sazonova trên Bapt]
Viết bằng C?
“Viết bằng chữ C. ?”
“Bạn vừa nói về một quá trình chuyển đổi sơ bộ sang Java, giờ chúng ta sẽ chuyển sang C?. ”. Đúng, viết mã bằng C có thể hơi khó khăn nhưng bạn sẽ thấy tốc độ tăng gấp 100 lần hoàn toàn xứng đáng
Ngoài ra, chúng tôi chỉ phải viết lại một phần nhỏ mã của mình sang C [trong trường hợp của chúng tôi chỉ là một hàm duy nhất]
Điều này không giống như viết lại dự án bằng ngôn ngữ khác sao?
Cái hay của giải pháp mà bài viết này mô tả là bạn chỉ phải viết lại những phần chậm trong mã của mình. Hãy tưởng tượng chúng ta đã lập trình một API bằng Python để nhận và phân tích các tệp âm thanh. Nên viết lại hàm phân tích tệp âm thanh trong C vì đây là nút cổ chai của dự án. Chúng tôi sẽ lãng phí rất nhiều thời gian để viết lại API của mình
Điều này không thể được thực hiện đơn giản hơn?
Vâng, có nhiều cách đơn giản hơn để tạo tiện ích mở rộng C sẽ tăng tốc chương trình của chúng tôi rất nhiều. Trong bài viết này, chúng tôi đã sử dụng Cython để chuyển đổi một số mã giống như Python thành mô-đun C để đạt được mức tăng hiệu suất gần như tương tự
Tuy nhiên, trong bài đăng này, chúng tôi sẽ thực hiện một cách khó khăn và viết mô-đun của riêng mình bằng C vì nó cho chúng tôi cái nhìn rất thú vị về hoạt động bên trong của Python và cách nó tích hợp các mô-đun được viết bằng C
Khi nào thì tạo một mô-đun C là hợp lý?
Loại tác vụ mà chúng tôi có thể tối ưu hóa là tác vụ nặng về CPU, không phải tác vụ I/O như chờ phản hồi. Chờ API không nhanh hơn trong 'ngôn ngữ nhanh hơn'
Chúng tôi muốn tối ưu hóa một phần nhỏ mã của chúng tôi để thực hiện một tác vụ rất nặng về CPU. Những loại tác vụ này rất phù hợp để tối ưu hóa trong C
Hãy thiết lập cửa hàng và tăng tốc chương trình này [hình ảnh của Damir Kopezhanov trên Bapt]Thiết lập
điều đầu tiên đầu tiên. thiết lập môi trường ảo. Điều này không thực sự cần thiết nhưng đó là cách tốt nhất để giữ cho các phần phụ thuộc của bạn không bị rối
Như bạn đã đọc ở phần trước, chúng ta sẽ cần một hàm tính toán nhiều. Chúng tôi sẽ sử dụng một ví dụ đơn giản. tính số lượng các số nguyên tố trong một phạm vi. Đây là mã Python vanilla sẽ làm điều đó
Đoạn mã trên trông hơi lộn xộn và tôi nghe bạn nghĩ “WHILE LOOPS?. CỜ?. ”. Tin tôi đi, họ ở đó vì một lý do chính đáng
Cũng lưu ý rằng đây không phải là cách hiệu quả nhất để tính số nguyên tố nhưng đó không phải là vấn đề. chúng ta chỉ cần một hàm cần nhiều tính toán
Bạn đã sử dụng các chức năng biên dịch C
Thay vì các vòng lặp và cờ while, chúng ta có thể sử dụng hàm có sẵn range[]
. Điều này vượt qua quá trình tạo, lặp lại và kiểm tra xem chúng tôi đã hoàn thành mô-đun C nhanh hơn nhiều chưa. Hãy nâng cấp chức năng khó chịu đó với range[]
Lưu ý rằng mã này không chỉ dễ đọc hơn. nó cũng nhanh hơn. Chúng ta có thể sử dụng hai hàm này để tra cứu số lượng các số nguyên tố từ 0 đến 100. 000
[Vanilla] examined 100000 numbers; found 9592 primes in 30.38632 sec
[V+range] examined 100000 numbers; found 9592 primes in 20.00026 sec
Việc sử dụng một số mô-đun C tích hợp đã cải thiện một chút tốc độ thực thi nhưng chúng tôi mới chỉ bắt đầu
Chúng ta sẽ phải làm bẩn tay một chút nhưng kết quả sẽ rất phi thường [hình ảnh của Adi Goldstein trên Bapt] Viết mô-đun C cho Python
Việc đầu tiên chúng ta phải làm là chuyển hàm tìm nguyên tố sang C. Sau đó, bằng cách nào đó, chúng ta phải khiến Python nói chuyện với hàm C đó. Giải pháp cho việc này là bọc hàm C trong mô-đun Python
Bạn đã quen thuộc với những điều này; . g. Chúng tôi sẽ gọi mô-đun của chúng tôi là Fastcount
Ở cuối phần này, chúng ta sẽ cài đặt mô-đun để bạn có thể nhập mô-đun và thực thi một phương thức trên mô-đun như thế này
import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
Chúng tôi sẽ làm điều này trong 3 bước
- Viết lại hàm tìm số nguyên tố trong C
- Đóng gói hàm C trong mô-đun Python
- Xây dựng và cài đặt mô-đun
Bước 1. chức năng C
Phần này có thể hơi khó nếu bạn chưa quen với ngôn ngữ C. Nó giống như Python nhưng bị hạn chế hơn nhiều. Kiểm tra nó ra
Ngoài một số cú pháp ở đây và ở đó, hàm này trông rất giống với cú pháp khó chịu mà chúng ta đã viết trong chương trước
2. Gói chức năng C trong một mô-đun
Được rồi, vậy là chúng ta có một hàm được viết bằng C và một tệp Python. Làm cách nào chúng ta có thể truy cập hàm C từ tệp Python?
Làm thế nào tất cả các yếu tố phù hợp với nhau [hình ảnh của tác giả]
Chúng ta đã xác định hàm C của mình ở trên, vì vậy hãy bọc hàm C đó trong một đối tượng Python và [màu đen] và thực hiện theo cách của chúng ta
2. 1 bọc C-function
thành một
import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
0Hãy đi qua mã
Hãy nhớ rằng mọi thứ trong Python đều là đối tượng? . Ngay cả việc khai báo kết quả số nguyên trong một
import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
1. Về cơ bản, công cụ Python hoạt động với cấu trúc này để cho phép nhập độngTrong đoạn mã trên, chúng tôi đang gói hàm C của mình trong một
import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
1. Hàm này phân tích cú pháp các đối số mà Python gửi cho chúng ta bằng cách sử dụng hàm import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
4. import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
5 chỉ ra rằng chúng tôi mong đợi hai số nguyên [thêm thông tin tại đây]Tiếp theo, chúng ta gọi hàm C là số nguyên tố tìm được. Cuối cùng, chúng tôi trả về số lượng các số nguyên tố đã tìm được sau khi chúng tôi chuyển đổi chúng thành
import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
6; 2. 2 thêm
import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
0 vào một import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
9Đoạn mã dưới đây chỉ định tên hàm mà chúng ta gọi trong Python
Ở đây chúng tôi xác định danh sách tất cả các phương thức mà mô-đun của chúng tôi có
Trong ví dụ này, chúng tôi định nghĩa
Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
0 là tên hàm; . Sau đó, chúng tôi đề cập đến chức năng tạo import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
1 từ bước trước. Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
2 xác định chữ ký. nó mong đợi Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
3 và Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
4 từ Python. Cuối cùng, chúng tôi xác định mô tả phương thức cho chuỗi tài liệuChúng tôi có thể thêm nhiều đối tượng hơn vào đó nếu chúng tôi muốn mô-đun của mình có nhiều chức năng hơn nhưng với mục đích của bản demo này, chúng tôi sẽ giữ cho nó đơn giản.
Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
5 này cũng yêu cầu dòng 3; 2. 3 Tạo
Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
7 trên đó chúng tôi đăng ký tên mô-đun, mô tả và import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
9Đây là mã
Đoạn mã này xác định mô-đun của chúng tôi. Mục đầu tiên là bắt buộc để tạo một
Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
9. Trong dòng 4 và 5, chúng tôi chỉ định tên và mô tả của mô-đun của chúng tôiTrong dòng 6, chúng ta có thể chỉ định dung lượng bộ nhớ cần thiết để lưu trữ trạng thái của chương trình. Nó được yêu cầu khi chương trình của bạn được sử dụng trong nhiều trình thông dịch phụ
Giá trị âm cho biết rằng mô-đun này không hỗ trợ trình thông dịch phụ. Chỉ định yêu cầu bộ nhớ của mô-đun của bạn sẽ được phân bổ trên mỗi phiên phiên dịch phụ với giá trị không âm
Mục cuối cùng, trong dòng 7, đề cập đến danh sách các phương thức mà chúng tôi đã chỉ định trong bước trước
2. 4 Tạo một range[]
0 để tạo mô-đun của chúng ta từ
Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
7mảnh ghép cuối cùng. Đây là hàm mà Python sẽ gọi khi nó nhập mô-đun của chúng ta lần đầu tiên
Chúng tôi sử dụng range[]
2 và chuyển cho nó một tham chiếu đến
Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
9 từ phần trước. Điều này sẽ trả về một import Fastcount
res = Fastcount.primecounter[0, 100]
print[res]
# 25
1 trong đó hàm C của chúng ta được bao bọc. Kiểm tra toàn bộ mã ở đây3. Xây dựng, cài đặt và chạy tiện ích mở rộng
Phần này tương tự như bước trong quá trình tạo Gói Python công khai hoặc riêng tư của riêng bạn. Chúng tôi phải tạo một tệp range[]
5 trỏ đến mã C của chúng tôi từ bước trước và sau đó tạo gói. Đi nào
Đoạn mã trên khá dễ hiểu;
Bước tiếp theo. chỉ cần gọi range[]
6. Điều này sẽ lấy tất cả mã của chúng tôi và đóng gói nó trong một mô-đun có tên là Fastcount. Bây giờ trong Python, chúng ta có thể
Xử lý sự cố
các cửa sổ. gọi điện cho range[]
6 có thể khiến bạn gặp lỗi khi đọc nội dung nào đó như
Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": //visualstudio.microsoft.com/visual-cpp-build-tools
Bạn có thể giải quyết vấn đề này bằng cách cài đặt các công cụ xây dựng C++ mà bạn có thể tải xuống tại đây
Hãy chạy đua thuật toán [hình ảnh của Jonathan Chng trên Bapt]Đo điểm chuẩn
Hãy thử nghiệm mã của chúng ta; . 000. Để làm được điều này chúng ta cần kiểm tra khoảng 1. 3 tỷ số; . Chúng tôi đang đi đến điểm chuẩn
- vani Python
- vanilla Python + tích hợp sẵn [như phạm vi]
- Fastcount [mô-đun C của chúng tôi]
- MP đếm nhanh
Sau khi thực hiện tất cả các phương pháp nhiều lần và mất ít thời gian nhất, kết quả sẽ ở dạng. Tất cả các phương pháp tìm thấy số nguyên tố chính xác [41. 538] và đây là khoảng thời gian họ sử dụng [càng thấp càng tốt]
Thời gian thực hiện của tất cả các phương pháp tìm số nguyên tố trong một khoảng [càng thấp càng tốt, ảnh của tác giả]
Sử dụng các chức năng tích hợp sẵn như range[]
đã loại bỏ khoảng 35% thời gian cần thiết để hoàn thành. Mặc dù đây là một mức tăng khá tốt, mô-đun riêng của chúng tôi đã hoàn thành nhiệm vụ gần như range[]
9 so với vanilla Python
Để đạt được tốc độ cao hơn nữa, chúng tôi phân bổ tất cả các phép tính trên nhiều CPU bằng cách đa xử lý hàm của chúng tôi để hoàn thành phép tính của chúng tôi range[]
0 so với vanilla Python. Hãy xem bài viết này hoặc bài viết này để tìm hiểu thêm về đa tác vụ an toàn trong Python bằng cách sử dụng luồng và quy trình
Phần kết luận
Đây là một bài viết rất dài và khá phức tạp nhưng chúng ta đã học được rất nhiều về cách thức hoạt động của Python. Chúng tôi đã viết, biên dịch, đóng gói và nhập mô-đun C tùy chỉnh của riêng mình. Mặc dù bài viết này khá dài nhưng cuối cùng, chúng tôi đã giảm tốc độ thực thi từ hơn 10 phút xuống còn gần 6 giây. loại bỏ 99% thời gian thực hiện
Nếu bạn có góp ý/làm rõ xin vui lòng bình luận để tôi có thể cải thiện bài viết này. Trong thời gian chờ đợi, hãy xem các bài viết khác của tôi về tất cả các loại chủ đề liên quan đến lập trình như thế này