Python nhập song song

Joblib cung cấp một lớp trình trợ giúp đơn giản để viết các vòng lặp song song bằng đa xử lý. Ý tưởng cốt lõi là viết mã để được thực thi dưới dạng biểu thức trình tạo và chuyển đổi nó thành tính toán song song

>>> from math import sqrt
>>> [sqrt(i ** 2) for i in range(10)]
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

có thể trải rộng trên 2 CPU bằng cách sử dụng như sau

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

Song song dựa trên luồng so với song song dựa trên quy trình¶

Theo mặc định,

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 sử dụng mô-đun phụ trợ
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9 để bắt đầu các quy trình công nhân Python riêng biệt nhằm thực thi đồng thời các tác vụ trên các CPU riêng biệt. Đây là một giá trị mặc định hợp lý cho các chương trình Python chung nhưng có thể gây ra chi phí hoạt động đáng kể vì dữ liệu đầu vào và đầu ra cần được tuần tự hóa trong hàng đợi để liên lạc với các quy trình worker (xem Sắp xếp thứ tự & Quy trìnhSắp xếp thứ tự & Quy trìnhSắp xếp thứ tự & Quy trìnhSắp xếp thứ tự & Quy trìnhSắp xếp thứ tự & Quy trìnhSắp xếp thứ tự & Quy trình).

Khi bạn biết rằng chức năng bạn đang gọi dựa trên một tiện ích mở rộng đã biên dịch sẽ giải phóng Khóa thông dịch viên toàn cầu Python (GIL) trong hầu hết quá trình tính toán của nó thì việc sử dụng các luồng thay vì các quy trình Python làm công nhân đồng thời sẽ hiệu quả hơn. Chẳng hạn, đây là trường hợp nếu bạn viết phần chuyên sâu về CPU của mã bên trong khối with nogil của hàm Cython

Để gợi ý rằng mã của bạn có thể sử dụng các luồng một cách hiệu quả, chỉ cần chuyển

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
0 làm tham số của hàm tạo
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8. Trong trường hợp này, joblib sẽ tự động sử dụng chương trình phụ trợ
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
2 thay vì chương trình phụ trợ
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
3 mặc định

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

Cũng có thể chọn thủ công một triển khai phụ trợ cụ thể với sự trợ giúp của trình quản lý ngữ cảnh

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]

Cái sau đặc biệt hữu ích khi gọi một thư viện sử dụng nội bộ

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 mà không để lộ lựa chọn phụ trợ như một phần của API công khai của nó

Lưu ý rằng tùy chọn

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
0 đã được giới thiệu trong joblib 0. 12. Trong các phiên bản trước, hiệu ứng tương tự có thể đạt được bằng cách mã hóa cứng một triển khai phụ trợ cụ thể, chẳng hạn như
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
6 trong lệnh gọi tới
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 nhưng điều này hiện được coi là một mẫu xấu (khi được thực hiện trong thư viện) vì nó không thể ghi đè lựa chọn đó

Phần phụ trợ loky có thể không phải lúc nào cũng khả dụng

Một số hệ thống hiếm hoi không hỗ trợ đa xử lý (ví dụ Pyodide). Trong trường hợp này, phần phụ trợ loky không khả dụng và phần phụ trợ mặc định sẽ quay trở lại phân luồng

Bên cạnh các phụ trợ joblib dựng sẵn, chúng ta có thể sử dụng Joblib Apache Spark Backend để phân phối các tác vụ joblib trên một cụm Spark

Tuần tự hóa & Quy trình¶

Để chia sẻ định nghĩa hàm trên nhiều quy trình python, cần phải dựa vào giao thức tuần tự hóa. Giao thức chuẩn trong python là

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9 nhưng việc triển khai mặc định của nó trong thư viện chuẩn có một số hạn chế. Chẳng hạn, nó không thể tuần tự hóa các chức năng được xác định tương tác hoặc trong mô-đun
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
60

Để tránh giới hạn này, chương trình phụ trợ

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
61 hiện dựa vào
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
62 để tuần tự hóa các đối tượng python.
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
62 là một triển khai thay thế của giao thức pickle cho phép tuần tự hóa số lượng đối tượng lớn hơn, cụ thể là các hàm được xác định tương tác. Vì vậy, đối với hầu hết các mục đích sử dụng, loky
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
64 sẽ hoạt động trơn tru

Hạn chế chính của

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
62 là nó có thể chậm hơn mô-đun
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9 trong thư viện chuẩn. Đặc biệt, nó rất quan trọng đối với các danh sách hoặc từ điển python lớn, trong đó thời gian tuần tự hóa có thể chậm hơn tới 100 lần. Có hai cách để thay đổi quy trình lập số sê-ri cho
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
67 để khắc phục vấn đề này

  • Nếu bạn đang sử dụng hệ thống UNIX, bạn có thể chuyển về chương trình phụ trợ

    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    68 cũ. Với chương trình phụ trợ này, các hàm được xác định tương tác có thể được chia sẻ với các quy trình worker bằng cách sử dụng nhanh
    >>> from joblib import parallel_backend
    >>> with parallel_backend('threading', n_jobs=2):
    ..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    9. Vấn đề chính với giải pháp này là việc sử dụng
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    60 để bắt đầu quy trình sẽ phá vỡ tiêu chuẩn POSIX và có thể có tương tác kỳ lạ với các thư viện của bên thứ ba, chẳng hạn như
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    61 và
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    62

  • Nếu bạn muốn sử dụng chương trình phụ trợ

    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    61 với một thư viện tuần tự hóa khác, bạn có thể đặt biến môi trường
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    64 để sử dụng
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    65 làm thư viện tuần tự hóa cho
    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    61. Mô-đun
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    65 được truyền dưới dạng đối số phải có thể nhập được dưới dạng
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    68 và phải chứa đối tượng
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    69, đối tượng này sẽ được sử dụng để tuần tự hóa các đối tượng. Nó có thể được đặt thành
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    60 để sử dụng mô-đun tẩy từ stdlib. Hạn chế chính với
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    60 là các chức năng được xác định tương tác sẽ không thể tuần tự hóa được nữa. Để giải quyết vấn đề này, bạn có thể sử dụng giải pháp này cùng với trình bao bọc
    >>> Parallel(n_jobs=2, prefer="threads")(
    ..     delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    62, có thể được sử dụng làm công cụ trang trí để kích hoạt cục bộ bằng cách sử dụng
    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    62 cho các đối tượng cụ thể. Bằng cách này, bạn có thể chọn nhanh tất cả các đối tượng python và kích hoạt tính năng tẩy chậm cục bộ cho các chức năng tương tác. Một ví dụ được đưa ra trong loky_wrapper

Ngữ nghĩa bộ nhớ dùng chung¶

Phần phụ trợ mặc định của joblib sẽ chạy từng lệnh gọi hàm trong các quy trình Python bị cô lập, do đó chúng không thể thay đổi một đối tượng Python chung được xác định trong chương trình chính

Tuy nhiên, nếu chức năng song song thực sự cần dựa vào ngữ nghĩa bộ nhớ dùng chung của các luồng, chẳng hạn, nó phải được làm rõ bằng

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
64

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
6

Hãy nhớ rằng việc dựa vào ngữ nghĩa của bộ nhớ dùng chung có thể là không tối ưu từ quan điểm hiệu suất vì quyền truy cập đồng thời vào một đối tượng Python dùng chung sẽ bị tranh chấp khóa

Tái sử dụng một nhóm công nhân¶

Một số thuật toán yêu cầu thực hiện một số lệnh gọi liên tiếp đến một hàm song song xen kẽ với việc xử lý các kết quả trung gian. Gọi

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 nhiều lần trong một vòng lặp là không tối ưu vì nó sẽ tạo và hủy một nhóm công nhân (luồng hoặc quy trình) nhiều lần, điều này có thể gây ra chi phí hoạt động đáng kể

Đối với trường hợp này, sẽ hiệu quả hơn khi sử dụng API trình quản lý bối cảnh của lớp

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 để sử dụng lại cùng một nhóm công nhân cho một số cuộc gọi đến đối tượng
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
6

Lưu ý rằng chương trình phụ trợ

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9 hiện được sử dụng theo mặc định cho tính song song dựa trên quy trình sẽ tự động cố gắng duy trì và sử dụng lại một nhóm công nhân ngay cả đối với các cuộc gọi mà không có trình quản lý ngữ cảnh

Làm việc với dữ liệu số trong bộ nhớ dùng chung (ghi nhớ)¶

Theo mặc định, công nhân của nhóm là các quy trình Python thực được rẽ nhánh bằng cách sử dụng mô-đun

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
68 của thư viện chuẩn Python khi
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
80. Các đối số được truyền dưới dạng đầu vào cho lệnh gọi
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
81 được tuần tự hóa và phân bổ lại trong bộ nhớ của từng quy trình worker

Điều này có thể gây rắc rối cho các đối số lớn vì chúng sẽ được phân bổ lại

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
82 lần bởi các công nhân

Vì vấn đề này thường có thể xảy ra trong điện toán khoa học với cơ sở dữ liệu dựa trên

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
61, nên
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 cung cấp một cách xử lý đặc biệt cho các mảng lớn để tự động kết xuất chúng trên hệ thống tệp và chuyển tham chiếu đến worker để mở chúng dưới dạng bản đồ bộ nhớ trên tệp đó bằng cách sử dụng lớp con
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
85 của . Điều này cho phép chia sẻ một đoạn dữ liệu giữa tất cả các worker process

Ghi chú

Điều sau đây chỉ áp dụng với phụ trợ quy trình

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
87. Nếu mã của bạn có thể giải phóng GIL, thì việc sử dụng chương trình phụ trợ phụ trợ dựa trên luồng bằng cách chuyển
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
88 thậm chí còn hiệu quả hơn vì có thể tránh được chi phí giao tiếp của xử lý song song dựa trên quy trình

Các thư viện Python khoa học như numpy, scipy, pandas và scikit-learning thường phát hành GIL trong các đường dẫn mã quan trọng về hiệu suất. Do đó, nên luôn đo tốc độ song song dựa trên luồng và sử dụng nó khi khả năng mở rộng không bị giới hạn bởi GIL

Tự động chuyển mảng sang memmap¶

Chuyển đổi mảng tự động sang memmap được kích hoạt bởi một ngưỡng có thể định cấu hình về kích thước của mảng

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
6

Theo mặc định, dữ liệu được kết xuất vào phân vùng bộ nhớ dùng chung

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
89 nếu nó tồn tại và có thể ghi được (thường là trường hợp trong Linux). Nếu không, thư mục tạm thời của hệ điều hành được sử dụng. Vị trí của các tệp dữ liệu tạm thời có thể được tùy chỉnh bằng cách chuyển đối số
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
90 cho hàm tạo
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
81

Vượt qua

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
92 để có thể vô hiệu hóa mảng tự động thành chuyển đổi memmap

Quản lý thủ công dữ liệu đầu vào được ghi nhớ¶

Để điều chỉnh tốt hơn việc sử dụng bộ nhớ, cũng có thể kết xuất mảng dưới dạng bản ghi nhớ trực tiếp từ tiến trình cha để giải phóng bộ nhớ trước khi rẽ nhánh các tiến trình worker. Chẳng hạn, hãy cấp phát một mảng lớn trong bộ nhớ của tiến trình cha

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8

Kết xuất nó vào một tệp cục bộ để ghi nhớ

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9

Biến

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
93 đang trỏ đến một phiên bản
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
85

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
6

Mảng ban đầu có thể được giải phóng khỏi bộ nhớ tiến trình chính

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
0

Có thể cắt

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
93 thành một memmap nhỏ hơn

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
1

Cuối cùng, có thể sử dụng chế độ xem

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
96 được sao lưu trên cùng tệp ánh xạ bộ nhớ đó

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
2

Tất cả ba cơ sở dữ liệu này đều trỏ đến cùng một bộ đệm bộ nhớ và cùng bộ đệm này cũng sẽ được sử dụng lại trực tiếp bởi các quy trình worker của một cuộc gọi

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
81

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
3

Lưu ý rằng ở đây

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
92 được sử dụng để vô hiệu hóa tính năng tự động đổ của
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
81.
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
60 vẫn nằm trong bộ nhớ dùng chung trong quy trình công nhân vì nó đã được hỗ trợ bởi bộ nhớ dùng chung trong quy trình cha. Bộ máy tẩy của hàng đợi đa xử lý
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
81 có thể phát hiện tình huống này và tối ưu hóa nó một cách nhanh chóng để hạn chế số lượng bản sao bộ nhớ

Viết kết quả tính toán song song trong bộ nhớ dùng chung¶

Nếu dữ liệu được mở bằng chế độ

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
62 hoặc
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
63 trong chương trình chính, nhân viên sẽ có quyền truy cập chế độ
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
63. Do đó, công nhân sẽ có thể ghi kết quả của nó trực tiếp vào dữ liệu gốc, giảm bớt nhu cầu tuần tự hóa để gửi lại kết quả cho quy trình gốc

Đây là tập lệnh ví dụ về xử lý song song với cơ sở dữ liệu

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
85 được phân bổ trước Bản đồ ghi nhớ NumPy trong joblib. Song song .

Cảnh báo

Chẳng hạn, có các công nhân đồng thời ghi trên các phân đoạn dữ liệu bộ nhớ dùng chung chồng chéo bằng cách sử dụng các toán tử tại chỗ và các phép gán trên một khối u. memmap, có thể dẫn đến hỏng dữ liệu vì numpy không cung cấp các hoạt động nguyên tử. Ví dụ trước không gặp rủi ro về vấn đề đó vì mỗi tác vụ đang cập nhật một phân đoạn độc quyền của mảng kết quả được chia sẻ

Một số trình biên dịch C/C++ cung cấp các nguyên hàm nguyên tử không khóa như thêm và tìm nạp hoặc so sánh và hoán đổi có thể được tiếp xúc với Python thông qua CFFI chẳng hạn. Tuy nhiên, việc cung cấp các cấu trúc nguyên tử nhận biết numpy nằm ngoài phạm vi của dự án joblib

Một lưu ý cuối cùng. đừng quên dọn sạch mọi thư mục tạm thời khi bạn hoàn thành việc tính toán

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
4

Tránh đăng ký quá nhiều tài nguyên CPU¶

Tính song song tính toán dựa trên việc sử dụng nhiều CPU để thực hiện thao tác đồng thời. Khi sử dụng nhiều quy trình hơn số lượng CPU trên một máy, hiệu suất của từng quy trình sẽ bị suy giảm do có ít năng lượng tính toán hơn cho mỗi quy trình. Hơn nữa, khi nhiều tiến trình đang chạy, thời gian mà bộ lập lịch hệ điều hành dành để chuyển đổi giữa chúng có thể cản trở hiệu suất tính toán hơn nữa. Nói chung, tốt hơn là tránh sử dụng nhiều quy trình hoặc luồng hơn đáng kể so với số lượng CPU trên máy

Một số thư viện của bên thứ ba – e. g. thời gian chạy BLAS được sử dụng bởi

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
61 – quản lý nội bộ nhóm luồng để thực hiện các tính toán của họ. Hành vi mặc định thường là sử dụng số lượng luồng bằng với số lượng CPU có sẵn. Khi các thư viện này được sử dụng với
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8, mỗi công nhân sẽ sinh ra các nhóm luồng riêng, dẫn đến việc đăng ký quá nhiều tài nguyên có thể làm chậm quá trình tính toán so với quá trình tuần tự. Để đối phó với vấn đề này, joblib yêu cầu các thư viện bên thứ ba được hỗ trợ sử dụng một số luồng hạn chế trong các công nhân do chương trình phụ trợ
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9 quản lý. theo mặc định, mỗi worker process sẽ có các biến môi trường được đặt để cho phép tối đa là
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
69 để tổng số luồng được sử dụng bởi tất cả worker không vượt quá số lượng CPU của máy chủ

Hành vi này có thể được ghi đè bằng cách đặt các biến môi trường phù hợp với số lượng luồng mong muốn. Ghi đè này được hỗ trợ cho các thư viện sau

  • OpenMP với biến môi trường

    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    00,

  • OpenBLAS với

    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    01,

  • MKL với biến môi trường

    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    02,

  • Tăng tốc với biến môi trường

    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    03,

  • Numexpr với biến môi trường

    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    04

Vì joblib 0. 14, cũng có thể lập trình ghi đè số luồng mặc định bằng cách sử dụng đối số

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
05 của hàm
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 như sau

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
5

Trong ví dụ này, 4 worker process Python sẽ được phép sử dụng 2 luồng, mỗi process có nghĩa là chương trình này sẽ có thể sử dụng đồng thời tối đa 8 CPU

API phụ trợ tùy chỉnh (thử nghiệm)¶

Mới trong phiên bản 0. 10

Cảnh báo

API phụ trợ tùy chỉnh là thử nghiệm và có thể thay đổi mà không trải qua chu kỳ ngừng sử dụng

Người dùng có thể cung cấp triển khai phụ trợ xử lý song song của riêng họ ngoài các phụ trợ

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9,
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
08,
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
09 được cung cấp theo mặc định. Phần phụ trợ được đăng ký với hàm
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
10 bằng cách chuyển tên và nhà máy phụ trợ

Nhà máy phụ trợ có thể là bất kỳ khả năng gọi nào trả về một phiên bản của

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
11. Vui lòng tham khảo mã nguồn phụ trợ mặc định làm tài liệu tham khảo nếu bạn muốn triển khai phụ trợ tùy chỉnh của riêng mình

Lưu ý rằng có thể đăng ký lớp phụ trợ có một số tham số hàm tạo bắt buộc như địa chỉ mạng và thông tin xác thực kết nối cho dịch vụ điện toán cụm từ xa

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
6

Sau đó, các tham số kết nối có thể được chuyển đến trình quản lý ngữ cảnh

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
12

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
7

Sử dụng trình quản lý bối cảnh có thể hữu ích khi sử dụng thư viện của bên thứ ba sử dụng nội bộ

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 trong khi không hiển thị đối số
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
64 trong API của chính nó

Một vấn đề tồn tại là các gói bên ngoài đăng ký phụ trợ song song mới hiện phải được nhập rõ ràng để phụ trợ của chúng được xác định bởi joblib

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8

Điều này có thể gây nhầm lẫn cho người dùng. Để giải quyết vấn đề này, các gói bên ngoài có thể đăng ký trực tiếp các phần phụ trợ của chúng một cách an toàn trong cơ sở mã joblib bằng cách tạo một hàm nhỏ đăng ký phần phụ trợ của chúng và bao gồm hàm này trong từ điển

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
15

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9

Điều này có thể được cộng đồng xem xét, nhưng có thể làm giảm sự nhầm lẫn cho người dùng khi dựa vào tác dụng phụ của việc nhập gói bên ngoài

Chương trình phụ trợ đa xử lý cũ¶

Trước phiên bản 0. 12, joblib đã sử dụng chương trình phụ trợ

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
09 làm chương trình phụ trợ mặc định thay vì
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9

Chương trình phụ trợ này tạo ra một phiên bản đa xử lý. Nhóm phân nhánh trình thông dịch Python trong nhiều quy trình để thực thi từng mục trong danh sách. Hàm bị trì hoãn là một thủ thuật đơn giản để có thể tạo một bộ (hàm, args, kwargs) bằng cú pháp gọi hàm

Cảnh báo

Trong Windows, việc sử dụng

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
18 yêu cầu bảo vệ vòng lặp mã chính để tránh sinh ra đệ quy các quy trình con khi sử dụng
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8. Nói cách khác, bạn nên viết mã như thế này khi sử dụng chương trình phụ trợ
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
09

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
0

Không có mã nào được chạy bên ngoài khối

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
21, chỉ nhập và định nghĩa

Chương trình phụ trợ

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9 được sử dụng theo mặc định trong joblib 0. 12 trở về sau không áp đặt cái này nữa

Tương tác kém giữa thư viện đa xử lý và bên thứ ba¶

Việc sử dụng chương trình phụ trợ

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
09 có thể gây ra sự cố khi sử dụng thư viện bên thứ ba quản lý nhóm luồng riêng nếu thư viện được sử dụng lần đầu trong quy trình chính và sau đó được gọi lại trong quy trình worker (bên trong lệnh gọi
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8)

Joblib phiên bản 0. 12 trở lên không còn gặp phải sự cố này nhờ sử dụng loky làm phụ trợ mặc định mới cho xử lý song song dựa trên quy trình

Trước Python 3. 4 chương trình phụ trợ

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
09 của joblib chỉ có thể sử dụng chiến lược
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
60 để tạo các quy trình worker trong các hệ thống không phải Windows. Điều này có thể khiến một số thư viện của bên thứ ba bị sập hoặc đóng băng. Các thư viện như vậy bao gồm Apple vecLib/Tăng tốc (được sử dụng bởi NumPy trong OSX), một số phiên bản OpenBLAS cũ (trước 0. 2. 10) hoặc triển khai thời gian chạy OpenMP từ GCC được sử dụng nội bộ bởi các thư viện bên thứ ba như XGBoost, spaCy, OpenCV…

Cách tốt nhất để tránh vấn đề này là sử dụng chương trình phụ trợ

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
9 thay vì chương trình phụ trợ
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
68. Trước joblib 0. 12, cũng có thể cấu hình
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 để sử dụng phương thức khởi động
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
30 trên Python 3. 4 trở lên. Phương thức bắt đầu phải được định cấu hình bằng cách đặt biến môi trường
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
31 thành
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
30 thay vì phương thức bắt đầu
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
33 mặc định. Tuy nhiên, người dùng cần lưu ý rằng việc sử dụng phương pháp
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
30 sẽ ngăn cản
>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 gọi hàm được xác định một cách tương tác trong phiên trình bao

Bạn có thể đọc thêm về chủ đề này trong tài liệu multiprocessing

Trong Windows, cuộc gọi hệ thống

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
60 hoàn toàn không tồn tại nên vấn đề này không tồn tại (nhưng đa xử lý có nhiều chi phí hơn)

Tài liệu tham khảo song song¶

lớp joblib. Song song(n_jobs=Không có, backend=None, verbose=0, timeout=None, pre_dispatch='2 * n_jobs', batch_size='auto', temp_folder=None, max_nbytes='1M', mmap_mode='r', prefer=None, require=None)

Lớp trình trợ giúp để ánh xạ song song có thể đọc được

Đọc thêm trong Hướng dẫn sử dụng .

Tham số N_jobs. int, mặc định. Không có

Số lượng công việc chạy đồng thời tối đa, chẳng hạn như số lượng quy trình công nhân Python khi backend=”multiprocessing” hoặc kích thước của thread-pool khi backend=”threading”. Nếu -1 tất cả các CPU được sử dụng. Nếu 1 được đưa ra, không có mã tính toán song song nào được sử dụng, điều này hữu ích cho việc gỡ lỗi. Đối với n_jobs bên dưới -1, (n_cpus + 1 + n_jobs) được sử dụng. Do đó, đối với n_jobs = -2, tất cả các CPU trừ một CPU đều được sử dụng. Không có gì là điểm đánh dấu cho 'không đặt' sẽ được hiểu là n_jobs=1 (thực hiện tuần tự) trừ khi lệnh gọi được thực hiện dưới trình quản lý ngữ cảnh

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8 đặt giá trị khác cho n_jobs

phụ trợ. str, ví dụ ParallelBackendBase hoặc Không, mặc định. 'loky'

Chỉ định triển khai phụ trợ song song hóa. Phụ trợ được hỗ trợ là

  • “loky” được sử dụng theo mặc định, có thể gây ra một số chi phí giao tiếp và bộ nhớ khi trao đổi dữ liệu đầu vào và đầu ra với worker process Python. Trên một số hệ thống hiếm hoi (chẳng hạn như Pyiodide), phần phụ trợ loky có thể không khả dụng

  • "đa xử lý" phụ trợ dựa trên quy trình trước đó dựa trên đa xử lý. Hồ bơi. Ít mạnh mẽ hơn loky

  • “luồng” là một phụ trợ có chi phí rất thấp nhưng nó bị Khóa phiên dịch toàn cầu Python nếu hàm được gọi phụ thuộc nhiều vào các đối tượng Python. "phân luồng" chủ yếu hữu ích khi nút cổ chai thực thi là một tiện ích mở rộng được biên dịch giải phóng GIL một cách rõ ràng (ví dụ: vòng lặp Cython được bao bọc trong khối "có nogil" hoặc lệnh gọi đắt tiền tới thư viện chẳng hạn như NumPy)

  • cuối cùng, bạn có thể đăng ký phụ trợ bằng cách gọi

    >>> from math import sqrt
    >>> from joblib import Parallel, delayed
    >>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
    [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
    
    38. Điều này sẽ cho phép bạn triển khai một chương trình phụ trợ theo ý thích của mình

Không nên mã hóa cứng tên phụ trợ trong lệnh gọi tới

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
81 trong thư viện. Thay vào đó, nên đặt gợi ý mềm (ưu tiên) hoặc ràng buộc cứng (yêu cầu) để giúp người dùng thư viện có thể thay đổi phần phụ trợ từ bên ngoài bằng cách sử dụng trình quản lý ngữ cảnh
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8

thích hơn. str trong {‘quy trình’, ‘luồng’} hoặc Không có, mặc định. Không có

Gợi ý mềm để chọn chương trình phụ trợ mặc định nếu không có chương trình phụ trợ cụ thể nào được chọn với trình quản lý ngữ cảnh

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
8. Chương trình phụ trợ dựa trên quy trình mặc định là 'loky' và chương trình phụ trợ dựa trên luồng mặc định là 'luồng'. Bỏ qua nếu tham số
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
64 được chỉ định

yêu cầu. 'sharedmem' hoặc Không có, mặc định Không có

Ràng buộc cứng để chọn phụ trợ. Nếu được đặt thành 'sharedmem', chương trình phụ trợ đã chọn sẽ là một máy chủ lưu trữ và dựa trên luồng ngay cả khi người dùng yêu cầu chương trình phụ trợ không dựa trên luồng với parallel_backend

dài dòng. int, tùy chọn

Mức độ chi tiết. nếu khác không, thông báo tiến trình được in. Trên 50, đầu ra được gửi đến thiết bị xuất chuẩn. Tần suất của các tin nhắn tăng theo mức độ chi tiết. Nếu nó lớn hơn 10, tất cả các lần lặp lại được báo cáo

hết giờ. phao, tùy chọn

Giới hạn thời gian chờ cho mỗi nhiệm vụ để hoàn thành. Nếu bất kỳ tác vụ nào mất nhiều thời gian hơn, TimeOutError sẽ được nâng lên. Chỉ áp dụng khi n_jobs. = 1

pre_dispatch. {‘tất cả’, số nguyên hoặc biểu thức, như trong ‘3*n_jobs’}

Số lô (nhiệm vụ) được gửi trước. Mặc định là '2*n_jobs'. Khi batch_size=”auto” thì đây là mặc định hợp lý và công nhân sẽ không bao giờ bị chết đói. Lưu ý rằng chỉ các số học cơ bản mới được phép ở đây và không thể sử dụng mô-đun nào trong biểu thức này

batch_size. int hoặc 'auto', mặc định. 'Tự động'

Số lượng nhiệm vụ nguyên tử để gửi cùng một lúc cho mỗi công nhân. Khi các đánh giá riêng lẻ diễn ra rất nhanh, việc gửi các cuộc gọi đến nhân viên có thể chậm hơn so với tính toán tuần tự do chi phí hoạt động. Việc tính toán hàng loạt các tính toán nhanh cùng nhau có thể giảm thiểu điều này. Chiến lược

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
43 theo dõi thời gian cần thiết để hoàn thành một lô và tự động điều chỉnh kích thước lô để giữ thời gian theo thứ tự nửa giây, sử dụng phương pháp phỏng đoán. Kích thước lô ban đầu là 1.
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
44 với
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
6 sẽ gửi các lô của một tác vụ tại một thời điểm vì phần phụ trợ luồng có rất ít chi phí hoạt động và việc sử dụng kích thước lô lớn hơn đã không được chứng minh là mang lại bất kỳ lợi ích nào trong trường hợp đó

temp_folder. str, tùy chọn

Thư mục được nhóm sử dụng để ghi nhớ các mảng lớn để chia sẻ bộ nhớ với các quy trình worker. Nếu Không, điều này sẽ thử theo thứ tự

  • một thư mục được trỏ bởi biến môi trường JOBLIB_TEMP_FOLDER,

  • /dev/shm nếu thư mục tồn tại và có thể ghi được. đây là hệ thống tệp đĩa RAM có sẵn theo mặc định trên các bản phân phối Linux hiện đại,

  • thư mục tạm thời của hệ thống mặc định có thể được ghi đè bằng các biến môi trường TMP, TMPDIR hoặc TEMP, điển hình là /tmp trong hệ điều hành Unix

Chỉ hoạt động khi backend=”loky” hoặc “multiprocessing”

max_nbytes int, str hoặc Không, tùy chọn, 1M theo mặc định

Ngưỡng về kích thước của mảng được truyền cho các công nhân kích hoạt ánh xạ bộ nhớ tự động trong temp_folder. Có thể là một int tính bằng Byte hoặc một chuỗi mà con người có thể đọc được, e. g. , '1M' cho 1 megabyte. Sử dụng Không có để tắt tính năng ghi nhớ các mảng lớn. Chỉ hoạt động khi backend=”loky” hoặc “multiprocessing”

mmap_mode. {Không có, 'r+', 'r', 'w+', 'c'}, mặc định. 'r'

Chế độ ghi nhớ cho các mảng có nhiều mảng được chuyển cho công nhân. Không có chế độ nào sẽ vô hiệu hóa tính năng ghi nhớ, các chế độ khác được xác định trong numpy. tài liệu ghi nhớ. https. // numpy. org/doc/ổn định/tham chiếu/được tạo/numpy. bản ghi nhớ. html Ngoài ra, hãy xem tài liệu tham số 'max_nbytes' để biết thêm chi tiết

ghi chú

Đối tượng này sử dụng worker để tính toán song song ứng dụng của một hàm cho nhiều đối số khác nhau. Các chức năng chính mà nó mang lại ngoài việc sử dụng đa xử lý thô hoặc đồng thời. API tương lai là (xem ví dụ để biết chi tiết)

  • Mã dễ đọc hơn, đặc biệt vì nó tránh được việc xây dựng danh sách các đối số

  • Gỡ lỗi dễ dàng hơn
    • truy nguyên thông tin ngay cả khi lỗi xảy ra ở phía máy khách

    • sử dụng 'n_jobs=1' cho phép tắt tính toán song song để gỡ lỗi mà không thay đổi đường dẫn mã

    • nắm bắt sớm các lỗi ngâm

  • Một máy đo tiến độ tùy chọn

  • Gián đoạn công việc đa xử lý với 'Ctrl-C'

  • Kiểm soát tẩy linh hoạt để liên lạc đến và từ các quy trình của công nhân

  • Khả năng sử dụng bộ nhớ dùng chung một cách hiệu quả với các quy trình công nhân cho các cơ sở dữ liệu dựa trên numpy lớn

ví dụ

Một ví dụ đơn giản

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
1

Định hình lại đầu ra khi hàm có một số giá trị trả về

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
2

Máy đo tiến độ. giá trị dài dòng càng cao, càng nhiều thông điệp

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
3

Ví dụ về truy nguyên, lưu ý cách chỉ ra dòng lỗi cũng như các giá trị của tham số được truyền cho hàm đã kích hoạt ngoại lệ, mặc dù quá trình truy ngược xảy ra trong tiến trình con

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
4

Sử dụng pre_dispatch trong tình huống của nhà sản xuất/người tiêu dùng, trong đó dữ liệu được tạo nhanh chóng. Lưu ý cách nhà sản xuất được gọi lần đầu tiên 3 lần trước khi bắt đầu vòng lặp song song và sau đó được gọi để tạo dữ liệu mới một cách nhanh chóng

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
5

joblib. trễ(chức năng)

Trình trang trí được sử dụng để nắm bắt các đối số của hàm

joblib. register_parallel_backend(tên , nhà máy, make_default=False)

Đăng ký một nhà máy phụ trợ song song mới

Phần phụ trợ mới sau đó có thể được chọn bằng cách chuyển tên của nó làm đối số phụ trợ cho lớp

>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
81. Hơn nữa, chương trình phụ trợ mặc định có thể được ghi đè trên toàn cầu bằng cách đặt make_default=True

Nhà máy có thể là bất kỳ khả năng gọi nào mà không cần đối số và trả về một thể hiện của

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
11

Cảnh báo. chức năng này là thử nghiệm và có thể thay đổi trong phiên bản tương lai của joblib

Mới trong phiên bản 0. 10

joblib. parallel_backend(backend , n_jobs=-1, inner_max_num_threads=None, **backend_params)

Thay đổi chương trình phụ trợ mặc định được sử dụng bởi Parallel bên trong khối with

Nếu

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
64 là một chuỗi thì nó phải khớp với triển khai đã đăng ký trước đó bằng cách sử dụng hàm
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
38

Theo mặc định, các phụ trợ sau đây có sẵn

  • 'loky'. máy chủ đơn, song song dựa trên quy trình (được sử dụng theo mặc định),

  • 'luồng'. máy chủ đơn, song song dựa trên luồng,

  • 'đa xử lý'. máy chủ đơn kế thừa, xử lý song song dựa trên quy trình

'loky' được khuyến nghị để chạy các chức năng thao tác với các đối tượng Python. 'luồng' là một giải pháp thay thế chi phí thấp, hiệu quả nhất cho các chức năng giải phóng Khóa phiên dịch toàn cầu. e. g. Mã giới hạn I/O hoặc mã giới hạn CPU trong một số lần gọi tới mã gốc giải phóng rõ ràng GIL. Lưu ý rằng trên một số hệ thống hiếm (chẳng hạn như pyiodine), tính năng đa xử lý và loky có thể không khả dụng, trong trường hợp đó, joblib mặc định là phân luồng

Ngoài ra, nếu các gói dask và Python phân tán được cài đặt, thì có thể sử dụng phụ trợ 'dask' để lên lịch tốt hơn cho các cuộc gọi song song lồng nhau mà không cần đăng ký quá mức và có khả năng phân phối các cuộc gọi song song trên một cụm mạng của một số máy chủ

Cũng có thể sử dụng phụ trợ 'ray' phân tán để phân phối khối lượng công việc cho một cụm nút. Để sử dụng chương trình phụ trợ 'ray' joblib, hãy thêm các dòng sau

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
6

Ngoài ra, chương trình phụ trợ có thể được chuyển trực tiếp dưới dạng ví dụ

Theo mặc định, tất cả các công nhân có sẵn sẽ được sử dụng (

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
50) trừ khi người gọi chuyển một giá trị rõ ràng cho tham số
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
82

Đây là một giải pháp thay thế cho việc truyền đối số

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
52 cho phương thức khởi tạo của lớp
>>> from joblib import parallel_backend
>>> with parallel_backend('threading', n_jobs=2):
..    Parallel()(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
81. Nó đặc biệt hữu ích khi gọi mã thư viện sử dụng joblib nội bộ nhưng không hiển thị đối số phụ trợ trong API của chính nó

>>> Parallel(n_jobs=2, prefer="threads")(
..     delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
7

Cảnh báo. chức năng này là thử nghiệm và có thể thay đổi trong phiên bản tương lai của joblib

Joblib cũng cố gắng hạn chế đăng ký quá mức bằng cách giới hạn số lượng chuỗi có thể sử dụng trong một số nhóm chuỗi thư viện của bên thứ ba như OpenBLAS, MKL hoặc OpenMP. Giới hạn mặc định trong mỗi công nhân được đặt thành

>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
54 nhưng giới hạn này có thể được ghi đè bằng đối số
>>> from math import sqrt
>>> from joblib import Parallel, delayed
>>> Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(10))
[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
05 sẽ được sử dụng để đặt giới hạn này trong các quy trình con