Câu trả lời ngắn
Thuật toán kích thước khối của Pool là một thuật toán kinh nghiệm. Nó cung cấp một giải pháp đơn giản cho tất cả các tình huống vấn đề có thể tưởng tượng mà bạn đang cố gắng đưa vào các phương pháp của Pool. Do đó, nó không thể được tối ưu hóa cho bất kỳ trường hợp cụ thể nào .
Thuật toán tự ý chia phần có thể lặp lại thành nhiều phần hơn xấp xỉ bốn lần so với cách tiếp cận đơn giản. Nhiều khối hơn có nghĩa là nhiều chi phí hơn, nhưng tăng tính linh hoạt trong lịch trình. Câu trả lời này sẽ hiển thị như thế nào, điều này dẫn đến mức sử dụng công nhân trung bình cao hơn, nhưng không đảm bảo thời gian tính toán tổng thể ngắn hơn cho mọi trường hợp.
"Thật tuyệt khi biết" bạn có thể nghĩ, "nhưng làm thế nào biết được điều này giúp tôi giải quyết các vấn đề cụ thể về xử lý đa quy trình của mình?" Vâng, nó không. Câu trả lời ngắn gọn trung thực hơn là, "không có câu trả lời ngắn", "đa xử lý là phức tạp" và "nó phụ thuộc". Một triệu chứng quan sát được có thể có nhiều gốc rễ khác nhau, ngay cả đối với các tình huống tương tự.
Câu trả lời này cố gắng cung cấp cho bạn các khái niệm cơ bản giúp bạn có hình dung rõ ràng hơn về hộp đen lập lịch của Pool. Nó cũng cố gắng cung cấp cho bạn một số công cụ cơ bản trong tầm tay để nhận biết và tránh các vách đá tiềm ẩn liên quan đến kích thước khối.
Mục lục
Phần I
- Các định nghĩa
- Mục tiêu song song
- Kịch bản song song
- Rủi ro của Chunksize> 1
- Pool's Chunksize-Algorithm
Định lượng hiệu quả thuật toán
6.1 Mô hình
6.2 Lịch trình song song
6.3 Hiệu quả
6.3.1 Hiệu quả phân phối tuyệt đối (ADE)
6.3.2 Hiệu quả phân phối tương đối (RDE)
Phần II
- Thuật toán Chunksize-Algorithm của Naive vs.
- Kiểm tra thực tế
- Phần kết luận
Trước tiên cần phải làm rõ một số thuật ngữ quan trọng.
1. Định nghĩa
Chunk
Một đoạn ở đây là một phần của iterable
-argument được chỉ định trong một lời gọi phương thức gộp. Kích thước khối được tính toán như thế nào và điều này có thể có những ảnh hưởng gì, là chủ đề của câu trả lời này.
Bài tập
Biểu diễn vật lý của nhiệm vụ trong quy trình công nhân về mặt dữ liệu có thể được nhìn thấy trong hình bên dưới.
Hình bên cho thấy một lệnh gọi ví dụ đến pool.map()
, được hiển thị dọc theo một dòng mã, được lấy từ multiprocessing.pool.worker
hàm, trong đó một tác vụ được đọc từ inqueue
nó được giải nén. worker
là chức năng chính cơ bản trong MainThread
quy trình pool-worker-process. Đối số func
-được chỉ định trong phương thức func
gộp sẽ chỉ khớp với -variable bên trong-function worker
đối với các phương thức gọi đơn như apply_async
và for imap
với chunksize=1
. Đối với phần còn lại của các phương thức chunksize
gộp với -parameter, hàm xử lý func
sẽ là một hàm ánh xạ ( mapstar
hoặc starmapstar
). Chức năng này cũng ánh xạ người dùng do người dùng chỉ định như một đơn vị công việc .func
-parameter do trên mọi phần tử của đoạn được truyền của tệp có thể lặp (-> "map-task"). Thời gian này cần, xác định một nhiệm vụ
Taskel
Mặc dù việc sử dụng từ "task" cho toàn bộ quá trình xử lý một đoạn được khớp với mã bên trong multiprocessing.pool
, không có dấu hiệu nào cho thấy một lệnh gọi đơn lẻ đến người dùng do người dùng chỉ định func
, với một phần tử của đoạn là (các) đối số, sẽ như thế nào goi. Để tránh nhầm lẫn phát sinh từ xung đột đặt tên (hãy nghĩ đến maxtasksperchild
-parameter cho -method của Pool __init__
), câu trả lời này sẽ đề cập đến các đơn vị công việc đơn lẻ trong một nhiệm vụ dưới dạng taskel .
Một taskel (từ nhiệm vụ + el ement) là đơn vị nhỏ nhất của công việc trong một nhiệm vụ . Nó là việc thực thi một hàm được chỉ định với func
-parameter của một -method Pool
, được gọi với các đối số thu được từ một phần tử duy nhất của đoạn được truyền . Một nhiệm vụ bao gồm chunksize
taskels .
Chi phí song song (PO)
PO bao gồm chi phí nội bộ Python và chi phí chung cho giao tiếp giữa các quá trình (IPC). Chi phí cho mỗi tác vụ trong Python đi kèm với mã cần thiết để đóng gói và giải nén các tác vụ cũng như kết quả của nó. IPC-overhead đi kèm với sự đồng bộ hóa cần thiết của các luồng và sao chép dữ liệu giữa các không gian địa chỉ khác nhau (cần hai bước sao chép: cha -> hàng đợi -> con). Số lượng chi phí IPC phụ thuộc vào hệ điều hành, phần cứng và kích thước dữ liệu, điều này khiến việc khái quát về tác động trở nên khó khăn.
2. Mục tiêu song song hóa
Khi sử dụng đa xử lý, mục tiêu tổng thể của chúng tôi (rõ ràng) là giảm thiểu tổng thời gian xử lý cho tất cả các tác vụ. Để đạt được mục tiêu tổng thể này, mục tiêu kỹ thuật của chúng tôi cần phải tối ưu hóa việc sử dụng tài nguyên phần cứng .
Một số mục tiêu phụ quan trọng để đạt được mục tiêu kỹ thuật là:
- giảm thiểu chi phí song song (nổi tiếng nhất, nhưng không đơn lẻ: IPC )
- sử dụng cao trên tất cả các lõi cpu
- hạn chế mức sử dụng bộ nhớ để ngăn hệ điều hành phân trang quá mức (chuyển vào thùng rác )
Lúc đầu, các tác vụ cần phải đủ nặng về mặt tính toán (chuyên sâu), để kiếm lại PO, chúng ta phải trả tiền cho quá trình song song hóa. Mức độ liên quan của PO giảm khi tăng thời gian tính toán tuyệt đối cho mỗi nhiệm vụ. Hoặc, nói cách khác, thời gian tính toán tuyệt đối trên mỗi nhiệm vụ cho vấn đề của bạn càng lớn, thì nhu cầu giảm PO càng ít liên quan. Nếu quá trình tính toán của bạn mất hàng giờ cho mỗi tác vụ, thì tổng chi phí IPC sẽ không đáng kể so với. Mối quan tâm chính ở đây là ngăn chặn các tiến trình của worker chạy không tải sau khi tất cả các tác vụ đã được phân phối. Giữ tất cả các lõi được tải có nghĩa là, chúng tôi đang song song hóa nhiều nhất có thể.
3. Kịch bản song song
Yếu tố nào xác định một đối số kích thước khối tối ưu cho các phương thức như multiprocessing.Pool.map ()
Yếu tố chính được đề cập là thời gian tính toán có thể khác nhau trên các tác vụ đơn lẻ của chúng tôi. Để đặt tên cho nó, sự lựa chọn cho một kích thước khối tối ưu được xác định bởi Hệ số biến thiên ( CV ) cho thời gian tính toán trên mỗi nhiệm vụ.
Hai kịch bản cực đoan trên quy mô, sau đây từ mức độ của biến thể này là:
- Tất cả các nhiệm vụ cần thời gian tính toán chính xác như nhau.
- Nhiệm vụ có thể mất vài giây hoặc vài ngày để hoàn thành.
Để có khả năng ghi nhớ tốt hơn, tôi sẽ gọi các trường hợp sau là:
- Kịch bản dày đặc
- Kịch bản rộng
Kịch bản dày đặc
Trong một Kịch bản dày đặc, bạn nên phân phối tất cả các nhiệm vụ cùng một lúc, để giữ IPC cần thiết và chuyển đổi ngữ cảnh ở mức tối thiểu. Điều này có nghĩa là chúng tôi chỉ muốn tạo càng nhiều khối, càng nhiều quy trình công nhân càng có nhiều. Như đã nêu ở trên, trọng lượng của PO tăng lên khi thời gian tính toán ngắn hơn cho mỗi tác vụ.
Để có thông lượng tối đa, chúng tôi cũng muốn tất cả các quy trình của nhân viên bận rộn cho đến khi tất cả các tác vụ được xử lý (không có công nhân chạy không tải). Đối với mục tiêu này, các khối được phân phối phải có kích thước bằng hoặc gần bằng.
Kịch bản rộng
Ví dụ điển hình cho một Kịch bản rộng sẽ là một vấn đề tối ưu hóa, trong đó kết quả hội tụ nhanh chóng hoặc việc tính toán có thể mất hàng giờ, nếu không phải vài ngày. Thông thường, không thể dự đoán được hỗn hợp của "nhiệm vụ nhẹ" và "nhiệm vụ nặng" mà một nhiệm vụ sẽ chứa trong trường hợp này, do đó không nên phân phối quá nhiều nhiệm vụ trong một loạt tác vụ cùng một lúc. Phân phối ít nhiệm vụ cùng một lúc hơn có thể, có nghĩa là tăng tính linh hoạt trong việc lập lịch trình. Điều này là cần thiết ở đây để đạt được mục tiêu phụ của chúng tôi là sử dụng cao tất cả các lõi.
Nếu Pool
các phương pháp, theo mặc định, sẽ được tối ưu hóa hoàn toàn cho Kịch bản dày đặc, thì chúng sẽ ngày càng tạo ra thời gian tối ưu cho mọi vấn đề ở gần Kịch bản rộng hơn.
4. Rủi ro của Chunksize> 1
Hãy xem xét ví dụ về mã giả được đơn giản hóa này về một Kịch bản rộng-có thể xác định được, mà chúng tôi muốn chuyển vào một phương thức gộp:
good_luck_iterable = [60, 60, 86400, 60, 86400, 60, 60, 84600]
Thay vì các giá trị thực tế, chúng tôi giả vờ xem thời gian tính toán cần thiết tính bằng giây, đơn giản là chỉ 1 phút hoặc 1 ngày. Chúng tôi giả định rằng nhóm có bốn quy trình công nhân (trên bốn lõi) và chunksize
được đặt thành 2
. Bởi vì đơn đặt hàng sẽ được giữ, các khối gửi cho người lao động sẽ là:
[(60, 60), (86400, 60), (86400, 60), (60, 84600)]
Vì chúng tôi có đủ công nhân và thời gian tính toán đủ cao, chúng tôi có thể nói, rằng mọi quy trình công nhân sẽ có một phần nhỏ để làm việc ngay từ đầu. (Điều này không nhất thiết phải xảy ra khi hoàn thành nhiệm vụ nhanh chóng). Hơn nữa, chúng tôi có thể nói, toàn bộ quá trình xử lý sẽ mất khoảng 86400 + 60 giây, bởi vì đó là tổng thời gian tính toán cao nhất cho một đoạn trong kịch bản nhân tạo này và chúng tôi chỉ phân phối các đoạn một lần.
Bây giờ hãy xem xét điều này có thể lặp lại, chỉ có một phần tử chuyển đổi vị trí của nó so với phần tử có thể lặp lại trước đó:
bad_luck_iterable = [60, 60, 86400, 86400, 60, 60, 60, 84600]
... và các phần tương ứng:
[(60, 60), (86400, 86400), (60, 60), (60, 84600)]
Thật không may mắn với việc sắp xếp các tệp có thể lặp lại đã tăng gần gấp đôi (86400 + 86400) tổng thời gian xử lý của chúng tôi! Công nhân nhận được -chunk xấu xa (86400, 86400) đang chặn nhiệm vụ nặng thứ hai trong nhiệm vụ của nó để phân phối cho một trong những công nhân đang chạy không tải đã hoàn thành -chunks (60, 60) của họ. Chúng tôi rõ ràng sẽ không mạo hiểm với một kết quả khó chịu như vậy nếu chúng tôi đặt chunksize=1
.
Đây là nguy cơ của các khối lớn hơn. Với kích thước cao hơn, chúng tôi giao dịch tính linh hoạt trong việc lập lịch trình để có ít chi phí hơn và trong những trường hợp như trên, đó là một thỏa thuận tồi.
Chúng ta sẽ thấy như thế nào trong chương 6. Định lượng Hiệu quả Thuật toán , các khối lớn hơn cũng có thể dẫn đến kết quả không tối ưu cho các Kịch bản dày đặc .
5. Thuật toán Chunksize-Algorithm của Pool
Dưới đây, bạn sẽ tìm thấy một phiên bản được sửa đổi một chút của thuật toán bên trong mã nguồn. Như bạn có thể thấy, tôi đã cắt phần dưới và gói nó thành một hàm để tính toán chunksize
đối số bên ngoài. Tôi cũng thay thế 4
bằng một factor
tham số và thuê ngoài các len()
cuộc gọi.
def calc_chunksize(n_workers, len_iterable, factor=4):
"""Calculate chunksize argument for Pool-methods.
Resembles source-code within `multiprocessing.pool.Pool._map_async`.
"""
chunksize, extra = divmod(len_iterable, n_workers * factor)
if extra:
chunksize += 1
return chunksize
Để đảm bảo tất cả chúng ta đều ở trên cùng một trang, đây là những gì divmod
:
divmod(x, y)
là một hàm nội trang trả về (x//y, x%y)
.
x // y
là phép chia tầng, trả về thương số làm tròn xuống từ x / y
, trong khi
x % y
phép toán modulo trả về phần còn lại từ đó x / y
. Do đó, ví dụ như divmod(10, 3)
lợi nhuận (3, 1)
.
Bây giờ khi bạn nhìn vào chunksize, extra = divmod(len_iterable, n_workers * 4)
, bạn sẽ thấy n_workers
đây là số chia y
trong x / y
và phép nhân bằng 4
, mà không điều chỉnh hơn nữa thông qua if extra: chunksize +=1
sau này, dẫn đến một chunksize ban đầu ít nhất nhỏ hơn gấp bốn lần (cho len_iterable >= n_workers * 4
) hơn nó sẽ khác.
Để xem ảnh hưởng của phép nhân với 4
kết quả kích thước khối trung gian, hãy xem xét hàm này:
def compare_chunksizes(len_iterable, n_workers=4):
"""Calculate naive chunksize, Pool's stage-1 chunksize and the chunksize
for Pool's complete algorithm. Return chunksizes and the real factors by
which naive chunksizes are bigger.
"""
cs_naive = len_iterable // n_workers or 1
cs_pool1 = len_iterable // (n_workers * 4) or 1
cs_pool2 = calc_chunksize(n_workers, len_iterable)
real_factor_pool1 = cs_naive / cs_pool1
real_factor_pool2 = cs_naive / cs_pool2
return cs_naive, cs_pool1, cs_pool2, real_factor_pool1, real_factor_pool2
Hàm trên tính toán kích thước khối ngây thơ ( cs_naive
) và kích thước khối bước đầu tiên của thuật toán chunksize-Pool ( cs_pool1
), cũng như kích thước khối cho thuật toán Pool-hoàn chỉnh ( cs_pool2
). Hơn nữa, nó tính toán các yếu tố thực rf_pool1 = cs_naive / cs_pool1
và rf_pool2 = cs_naive / cs_pool2
cho chúng ta biết số lần các khối được tính toán nguyên bản lớn hơn (các) phiên bản nội bộ của Pool.
Dưới đây, bạn thấy hai hình được tạo với đầu ra từ chức năng này. Hình bên trái chỉ cho thấy các khối cho n_workers=4
đến khi có độ dài có thể lặp lại là 500
. Hình bên phải hiển thị các giá trị cho rf_pool1
. Đối với độ dài có thể lặp lại 16
, yếu tố thực trở thành >=4
(for len_iterable >= n_workers * 4
) và giá trị lớn nhất của nó là 7
đối với độ dài có thể lặp lại 28-31
. Đó là một độ lệch lớn so với yếu tố ban đầu 4
mà thuật toán hội tụ cho các lần lặp dài hơn. 'Dài hơn' ở đây là tương đối và phụ thuộc vào số lượng công nhân cụ thể.
Hãy nhớ chunksize cs_pool1
vẫn thiếu phần điều chỉnh- extra
với phần còn lại từ được divmod
chứa trong cs_pool2
thuật toán hoàn chỉnh.
Thuật toán tiếp tục với:
if extra:
chunksize += 1
Bây giờ trong trường hợp đã có là một phần còn lại (một extra
từ divmod tác), tăng chunksize bởi 1 rõ ràng là không thể làm việc ra cho mỗi nhiệm vụ. Rốt cuộc, nếu có, sẽ không có phần còn lại để bắt đầu.
Làm thế nào bạn có thể nhìn thấy trong hình dưới đây, các " ngoại điều trị " có hiệu lực thi hành, rằng yếu tố thực cho rf_pool2
hiện nay hội tụ về phía 4
từ bên dưới 4
và độ lệch có phần mượt mà hơn. Độ lệch chuẩn cho n_workers=4
và len_iterable=500
giảm từ 0.5233
cho rf_pool1
đến 0.4115
cho rf_pool2
.
Cuối cùng, tăng chunksize
1 có tác dụng, tác vụ cuối cùng được truyền chỉ có kích thước là len_iterable % chunksize or chunksize
.
Tuy nhiên, điều thú vị hơn và chúng ta sẽ thấy như thế nào sau này, do hậu quả, tác động của việc xử lý bổ sung có thể được quan sát đối với số lượng các khối được tạo ( n_chunks
). Đối với các đoạn lặp đủ dài, thuật toán kích thước khối đã hoàn thành của Pool ( n_pool2
trong hình bên dưới) sẽ ổn định số lượng khối tại n_chunks == n_workers * 4
. Ngược lại, thuật toán ngây thơ (sau một lần ợ hơi ban đầu) tiếp tục xen kẽ giữa n_chunks == n_workers
và n_chunks == n_workers + 1
khi độ dài của tệp có thể lặp lại tăng lên.
Dưới đây, bạn sẽ tìm thấy hai hàm thông tin nâng cao cho Pool và thuật toán kích thước khối ngây thơ. Đầu ra của các hàm này sẽ cần thiết trong chương tiếp theo.
from collections import namedtuple
Chunkinfo = namedtuple(
'Chunkinfo', ['n_workers', 'len_iterable', 'n_chunks',
'chunksize', 'last_chunk']
)
def calc_chunksize_info(n_workers, len_iterable, factor=4):
"""Calculate chunksize numbers."""
chunksize, extra = divmod(len_iterable, n_workers * factor)
if extra:
chunksize += 1
n_chunks = len_iterable // chunksize + (len_iterable % chunksize > 0)
last_chunk = len_iterable % chunksize or chunksize
return Chunkinfo(
n_workers, len_iterable, n_chunks, chunksize, last_chunk
)
Đừng bối rối bởi cái nhìn có thể bất ngờ của calc_naive_chunksize_info
. Các extra
từ divmod
không được sử dụng để tính chunksize.
def calc_naive_chunksize_info(n_workers, len_iterable):
"""Calculate naive chunksize numbers."""
chunksize, extra = divmod(len_iterable, n_workers)
if chunksize == 0:
chunksize = 1
n_chunks = extra
last_chunk = chunksize
else:
n_chunks = len_iterable // chunksize + (len_iterable % chunksize > 0)
last_chunk = len_iterable % chunksize or chunksize
return Chunkinfo(
n_workers, len_iterable, n_chunks, chunksize, last_chunk
)
6. Định lượng hiệu quả thuật toán
Bây giờ, sau khi chúng ta đã thấy đầu ra của Pool
thuật toán chunksize trông khác như thế nào so với đầu ra từ thuật toán ngây thơ ...
- Làm thế nào để biết liệu phương pháp của Pool có thực sự cải thiện điều gì đó không?
- Và chính xác thì thứ này có thể là gì?
Như đã trình bày trong chương trước, đối với các đoạn lặp dài hơn (số lượng nhiệm vụ lớn hơn), thuật toán phân khối của Pool chia khoảng có thể lặp lại thành nhiều phần nhiều hơn bốn lần so với phương pháp đơn giản. Các khối nhỏ hơn có nghĩa là nhiều nhiệm vụ hơn và nhiều nhiệm vụ hơn có nghĩa là Chi phí Song song (PO) nhiều hơn , một chi phí phải được cân nhắc dựa trên lợi ích của việc tăng tính linh hoạt trong lịch trình (nhớ lại "Rủi ro của Chunksize> 1" ).
Vì những lý do khá rõ ràng, thuật toán phân khối cơ bản của Pool không thể cân nhắc tính linh hoạt của việc lập lịch so với PO đối với chúng tôi. IPC-overhead phụ thuộc vào hệ điều hành, phần cứng- và kích thước dữ liệu. Thuật toán không thể biết chúng tôi chạy mã của mình trên phần cứng nào, cũng như không có manh mối về thời gian một nhiệm vụ sẽ hoàn thành. Đó là một heuristic cung cấp chức năng cơ bản cho tất cả các tình huống có thể xảy ra. Điều này có nghĩa là nó không thể được tối ưu hóa cho bất kỳ trường hợp cụ thể nào. Như đã đề cập trước đây, PO cũng ngày càng trở nên ít được quan tâm hơn với việc tăng thời gian tính toán trên mỗi nhiệm vụ (tương quan nghịch).
Khi bạn nhớ lại Mục tiêu song song từ chương 2, một gạch đầu dòng là:
- sử dụng cao trên tất cả các lõi cpu
Điều đã đề cập trước đó , thuật toán phân khối của Pool có thể cố gắng cải thiện là giảm thiểu các quy trình chạy không tải của nhân viên , tương ứng là việc sử dụng các lõi cpu .
Một câu hỏi lặp lại trên SO liên quan đến multiprocessing.Pool
được hỏi bởi những người thắc mắc về các lõi không sử dụng / các quy trình công nhân chạy không tải trong các tình huống mà bạn mong đợi tất cả các quy trình công nhân đều bận rộn. Mặc dù điều này có thể có nhiều lý do, các quy trình công nhân chạy không tải cho đến khi kết thúc tính toán là một quan sát mà chúng ta thường có thể thực hiện, ngay cả với các Kịch bản dày đặc (thời gian tính toán bằng nhau trên mỗi tác vụ) trong trường hợp số lượng công nhân không phải là ước của số của khối ( n_chunks % n_workers > 0
).
Câu hỏi bây giờ là:
Làm thế nào chúng ta thực tế có thể chuyển sự hiểu biết của chúng ta về các khối thành một thứ gì đó cho phép chúng ta giải thích việc sử dụng nhân viên được quan sát, hoặc thậm chí so sánh hiệu quả của các thuật toán khác nhau về vấn đề đó?
6.1 Mô hình
Để có những hiểu biết sâu sắc hơn ở đây, chúng ta cần một dạng trừu tượng của các phép tính song song giúp đơn giản hóa thực tế quá phức tạp xuống mức độ phức tạp có thể quản lý được, đồng thời duy trì ý nghĩa trong các ranh giới xác định. Một sự trừu tượng như vậy được gọi là một mô hình . Việc triển khai " Mô hình song song" (PM) như vậy sẽ tạo ra siêu dữ liệu được ánh xạ công nhân (dấu thời gian) giống như các phép tính thực tế, nếu dữ liệu được thu thập. Siêu dữ liệu do mô hình tạo ra cho phép dự đoán các số liệu của các phép tính song song theo các ràng buộc nhất định.
Một trong hai mô hình con trong PM được xác định ở đây là Mô hình phân phối (DM) . Các DM giải thích cách các đơn vị công tác (taskels) nguyên tử được phân phối qua công nhân song song và thời gian , khi không có các yếu tố khác hơn chunksize-thuật toán tương ứng, số lượng người lao động, đầu vào-iterable (số taskels) và thời gian tính toán của họ được coi . Điều này có nghĩa bất kỳ hình thức chi phí được không bao gồm.
Để có được PM hoàn chỉnh , DM được mở rộng với Mô hình trên không (OM) , đại diện cho nhiều dạng khác nhau của Chi phí song song (PO) . Một mô hình như vậy cần được hiệu chỉnh cho từng nút riêng lẻ (phần cứng, phụ thuộc hệ điều hành). Có bao nhiêu dạng chi phí được biểu diễn trong một OM được bỏ ngỏ và vì vậy có thể tồn tại nhiều OM với mức độ phức tạp khác nhau. Mức độ chính xác mà OM đã thực hiện cần được xác định bởi trọng số tổng thể của PO cho phép tính cụ thể. Nguyên công ngắn hơn dẫn đến trọng lượng PO cao hơn , do đó đòi hỏi OM chính xác hơn nếu chúng tôi cố gắng dự đoán Hiệu quả song song (PE) .
6.2 Lịch trình song song (PS)
Các Schedule Parallel là một đại diện hai chiều của việc tính toán song song, nơi trục x đại diện cho thời gian và trục y đại diện cho một hồ bơi của người lao động song song. Số lượng công nhân và tổng thời gian tính toán đánh dấu phần mở rộng của một hình chữ nhật, trong đó các hình chữ nhật nhỏ hơn được vẽ vào. Những hình chữ nhật nhỏ hơn này đại diện cho các đơn vị nguyên tử của công việc (nhiệm vụ).
Dưới đây, bạn tìm thấy hình ảnh của một PS được vẽ bằng dữ liệu từ thuật toán kích thước khối của DM of Pool cho Kịch bản dày đặc .
- Trục x được chia thành các đơn vị thời gian bằng nhau, trong đó mỗi đơn vị tượng trưng cho thời gian tính toán mà nhiệm vụ yêu cầu.
- Trục y được chia thành số lượng xử lý công nhân mà nhóm sử dụng.
- Nhiệm vụ ở đây được hiển thị dưới dạng hình chữ nhật nhỏ nhất có màu lục lam, được đưa vào dòng thời gian (lịch trình) của một quy trình công nhân ẩn danh.
- Nhiệm vụ là một hoặc nhiều nhiệm vụ trong dòng thời gian của worker liên tục được đánh dấu với cùng một màu sắc.
- Đơn vị thời gian chạy không tải được thể hiện thông qua các ô màu đỏ.
- Lịch trình song song được chia thành nhiều phần. Phần cuối cùng là phần đuôi.
Tên của các bộ phận được sáng tác có thể được nhìn thấy trong hình dưới đây.
Trong một PM hoàn chỉnh bao gồm OM , Idling Share không giới hạn ở phần đuôi, mà còn bao gồm không gian giữa các nhiệm vụ và thậm chí giữa các nhiệm vụ.
6.3 Hiệu quả
Các Mô hình được giới thiệu ở trên cho phép định lượng tỷ lệ sử dụng công nhân. Chúng ta có thể phân biệt:
- Hiệu quả phân phối (DE) - được tính với sự trợ giúp của DM (hoặc một phương pháp đơn giản hóa cho Kịch bản dày đặc ).
- Hiệu quả song song (PE) - được tính toán với sự trợ giúp của PM đã hiệu chỉnh (dự đoán) hoặc được tính toán từ siêu dữ liệu của các phép tính thực.
Điều quan trọng cần lưu ý là hiệu quả được tính toán không tự động tương quan với tính toán tổng thể nhanh hơn cho một vấn đề song song nhất định. Việc sử dụng công nhân trong ngữ cảnh này chỉ phân biệt giữa công nhân có nhiệm vụ đã bắt đầu nhưng chưa hoàn thành và công nhân không có nhiệm vụ "mở" như vậy. Điều đó có nghĩa là, khả năng chạy không tải trong khoảng thời gian của tác vụ không được đăng ký.
Tất cả các hiệu quả được đề cập ở trên về cơ bản có được bằng cách tính toán thương số của bộ phận Chia sẻ bận rộn / Lịch trình song song . Sự khác biệt giữa DE và PE đi kèm với Tỷ lệ bận rộn chiếm một phần nhỏ hơn của Lịch trình song song tổng thể cho PM kéo dài trên không .
Câu trả lời này sẽ chỉ thảo luận thêm về một phương pháp đơn giản để tính DE cho Kịch bản dày đặc. Điều này đủ thích hợp để so sánh các thuật toán kích thước khối khác nhau, vì ...
- ... DM là một phần của PM , thay đổi với các thuật toán kích thước khối khác nhau được sử dụng.
- ... Kịch bản dày đặc với thời lượng tính toán bằng nhau trên mỗi tác vụ mô tả "trạng thái ổn định", mà các khoảng thời gian này không nằm ngoài phương trình. Bất kỳ kịch bản nào khác sẽ chỉ dẫn đến kết quả ngẫu nhiên vì thứ tự của các nguyên công sẽ quan trọng.
6.3.1 Hiệu quả phân phối tuyệt đối (ADE)
Nói chung, hiệu quả cơ bản này có thể được tính bằng cách chia Tỷ lệ bận rộn cho toàn bộ tiềm năng của Lịch trình song song :
Hiệu quả phân phối tuyệt đối (ADE) = Chia sẻ bận rộn / Lịch trình song song
Đối với Kịch bản dày đặc , mã tính toán đơn giản trông giống như sau:
def calc_ade(n_workers, len_iterable, n_chunks, chunksize, last_chunk):
"""Calculate Absolute Distribution Efficiency (ADE).
`len_iterable` is not used, but contained to keep a consistent signature
with `calc_rde`.
"""
if n_workers == 1:
return 1
potential = (
((n_chunks // n_workers + (n_chunks % n_workers > 1)) * chunksize)
+ (n_chunks % n_workers == 1) * last_chunk
) * n_workers
n_full_chunks = n_chunks - (chunksize > last_chunk)
taskels_in_regular_chunks = n_full_chunks * chunksize
real = taskels_in_regular_chunks + (chunksize > last_chunk) * last_chunk
ade = real / potential
return ade
Nếu không có Chia sẻ không hoạt động , Chia sẻ bận sẽ bằng với Lịch trình song song , do đó chúng tôi nhận được ADE là 100%. Trong mô hình đơn giản của chúng tôi, đây là một kịch bản mà tất cả các quy trình có sẵn sẽ bận rộn trong suốt thời gian cần thiết để xử lý tất cả các tác vụ. Nói cách khác, toàn bộ công việc đạt hiệu quả song song 100%.
Nhưng tại sao ở đây tôi cứ gọi PE là PE tuyệt đối ?
Để hiểu được điều đó, chúng ta phải xem xét một trường hợp có thể xảy ra đối với chunksize (cs) đảm bảo tính linh hoạt trong lịch trình tối đa (ngoài ra, số lượng người Tây Nguyên có thể có. Có trùng hợp không?):
__________________________________ ~ MỘT ~ __________________________________
Ví dụ: nếu chúng ta có bốn quy trình công nhân và 37 nhiệm vụ, sẽ có công nhân chạy không tải ngay cả với chunksize=1
, chỉ vì n_workers=4
không phải là ước của 37. Phần còn lại của phép chia 37/4 là 1. Nhiệm vụ duy nhất còn lại này sẽ phải là được xử lý bởi một công nhân duy nhất, trong khi ba người còn lại đang chạy không tải.
Tương tự như vậy, vẫn sẽ có một công nhân chạy không tải với 39 nguyên công, bạn có thể xem hình bên dưới.
Khi bạn so sánh Lịch biểu song song phía trên cho chunksize=1
với phiên bản bên dưới cho chunksize=3
, bạn sẽ nhận thấy rằng Lịch biểu song song phía trên nhỏ hơn, dòng thời gian trên trục x ngắn hơn. Bây giờ nó sẽ trở nên rõ ràng, các khối lớn hơn bất ngờ như thế nào cũng có thể dẫn đến tăng thời gian tính toán tổng thể, ngay cả đối với các Kịch bản dày đặc .
Nhưng tại sao không chỉ sử dụng độ dài của trục x để tính toán hiệu quả?
Bởi vì chi phí không được chứa trong mô hình này. Nó sẽ khác nhau đối với cả hai khối, do đó trục x không thực sự so sánh trực tiếp. Chi phí vẫn có thể dẫn đến tổng thời gian tính toán lâu hơn như thể hiện trong trường hợp 2 từ hình bên dưới.
6.3.2 Hiệu quả phân phối tương đối (RDE)
Các ADE giá trị không chứa các thông tin nếu một tốt hơn phân phối taskels có thể với bộ chunksize để 1. Better đây vẫn là một nhỏ Idling Share .
Để có được một giá trị DE được điều chỉnh cho DE lớn nhất có thể , chúng ta phải chia ADE được xem xét cho ADE mà chúng ta nhận được chunksize=1
.
Hiệu quả phân phối tương đối (RDE) = ADE_cs_x / ADE_cs_1
Đây là cách điều này trông như thế nào trong mã:
def calc_rde(n_workers, len_iterable, n_chunks, chunksize, last_chunk):
"""Calculate Relative Distribution Efficiency (RDE)."""
ade_cs1 = calc_ade(
n_workers, len_iterable, n_chunks=len_iterable,
chunksize=1, last_chunk=1
)
ade = calc_ade(n_workers, len_iterable, n_chunks, chunksize, last_chunk)
rde = ade / ade_cs1
return rde
RDE , được định nghĩa ở đây như thế nào, về bản chất là một câu chuyện về phần đuôi của một Lịch trình song song . RDE bị ảnh hưởng bởi kích thước khối hiệu quả tối đa có trong phần đuôi. (Đuôi này có thể có độ dài trục x chunksize
hoặc last_chunk
.) Điều này dẫn đến kết quả là RDE tự nhiên hội tụ 100% (thậm chí) cho tất cả các loại "đuôi trông" như thể hiện trong hình bên dưới.
RDE thấp ...
- là một gợi ý mạnh mẽ cho tiềm năng tối ưu hóa.
- tự nhiên ít có khả năng lặp lại hơn, vì phần đuôi tương đối của Lịch biểu song song tổng thể thu hẹp lại.
Vui lòng tìm Phần II của câu trả lời này tại đây .
4
Là tùy ý và toàn bộ việc tính toán kích thước khối là một phép tính toán. Yếu tố liên quan là thời gian xử lý thực tế của bạn có thể thay đổi bao nhiêu. Thêm một chút về điều này ở đây cho đến khi tôi có thời gian để trả lời nếu vẫn cần.