Danh sách xử lý song song Python

Một khía cạnh của mã hóa trong Python mà chúng ta vẫn chưa thảo luận chi tiết là làm thế nào để tối ưu hóa hiệu suất thực thi của các mô phỏng của chúng ta. Mặc dù NumPy, SciPy và pandas cực kỳ hữu ích về mặt này khi xem xét mã được vector hóa, nhưng chúng tôi không thể sử dụng các công cụ này một cách hiệu quả khi xây dựng các hệ thống hướng sự kiện. Có bất kỳ phương tiện nào khác có sẵn cho chúng tôi để tăng tốc mã của chúng tôi không?

Trong bài viết này, chúng ta sẽ xem xét các mô hình song song khác nhau có thể được đưa vào các chương trình Python của chúng ta. Các mô hình này hoạt động đặc biệt tốt cho các mô phỏng không cần chia sẻ trạng thái. Mô phỏng Monte Carlo được sử dụng để định giá quyền chọn và mô phỏng kiểm tra lại các thông số khác nhau cho giao dịch theo thuật toán thuộc danh mục này

Cụ thể, chúng ta sẽ xem xét thư viện Threading và thư viện Multiprocessing

Đồng thời trong Python

Một trong những câu hỏi thường gặp nhất đối với các lập trình viên Python mới bắt đầu khi họ khám phá mã đa luồng để tối ưu hóa mã giới hạn CPU là "Tại sao chương trình của tôi chạy chậm hơn khi tôi sử dụng nhiều luồng?"

Kỳ vọng là trên một máy đa lõi, mã đa luồng sẽ sử dụng các lõi bổ sung này và do đó tăng hiệu suất tổng thể. Thật không may, phần bên trong của trình thông dịch Python chính, CPython, phủ nhận khả năng đa luồng thực sự do một quá trình được gọi là Khóa thông dịch viên toàn cầu [GIL]

GIL là cần thiết vì trình thông dịch Python không an toàn cho luồng. Điều này có nghĩa là có một khóa được thi hành trên toàn cầu khi cố gắng truy cập an toàn các đối tượng Python từ bên trong các luồng. Tại bất kỳ thời điểm nào, chỉ một luồng duy nhất có thể có khóa cho đối tượng Python hoặc API C. Trình thông dịch sẽ yêu cầu lại khóa này cho mỗi 100 mã byte của hướng dẫn Python và xung quanh [có khả năng] chặn hoạt động I/O. Do khóa này, mã giới hạn CPU sẽ không tăng hiệu suất khi sử dụng thư viện Phân luồng, nhưng nó có thể sẽ tăng hiệu suất nếu sử dụng thư viện Đa xử lý

Thực hiện thư viện song song hóa

Bây giờ chúng tôi sẽ sử dụng hai thư viện riêng biệt ở trên để cố gắng tối ưu hóa song song vấn đề "đồ chơi"

Thư viện luồng

Ở trên, chúng tôi đã đề cập đến thực tế là Python trên trình thông dịch CPython không hỗ trợ thực thi đa lõi thực sự thông qua đa luồng. Tuy nhiên, Python KHÔNG có thư viện Threading. Vì vậy, lợi ích của việc sử dụng thư viện là gì nếu chúng ta [được cho là] ​​không thể sử dụng nhiều lõi?

Nhiều chương trình, đặc biệt là những chương trình liên quan đến lập trình mạng hoặc đầu vào/đầu ra dữ liệu [I/O] thường bị ràng buộc mạng hoặc bị ràng buộc I/O. Điều này có nghĩa là trình thông dịch Python đang đợi kết quả của lệnh gọi hàm thao tác dữ liệu từ nguồn "từ xa" chẳng hạn như địa chỉ mạng hoặc đĩa cứng. Truy cập như vậy chậm hơn nhiều so với đọc từ bộ nhớ cục bộ hoặc bộ đệm CPU

Do đó, một phương tiện để tăng tốc mã như vậy nếu nhiều nguồn dữ liệu đang được truy cập là tạo một chuỗi cho từng mục dữ liệu cần được truy cập

Ví dụ: hãy xem xét mã Python đang thu thập nhiều URL web. Cho rằng mỗi URL sẽ có thời gian tải xuống được liên kết vượt quá khả năng xử lý CPU của máy tính, việc triển khai đơn luồng sẽ bị ràng buộc I/O đáng kể

Bằng cách thêm một luồng mới cho mỗi tài nguyên tải xuống, mã có thể tải xuống nhiều nguồn dữ liệu song song và kết hợp các kết quả ở cuối mỗi lần tải xuống. Điều này có nghĩa là mỗi lần tải xuống tiếp theo không chờ tải xuống các trang web trước đó. Trong trường hợp này, chương trình hiện bị ràng buộc bởi giới hạn băng thông của [các] máy khách/máy chủ thay vì

Tuy nhiên, nhiều ứng dụng tài chính LÀ CPU bị ràng buộc vì chúng rất chuyên sâu về số lượng. Chúng thường liên quan đến các giải pháp đại số tuyến tính số quy mô lớn hoặc các phép rút thăm thống kê ngẫu nhiên, chẳng hạn như trong mô phỏng Monte Carlo. Do đó, đối với Python và GIL, không có lợi ích gì khi sử dụng thư viện Python Threading cho các tác vụ đó

Triển khai Python

Đoạn mã sau minh họa cách triển khai đa luồng cho mã "đồ chơi" để thêm các số vào danh sách một cách tuần tự. Mỗi luồng tạo một danh sách mới và thêm các số ngẫu nhiên vào đó. Cái này đã được chọn làm ví dụ đồ chơi vì nó nặng CPU

Đoạn mã sau sẽ phác thảo giao diện cho thư viện Threading nhưng nó sẽ không cấp cho chúng tôi bất kỳ khả năng tăng tốc bổ sung nào ngoài khả năng có thể đạt được trong triển khai đơn luồng. Khi chúng ta bắt đầu sử dụng thư viện Đa xử lý bên dưới, chúng ta sẽ thấy rằng nó sẽ giảm đáng kể thời gian chạy tổng thể

Hãy kiểm tra xem mã hoạt động như thế nào. Đầu tiên, chúng tôi nhập thư viện

time python thread_test.py
4. Sau đó, chúng tôi tạo một hàm
time python thread_test.py
5 có ba tham số. Đầu tiên,
time python thread_test.py
6, xác định kích thước của danh sách để tạo. Thứ hai,
time python thread_test.py
0, là ID của "công việc" [có thể hữu ích nếu chúng tôi đang ghi thông tin gỡ lỗi vào bảng điều khiển]. Tham số thứ ba,
time python thread_test.py
1, là danh sách để nối các số ngẫu nhiên vào

Hàm

time python thread_test.py
2 tạo một
time python thread_test.py
3 của $10^7$ và sử dụng hai
time python thread_test.py
4 để thực hiện công việc. Sau đó, nó tạo một danh sách
time python thread_test.py
5, được sử dụng để lưu trữ các chủ đề riêng biệt. Đối tượng
time python thread_test.py
6 lấy hàm
time python thread_test.py
5 làm tham số rồi nối nó vào danh sách
time python thread_test.py
5

Cuối cùng, các công việc được bắt đầu tuần tự và sau đó được "tham gia" tuần tự. Phương thức

time python thread_test.py
9 chặn luồng gọi [i. e. luồng trình thông dịch Python chính] cho đến khi luồng kết thúc. Điều này đảm bảo rằng tất cả các luồng đã hoàn thành trước khi in thông báo hoàn thành ra bàn điều khiển

# thread_test.py

import random
import threading


def list_append[count, id, out_list]:
    """
    Creates an empty list and then appends a 
    random number to the list 'count' number
    of times. A CPU-heavy operation!
    """
    for i in range[count]:
        out_list.append[random.random[]]

if __name__ == "__main__":
    size = 10000000   # Number of random numbers to add
    threads = 2   # Number of threads to create

    # Create a list of jobs and then iterate through
    # the number of threads appending each thread to
    # the job list 
    jobs = []
    for i in range[0, threads]:
        out_list = list[]
        thread = threading.Thread[target=list_append[size, i, out_list]]
        jobs.append[thread]

    # Start the threads [i.e. calculate the random number lists]
    for j in jobs:
        j.start[]

    # Ensure all of the threads have finished
    for j in jobs:
        j.join[]

    print "List processing complete."

Chúng tôi có thể tính thời gian mã này bằng lệnh gọi bàn điều khiển sau

time python thread_test.py

Nó tạo ra đầu ra sau

time python thread_test.py
1

Lưu ý rằng cả

time python thread_test.py
10 và
time python thread_test.py
11 đều xấp xỉ tổng của thời gian
time python thread_test.py
12. Điều này cho thấy rằng chúng tôi không thu được lợi ích gì từ việc sử dụng thư viện Threading. Nếu chúng tôi có thì chúng tôi mong đợi thời gian
time python thread_test.py
12 sẽ ít hơn đáng kể. Các khái niệm này trong lập trình đồng thời thường được gọi là thời gian CPU và thời gian đồng hồ treo tường tương ứng

Thư viện đa xử lý

Để thực sự sử dụng các lõi bổ sung có trong hầu hết các bộ xử lý tiêu dùng hiện đại, thay vào đó, chúng ta có thể sử dụng thư viện Đa xử lý. Điều này hoạt động theo một cách cơ bản khác với thư viện Threading, mặc dù cú pháp của cả hai cực kỳ giống nhau

Thư viện Đa xử lý thực sự sinh ra nhiều quy trình hệ điều hành cho mỗi tác vụ song song. Điều này hỗ trợ GIL một cách độc đáo, bằng cách cung cấp cho mỗi quy trình trình thông dịch Python của riêng nó và do đó, GIL của riêng nó. Do đó, mỗi quy trình có thể được đưa vào một lõi bộ xử lý riêng biệt và sau đó được nhóm lại ở cuối khi tất cả các quy trình đã hoàn thành

Có một số nhược điểm, tuy nhiên. Sinh ra các quy trình bổ sung giới thiệu chi phí I/O vì dữ liệu phải được xáo trộn giữa các bộ xử lý. Điều này có thể thêm vào thời gian chạy tổng thể. Tuy nhiên, giả sử dữ liệu bị hạn chế đối với từng quy trình, có thể tăng tốc đáng kể. Tất nhiên, người ta phải luôn nhận thức được Định luật Amdahl

Triển khai Python

Các sửa đổi duy nhất cần thiết cho việc triển khai Đa xử lý bao gồm thay đổi dòng nhập và dạng chức năng của dòng

time python thread_test.py
14. Trong trường hợp này, các đối số của hàm đích được truyền riêng. Ngoài ra, mã này gần giống với cách triển khai Threading ở trên

time python thread_test.py
7

Chúng ta có thể lặp lại mã này một lần nữa bằng cách sử dụng lệnh gọi bảng điều khiển tương tự

time python thread_test.py
8

Chúng tôi nhận được đầu ra sau

time python thread_test.py
9

Trong trường hợp này, bạn có thể thấy rằng mặc dù thời gian của

time python thread_test.py
10 và
time python thread_test.py
11 gần như giống nhau nhưng thời gian của
time python thread_test.py
12 đã giảm gần hai lần. Điều này có ý nghĩa vì chúng tôi đang sử dụng hai quy trình. Chia tỷ lệ thành bốn quy trình trong khi giảm một nửa kích thước danh sách để so sánh sẽ cho kết quả sau [với giả định rằng bạn có ít nhất bốn lõi. ]

time python thread_test.py
3

Đây là khoảng 3. Tăng tốc 8 lần với bốn quy trình. Tuy nhiên, chúng ta phải cẩn thận khi khái quát hóa điều này cho các chương trình lớn hơn, phức tạp hơn. Truyền dữ liệu, mức bộ đệm phần cứng và các vấn đề khác gần như chắc chắn sẽ làm giảm loại hiệu suất đạt được này trong các mã "thực"

Trong các bài viết sau, chúng tôi sẽ sửa đổi Trình kiểm tra theo hướng sự kiện để sử dụng các kỹ thuật song song nhằm cải thiện khả năng thực hiện các nghiên cứu tối ưu hóa tham số đa chiều

Danh sách song song trong Python là gì?

"Danh sách song song" là một biến thể của thuật ngữ "mảng song song" . Ý tưởng là thay vì có một mảng/danh sách/tập hợp các bản ghi [đối tượng có thuộc tính, theo thuật ngữ Python], bạn có một mảng/danh sách/tập hợp riêng cho từng trường của bản ghi khái niệm.

Có thể sử dụng Python để xử lý song song không?

Một cách để đạt được tính song song trong Python là sử dụng mô-đun đa xử lý . Mô-đun đa xử lý cho phép bạn tạo nhiều quy trình, mỗi quy trình có trình thông dịch Python riêng. Vì lý do này, đa xử lý Python thực hiện song song dựa trên quy trình.

Python có thể chạy song song hai chức năng không?

Một cách phổ biến để chạy các chức năng song song với Python là sử dụng mô-đun đa xử lý . Mô-đun này mạnh mẽ, có nhiều tùy chọn để định cấu hình và nhiều thứ để điều chỉnh.

4 khía cạnh của xử lý song song là gì?

Xử lý song song được liên kết với hệ thống thị giác trong đó não chia những gì nó nhìn thấy thành bốn thành phần. màu sắc, chuyển động, hình dạng và độ sâu .

Chủ Đề