Đệ quy có thể được thực hiện song song? Điều đó sẽ có ý nghĩa?


8

Giả sử, tôi đang sử dụng một thuật toán đệ quy đơn giản cho MySpace, sẽ được thực thi như sau:

fib(5) -> fib(4)+fib(3)
            |      |
      fib(3)+fib(2)|
                fib(2)+fib(1)

và như thế

Bây giờ, việc thực hiện sẽ vẫn tuần tự. Thay vào đó, tôi sẽ viết mã này như thế nào fib(4)fib(3)được tính bằng cách sinh ra 2 luồng riêng biệt, sau đó fib(4), 2 luồng được sinh ra cho fib(3)fib(2). Tương tự khi nào fib(3)được chia thành fib(2)fib(1)?

(Tôi biết rằng lập trình động sẽ là cách tiếp cận tốt hơn đối với Fibonacci, chỉ sử dụng nó như một ví dụ dễ dàng ở đây)

(nếu ai đó có thể chia sẻ một mẫu mã trong C \ C ++ \ C #, đó sẽ là lý tưởng)


3
Tất nhiên điều này là có thể - và đôi khi nó sẽ hữu ích. Điều kiện duy nhất là đó fibphải là một hàm thuần túy (có lẽ là trường hợp ở đây). Một đặc tính tốt là nếu phiên bản đệ quy tuần tự là chính xác, phiên bản song song cũng sẽ chính xác. Nhưng nếu nó không chính xác và có tính năng đệ quy vô hạn, bạn sẽ đột nhiên tạo ra một quả bom ngã ba .
amon

Là chủ đề gộp có thể trong trường hợp này? Tôi nghĩ là không, vì luồng tính toán fib(n)sẽ không hoàn thành cho đến khi nhận được kết quả từ cả hai fib(n-1)fib(n-2). Điều này sẽ gây ra bế tắc, vì để một chủ đề kết thúc và quay trở lại cuộc thăm dò, nó sẽ cần phải lấy một chủ đề khác từ nhóm. Có cách nào để giái quyết vấn đề này không?
Idan Arye

1
Bạn có thể tìm thấy các tính toán đệ quy bằng Mapreduce trên Stack Overflow một cách đọc thú vị.

Câu trả lời:


27

Điều này là có thể nhưng một ý tưởng thực sự tồi tệ; tính ra số lượng luồng bạn sẽ sinh ra khi tính toán sợi (16), sau đó nhân số đó với chi phí của một luồng. Chủ đề là cực kỳ tốn kém; làm điều này cho nhiệm vụ mà bạn mô tả giống như thuê một người đánh máy khác nhau để nhập từng nhân vật của một cuốn tiểu thuyết.

Điều đó nói rằng, các thuật toán đệ quy thường là ứng cử viên tốt cho việc song song hóa, đặc biệt nếu chúng chia công việc thành hai công việc nhỏ hơn có thể được thực hiện độc lập. Bí quyết là biết khi nào nên dừng song song.

Nói chung, bạn muốn song song chỉ các tác vụ "song song xấu hổ". Đó là, các nhiệm vụ tốn kém về mặt tính toán và có thể được tính toán độc lập . Nhiều người quên đi phần đầu tiên. Các chủ đề rất tốn kém đến nỗi chỉ có ý nghĩa để tạo ra một khi bạn có một khối lượng công việc khổng lồ để họ làm, và hơn nữa, bạn có thể dành toàn bộ bộ xử lý cho chuỗi . Nếu bạn có 8 bộ xử lý thì việc tạo ra 80 luồng sẽ buộc chúng phải chia sẻ bộ xử lý, làm chậm từng bộ xử lý xuống rất nhiều. Bạn làm tốt hơn để chỉ tạo 8 luồng và cho phép mỗi luồng có quyền truy cập 100% vào bộ xử lý khi bạn có một tác vụ song song đáng xấu hổ để thực hiện.

Các thư viện như Thư viện song song tác vụ trong .NET được thiết kế để tự động tìm hiểu mức độ hiệu quả của tính song song; bạn có thể xem xét nghiên cứu thiết kế của nó nếu chủ đề này làm bạn quan tâm.


3

Câu hỏi có hai câu trả lời, thực sự.

Đệ quy có thể được thực hiện song song? Điều đó sẽ có ý nghĩa?

Phải, tất nhiên. Trong hầu hết các trường hợp (tất cả?), Một thuật toán đệ quy có thể được viết lại theo cách không có đệ quy, dẫn đến một thuật toán thường khá dễ dàng song song. Không phải luôn luôn, nhưng thường xuyên.

Hãy suy nghĩ Quicksort hoặc lặp qua cây thư mục. Trong cả hai trường hợp, một hàng đợi có thể được sử dụng để giữ tất cả các kết quả trung gian tương ứng. thư mục con được tìm thấy. Hàng đợi có thể được xử lý song song, cuối cùng tạo ra nhiều mục cho đến khi nhiệm vụ được hoàn thành thành công.

Còn fib()ví dụ thì sao?

Thật không may, hàm Fibonacci là một lựa chọn tồi, bởi vì các giá trị đầu vào hoàn thành phụ thuộc vào kết quả tính toán trước đó. Sự phụ thuộc này làm cho nó khó thực hiện song song nếu bạn bắt đầu mọi lúc với 11.

Tuy nhiên, nếu bạn cần thực hiện các phép tính Fibonacci thường xuyên hơn, có thể nên lưu trữ (hoặc bộ đệm) các kết quả được tính toán trước để tránh tất cả các tính toán cho đến thời điểm đó. Khái niệm đằng sau khá giống với bảng cầu vồng.

Hãy nói rằng, bạn lưu trữ mỗi cặp số Fibo thứ 10 lên tới 10.000. Bắt đầu thói quen khởi tạo này trên một chủ đề nền. Bây giờ, nếu ai đó yêu cầu Fibo số 5246, thuật toán chỉ cần chọn cặp từ 5240 và bắt đầu tính toán từ thời điểm đó trở đi. Nếu cặp 5240 chưa có, chỉ cần chờ nó.

Bằng cách này, việc tính toán nhiều số fibo được chọn ngẫu nhiên có thể được thực hiện rất hiệu quả và song song, vì rất khó có khả năng hai luồng sẽ phải tính cùng một số - và thậm chí sau đó, nó sẽ không thành vấn đề.


1

Tất nhiên là có thể, nhưng với một ví dụ nhỏ như vậy (và thực tế, đối với nhiều người lớn hơn nhiều) số lượng mã kiểm soát hệ thống ống nước / đồng thời bạn phải viết sẽ che khuất mã doanh nghiệp đến mức nó sẽ không là một ý tưởng tốt trừ khi bạn thực sự, thực sự, thực sự cần số Fibonacci được tính toán rất nhanh.

Hầu như luôn luôn dễ đọc và dễ bảo trì hơn để xây dựng thuật toán của bạn một cách bình thường và sau đó để thư viện / phần mở rộng ngôn ngữ đồng thời như TBB hoặc GCD quan tâm đến cách phân phối các bước thực sự cho các luồng.


0

Trong ví dụ của bạn, bạn đang tính toán sợi (3) hai lần, điều này dẫn đến việc thực hiện gấp đôi toàn bộ sợi (1) và sợi (2), đối với số cao hơn, điều đó thậm chí còn tồi tệ hơn.

Bạn có thể đạt được tốc độ vượt trội so với giải pháp không lợi nhuận, nhưng nó sẽ tốn nhiều chi phí hơn cho tài nguyên (bộ xử lý) so với giá trị của nó.


0

Có nó có thể! Ví dụ đơn giản nhất tôi có thể cung cấp cho bạn là tưởng tượng một cây số nhị phân. Vì một số lý do, bạn muốn cộng tất cả các số trong cây nhị phân. Để làm như vậy, bạn cần thêm giá trị của nút gốc vào giá trị của nút trái / phải .... nhưng chính nút đó có thể là gốc của cây khác (một cây con cho cây gốc)
Thay vì tính toán tổng của cây con bên trái, sau đó là tổng của bên phải ... sau đó thêm chúng vào giá trị của gốc ... bạn có thể tính tổng của cây con bên trái và bên phải song song.


0

Một vấn đề là thuật toán đệ quy tiêu chuẩn cho hàm Wikipedia rất tệ, vì số lượng cuộc gọi để tính toán sợi (n) bằng với sợi (n) đang tăng rất nhanh. Vì vậy, tôi thực sự sẽ từ chối thảo luận về điều đó.

Hãy xem xét một thuật toán đệ quy hợp lý hơn, Quicksort. Nó sắp xếp một mảng bằng cách làm như sau: Nếu mảng nhỏ thì sắp xếp nó bằng cách sử dụng Bubbledort, sắp xếp chèn hoặc bất cứ thứ gì. Mặt khác: Chọn một phần tử của mảng. Đặt tất cả các yếu tố nhỏ hơn sang một bên, tất cả các yếu tố lớn hơn sang phía bên kia. Sắp xếp bên với các yếu tố nhỏ hơn. Sắp xếp bên với các yếu tố lớn hơn.

Để tránh đệ quy sâu tùy ý, phương pháp thông thường là hàm sắp xếp nhanh sẽ thực hiện một cuộc gọi đệ quy cho phần nhỏ hơn của hai bên (một phần tử có ít phần tử hơn) và tự xử lý phần lớn hơn.

Bây giờ bạn có một cách rất đơn giản để sử dụng nhiều luồng: Thay vì thực hiện cuộc gọi đệ quy để sắp xếp bên nhỏ hơn, hãy bắt đầu một chuỗi; sau đó sắp xếp một nửa lớn hơn, sau đó đợi cho chủ đề kết thúc. Nhưng chủ đề bắt đầu là đắt tiền. Vì vậy, bạn đo thời gian trung bình để sắp xếp n phần tử, so với thời gian để tạo một chủ đề. Từ đó bạn tìm thấy n nhỏ nhất sao cho đáng để tạo một chủ đề mới. Vì vậy, nếu phía nhỏ hơn cần được sắp xếp dưới kích thước đó, bạn thực hiện một cuộc gọi đệ quy. Nếu không, bạn sắp xếp một nửa trong một chủ đề mới.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.