2011-12-07 34 views
7

Cho một mảng [x1, x2, x3, ..., xk] trong đó xi là số mục trong hộp i, cách tôi có thể phân phối lại các mục sao cho không có hộp nào chứa nhiều hơn N mục. N gần bằng tổng (xi)/k - Nghĩa là, N gần với mọi ô có cùng số mục. Hộp trung gian không được dùng để mang vật phẩm - nếu x1 có thặng dư và x2 và x3 bị thiếu, x1 sẽ gửi một số mục đến x2 và x3, nhưng không gửi tất cả các mục của nó đến x2 và sau đó để x2 giải quyết phần dư .tên của thuật toán liên quan đến cân bằng tải/phân phối lại

Vấn đề thực tế là: mỗi nút tính toán có một tập hợp mẫu và sau một bước lấy mẫu lại, một số nút máy tính có thể có thặng dư trong khi các nút khác bị thiếu hụt, vì vậy tôi muốn phân phối lại mẫu .

Tôi hình dung loại sự cố này có tên.

+1

Câu hỏi của bạn chưa được xác định. Ví dụ, có thể tá tá các nút xa đồng thời nói chuyện với những người hàng xóm gần của họ, với chi phí 1? Nếu 100 nút có thư đến X, họ có thể gửi tất cả cùng một lúc, với chi phí 1 hay chúng phải được đăng theo thứ tự, với chi phí 100? Các thuật toán khác nhau sẽ áp dụng cho [mô hình tính toán] khác nhau (http://en.wikipedia.org/wiki/Models_of_computation). Cụ thể, nêu rõ [cấu trúc liên kết mạng] (http://en.wikipedia.org/wiki/Network_topology) và/hoặc mô hình bộ nhớ phân tán. –

+0

Nó thực sự chưa được xác định, nhưng một vài câu trả lời dưới đây đã cung cấp rất nhiều trợ giúp. – Gus

Trả lời

3

Sự cố này có thể được mô hình hóa dưới dạng phiên bản minimum-cost flow.

Cho G là một đồ thị có hướng với đỉnh s, t, một ..., một k, b , ..., b k và cung s → một i công suất x i và chi phí 0, vòng cung một i → b j công suất vô hạn và chi phí 0 nếu i = j và chi phí 1 nếu i ≠ j, và cung b j → t công suất N và chi phí 0. Chúng tôi muốn chi phí tối thiểu dòng chảy di chuyển ∑ i x i đơn vị từ s đến t. Ý tưởng là nếu các đơn vị y chuyển một số i → b j, thì các mục y được chuyển từ hộp i sang hộp j (nếu i ≠ j; nếu i = j, thì không có chuyển động nào xảy ra).

Sử dụng dòng chi phí tối thiểu để giải quyết vấn đề đơn giản này tất nhiên là sử dụng búa tạ để crack đai ốc, nhưng một số biến thể có thể được mô hình hóa dưới dạng sự cố dòng mạng.

  • Nếu một cặp nút i, j không được kết nối trực tiếp, tháo một i → b j arc.

  • Chúng tôi có thể khởi động và tắt các nút bằng cách chỉ cho chúng các đỉnh ở cạnh "a" hoặc cạnh "b".

  • Chúng tôi có thể mô hình hóa sự khác biệt về tốc độ truyền thông bằng cách điều chỉnh chi phí từ đồng phục.

  • Chúng tôi có thể giới hạn số lượng các mục mà hai nút trao đổi bằng cách giới hạn dung lượng vòng cung của chúng.

  • Chúng tôi có thể giới thiệu thêm các đỉnh nội thất và thay đổi cấu trúc liên kết hai bên hoàn chỉnh của các kết nối để mô hình hóa các tác động của tranh chấp mạng.

1

Có vẻ như bạn muốn sử dụng phù hợp băm

http://en.wikipedia.org/wiki/Consistent_hashing

Về cơ bản sử dụng một hàm băm ngẫu nhiên tốt sẽ cho phép bạn để có được id duy nhất cho mẫu của bạn mà đưa ra một phân phối chẵn. Sau đó, dễ dàng phân phối các mẫu trên một tập hợp các nút một cách nhất quán.

Xem

http://www.lexemetech.com/2007/11/consistent-hashing.html http://www.tomkleinpeter.com/2008/03/17/programmers-toolbox-part-3-consistent-hashing/

để biết thêm thông tin.

+0

Cảm ơn câu trả lời. Tôi không có kế hoạch thêm hoặc xóa các nút tính toán, tôi có nên sử dụng tính năng này không? Tôi không chính xác xem cách giải quyết vấn đề tôi đã xây dựng. – Gus

+0

Băm đồng nhất cũng chỉ là một dạng đặc biệt của bảng băm phân tán. Về mặt kỹ thuật nếu bạn không bao giờ thêm hoặc xóa các nút, bạn có thể làm điều gì đó đơn giản hơn một chút.Thường thì mọi người chỉ đơn giản là băm id của dữ liệu sau đó mod bằng số nút để xác định nút nào dữ liệu thuộc về. – nickmbailey

+0

Tôi muốn giảm thiểu giao tiếp. Nếu một nút có các mục dư thừa và một nút có đủ chỗ trống, tôi muốn di chuyển tất cả các mục thừa từ nút này sang nút khác. Tôi không cần biết nút nào chứa một số mục miễn là một số nút _does_. – Gus

0

Về cơ bản bạn muốn đi từ

[9, 6, 1, 6, 2] 

để

[5, 5, 4, 5, 5] 

Tôi nghĩ rằng cách tốt nhất để làm điều này là để tính toán sàn (sum (mảng)/len (mảng)), và sau đó đánh giá sự khác biệt cần thiết để đến vị trí này. Trong trường hợp này, tầng ((9 + 6 + 1 + 6 + 2)/5) = 4, do đó chúng tôi đang xem xét sự khác biệt ban đầu của [-5, -2, +3, -2, +2]. Sau đó bạn có thể trao đổi tham lam các giá trị trong các cặp liền kề với các dấu hiệu khác nhau (chẳng hạn như truyền 2 từ mảng [2] -> arr [1] và 2 từ mảng [4] -> mảng [3]). Sau đó, bạn còn lại với [-5,0,1,0,0] và từ đây bạn có thể tham lam chỉ định các bit còn lại.

+0

Tôi thực sự đang tìm kiếm một cái tên. Thuật toán bạn trình bày là trực quan, nhưng tôi muốn một cái gì đó giảm thiểu hoặc đến gần để giảm thiểu các hoán đổi. – Gus

0

Tôi nghĩ vấn đề phân phối lại này hơi khác so với tải cân bằng trong máy tính.

Thuật toán thuật toán cân bằng tải thường có nghĩa là tập hợp các chính sách/chẩn đoán để đảm bảo phân phối tương đối ngay cả tải FUTURE (và không phải hiện tại).

Trong trường hợp này, bộ cân bằng tải sẽ không phân phối lại tải từ máy chủ/hệ thống hiện có, nhưng bất kỳ yêu cầu mới đến, nó sẽ cố gắng chỉ định sử dụng một số chính sách (ví dụ: nạp ít nhất, roundrobin, v.v.) giữ cho máy chủ tương đối đồng đều được tải trong thời gian dài.

http://en.wikipedia.org/wiki/Load_balancing_(computing)

Các phân phối lại tải trong câu hỏi này, có lẽ có thể được thực hiện bằng cách di chuyển các mục từ tối đa nạp vào hộp nạp tối thiểu, lặp đi lặp lại.

+0

Tôi do dự gọi nó là cân bằng tải quá, nhưng tôi nghĩ khái niệm có liên quan, hoặc ít nhất đó là từ truy vấn duy nhất tôi có trong tâm trí của tôi ngay bây giờ. Bạn đã phân biệt về tải "tương lai" so với tải "hiện tại". Sau khi lấy lại mẫu, một máy chủ có thể có số lượng hạt gấp đôi số còn lại. Việc tính toán được thực hiện trên các hạt này có thể được coi là một tải "tương lai", mà bạn có thể cân bằng bằng cách phân phối các tập mẫu * hiện tại *. – Gus

3

Tôi không tin rằng vấn đề của bạn (như đã nêu) đủ phức tạp để thu hút nghiên cứu độc lập. Nếu số máy (gọi là số C) là hàng nghìn, và số lượng mẫu của bạn thậm chí là hàng nghìn tỷ, thì việc gửi mẫu đếm đến nút điều phối chính là không đáng kể.

Sau đó, chủ có thể tầm thường (O(C)) tính N, xác định các nút vi phạm giới hạn này và số lượng nút. Lưu ý rằng tổng số tiền vượt quá giới hạn là chính xác số lượng giao tiếp tối thiểu cần thiết. Bằng cách chèn thông số slack khi tính toán N (tức là bằng cách chấp nhận mất cân bằng), bạn có thể kiểm soát số lượng này.

Sử dụng sắp xếp, thứ tự các nút theo số lượng mẫu có thể có trong O(C log C). Đi hai con trỏ, một từ hai đầu, về phía giữa. Tại mỗi bước, lập lịch chuyển từ nút lớn sang nút nhỏ, có kích thước ở mức tối thiểu của phần dư còn lại trong phần lớn, hoặc còn lại trong phần nhỏ. Nâng cao con trỏ của bất kỳ nút nào có ràng buộc hoạt động trong câu cuối cùng và lặp lại cho đến khi lớn mới không có dư thừa. (Tôi tin rằng đây là thuật toán tham lam @Noxville đã nhận được tại.)

Giả sử N lớn hơn số lượng trung bình của các mẫu trên mỗi nút, điều này là tầm thường để thấy điều này là lý tưởng. giao tiếp tối thiểu.

Nếu mạng máy của bạn có bất kỳ ràng buộc , hoặc là các liên kết vắng mặt hoặc dòng tối đa hoặc chi phí biến đổi trên một cạnh thì bạn cần phải thoát ra khỏi luồng đồ thị. Tuy nhiên bạn đã không đề cập đến bất cứ điều gì như thế này, và các cụm máy tính trong cùng một trung tâm dữ liệu thường không có.

1

Tôi nghĩ câu hỏi cần được làm rõ theo cách này:

Với M nút thặng dư và thâm hụt K nút chúng ta nên phân phối mẫu giữa các nút với nhà nước với 0 nút thặng dư và (có thể) một số nút thâm hụt. Mẫu nên được trao đổi trong các gói và số lượng các gói này nên được giảm thiểu.

Hoặc, về mặt toán học, chúng tôi có M*K ma trận, mỗi tế bào của nó đại diện cho số lượng mẫu để vượt qua từ nút M đến nút K, với tổng cho các phần tử trong mỗi hàng và tối đa nhất định của tổng yếu tố trong mỗi cột. Mục đích là để giảm thiểu số lượng các ô không đông.

Đây là một loại "Constraint satisfaction problems". Nó là NP-hoàn thành. Tôi tìm thấy hai vấn đề cổ điển tương tự như câu hỏi này: "Đặt bao bì" và "bìa chính xác tổng quát".

Để giảm sự cố xuống "Set packing", chúng tôi cần thêm (tạm thời) một số nút thừa với N+1 mẫu mỗi, sao cho sau khi phân phối lại không còn thiếu nút. Sau đó, đối với mỗi nút, chúng ta cần tạo tất cả các phân vùng có thể cho các phần tử dư thừa và cho các phần tử "thâm hụt". Sau đó, để các phân vùng thặng dư và thâm hụt Cartesian product áp dụng "Đặt gói" trong phiên bản "tối ưu hóa", tìm thấy số lượng tối thiểu các tập hợp con.

Để giảm sự cố xuống "Generalized exact cover", đối với mỗi nút, chúng tôi cần tạo tất cả các phân đoạn có thể cho các phần tử dư thừa và cho các phần tử "thiếu hụt". Sau đó, chúng ta nên thêm M, M+1, ... các nút tối ưu hóa để giảm thiểu số lượng tập con trong trang bìa. Sau đó, sản phẩm Descartes của các phân vùng dư thừa và thâm hụt phân vùng và các nút tối ưu hóa áp dụng "Tổng quát chính xác bao gồm". Đối với số lượng nút tối ưu hóa nhỏ hơn, thuật toán này sẽ không thành công, đối với một số lớn hơn, nó sẽ tìm số lượng tối thiểu các tập con.

"Bìa chính xác tổng quát" có thể được giải quyết bằng "Knuth's Algorithm X". Tôi không biết bất kỳ thuật toán nào cho "Đặt gói".

Tất cả các giải pháp này đưa ra câu trả lời chính xác, nhưng chúng có độ phức tạp tính toán rất lớn, rất ít khả năng ai đó sử dụng chúng trong lịch trình thực. Cách tiếp cận thực tế là sử dụng một số thuật toán và thuật toán tham lam. Chỉ cần sắp xếp cả hai nút dư thừa và các nút thâm hụt và áp dụng chiến lược "phù hợp nhất".

1

Tôi thường chỉ thấy điều này được gọi là phân phối lại dữ liệu, với sự hiểu biết là nếu bạn phân phối lại nó, bạn muốn phân phối tối ưu theo một số chỉ số, như độ đều giữa các tác vụ.

Điều này xảy ra trong tính toán khoa học/kỹ thuật khi bạn đang cố gắng thực hiện cân bằng tải tính toán. Ngay cả khi bạn đang tính toán trong một số thứ nguyên, nếu bạn đang phân phối lại dữ liệu không gian mà bạn gán cho bộ vi xử lý bằng đường cong đầy không gian, vấn đề chính xác này xuất hiện và bạn thường muốn dữ liệu được chia đều.

Quy trình này khá đơn giản; bạn bắt đầu bằng cách lấy một độc quyền prefix sum của x i để bạn biết có bao nhiêu mục là "trái" của bạn. Ví dụ, ví dụ Noxville của trên, nếu bạn có dữ liệu

[9, 6, 1, 6, 2] 

số tiền prefix sẽ

[0, 9, 15, 16, 22] 

và bạn muốn tìm (từ tổng bộ vi xử lý mới nhất của cộng có bao nhiêu nó có) mà có Tổng cộng 24 mục.

Sau đó, bạn tìm ra phân vùng lý tưởng của bạn lớn đến mức nào - giả sử, ceil (totitems/nprocs). Bạn có thể làm điều này tuy nhiên bạn thích miễn là mọi bộ vi xử lý sẽ đồng ý về những gì tất cả các kích thước phân vùng sẽ được.

Bây giờ, bạn có một số cách để tiếp tục. Nếu các mục dữ liệu lớn theo một nghĩa nào đó và bạn không thể có hai bản sao của chúng trong bộ nhớ, thì bạn có thể bắt đầu chuyển dữ liệu sang các nước láng giềng gần nhất. Bạn biết số lượng các mục bên trái của bạn và "dư thừa" hoặc "thâm hụt" theo hướng đó; và bạn cũng biết có bao nhiêu bạn có (và sẽ có sau khi bạn đã thực hiện một phần của bạn để sửa chữa dư thừa hoặc thâm hụt). Vì vậy, bạn bắt đầu gửi dữ liệu đến hàng xóm bên trái và bên phải của bạn, và nhận dữ liệu từ hàng xóm bên trái và bên phải của bạn, cho đến khi các bộ vi xử lý để lại có số lượng mục phù hợp và bạn cũng làm như vậy.

Nhưng nếu bạn có đủ khả năng để có hai bản sao của dữ liệu, thì bạn có thể thực hiện một cách tiếp cận khác giúp giảm thiểu số lượng thư được gửi. Bạn có thể nghĩ số lượng ô ở bên trái của bạn là chỉ mục bắt đầu của dữ liệu cục bộ của bạn vào mảng "toàn cục". Vì bạn biết có bao nhiêu mục mà mỗi bộ xử lý sẽ kết thúc, bạn có thể tìm ra trực tiếp quy trình nào sẽ kết thúc và có thể gửi chúng trực tiếp. (Ví dụ, trong ví dụ trên, procesor 0 - có các mục 0..8 - biết rằng nếu mỗi bộ xử lý nhưng cuối cùng sẽ kết thúc với 5 mục dữ liệu thì giá trị 5-8 có thể được gửi tới bộ xử lý 1.) Một khi chúng được gửi đi, bạn chỉ đơn giản nhận được cho đến khi bạn có lượng dữ liệu bạn đang mong đợi; và bạn đã hoàn tất.

Dưới đây là một ví dụ đơn giản về việc thực hiện điều này trong C và MPI, nhưng cách tiếp cận cơ bản sẽ hoạt động khá nhiều ở mọi nơi. Hoạt động quét tiền tố của MPI tạo ra các khoản tiền bao gồm, vì vậy chúng tôi phải trừ số giá trị của riêng mình để có được số tiền độc quyền:

#include <stdio.h> 
#include <stdlib.h> 
#include <mpi.h> 
#include <time.h> 

void initdata(const int rank, const int maxvals, char **data, int *nvals) { 
    time_t t; 
    unsigned seed; 

    t = time(NULL); 
    seed = (unsigned)(t * (rank + 1)); 

    srand(seed); 
    *nvals = (rand() % (maxvals-1)) + 1; 
    *data = malloc((*nvals+1) * sizeof(char)); 

    for (int i=0; i<*nvals; i++) { 
     (*data)[i] = 'A' + (rank % 26); 
    } 
    (*data)[*nvals] = '\0'; 
} 

int assignrank(const int globalid, const int totvals, const int size) { 
    int nvalsperrank = (totvals + size - 1)/size; 
    return (globalid/nvalsperrank); 
} 

void redistribute(char **data, const int totvals, const int curvals, const int globalstart, 
        const int rank, const int size, int *newnvals) { 

    const int stag = 1; 
    int nvalsperrank = (totvals + size - 1)/size; 

    *newnvals = nvalsperrank; 
    if (rank == size-1) *newnvals = totvals - (size-1)*nvalsperrank; 

    char *newdata = malloc((*newnvals+1) * sizeof(char)); 
    newdata[(*newnvals)] = '\0'; 

    MPI_Request requests[curvals]; 

    int nmsgs=0; 

    /* figure out whose data we have, redistribute it */ 
    int start=0; 
    int newrank = assignrank(globalstart, totvals, size); 
    for (int val=1; val<curvals; val++) { 
     int nextrank = assignrank(globalstart+val, totvals, size); 
     if (nextrank != newrank) { 
      MPI_Isend(&((*data)[start]), (val-1)-start+1, MPI_CHAR, newrank, stag, MPI_COMM_WORLD, &(requests[nmsgs])); 
      nmsgs++; 
      start = val; 
      newrank = nextrank; 
     } 
    } 
    MPI_Isend(&((*data)[start]), curvals-start, MPI_CHAR, newrank, stag, MPI_COMM_WORLD, &(requests[nmsgs])); 
    nmsgs++; 

    /* now receive all of our data */ 
    int newvalssofar= 0; 
    int count; 
    MPI_Status status; 
    while (newvalssofar != *newnvals) { 
     MPI_Recv(&(newdata[newvalssofar]), *newnvals - newvalssofar, MPI_CHAR, MPI_ANY_SOURCE, stag, MPI_COMM_WORLD, &status); 
     MPI_Get_count(&status, MPI_CHAR, &count); 
     newvalssofar += count; 
    } 

    /* wait until all of our sends have been received */ 
    MPI_Status statuses[curvals]; 
    MPI_Waitall(nmsgs, requests, statuses); 

    /* now we can get rid of data and relace it with newdata */ 
    free(*data); 
    *data = newdata; 
} 

int main(int argc, char **argv) { 
    const int maxvals=30; 
    int size, rank; 
    char *data; 
    int mycurnvals, mylvals, myfinalnvals; 
    int totvals; 

    MPI_Init(&argc, &argv); 
    MPI_Comm_size(MPI_COMM_WORLD, &size); 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 

    initdata(rank, maxvals, &data, &mycurnvals); 

    MPI_Scan(&mycurnvals, &mylvals, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); 
    if (rank == size-1) totvals = mylvals; 
    mylvals -= mycurnvals; 

    MPI_Bcast(&totvals, 1, MPI_INT, size-1, MPI_COMM_WORLD); 

    printf("%3d  : %s %d\n", rank, data, mylvals); 

    redistribute(&data, totvals, mycurnvals, mylvals, rank, size, &myfinalnvals); 


    printf("%3d after: %s\n", rank, data); 

    free(data); 
    MPI_Finalize(); 
    return 0; 
} 

Chạy điều này. lưu ý rằng cách tôi đã xác định phân vùng "mong muốn" (sử dụng ceil (totvals/nprocesses)) bộ xử lý cuối cùng nói chung sẽ được tải dưới. Ngoài ra, tôi đã không thực hiện bất kỳ nỗ lực nào để đảm bảo trật tự được giữ lại trong phân phối lại (mặc dù điều đó đủ dễ dàng để thực hiện nếu đơn hàng quan trọng):

$ mpirun -np 13 ./distribute 
    0  : AAAAAAAAAAA 0 
    1  : BBBBBBBBBBBB 11 
    2  : CCCCCCCCCCCCCCCCCCCCCCCCCC 23 
    3  : DDDDDDD 49 
    4  : EEEEEEEEE 56 
    5  : FFFFFFFFFFFFFFFFFF 65 
    6  : G 83 
    7  : HHHHHHH 84 
    8  : IIIIIIIIIIIIIIIIIIIII 91 
    9  : JJJJJJJJJJJJJJJJJJ 112 
10  : KKKKKKKKKKKKKKKKKKKK 130 
11  : LLLLLLLLLLLLLLLLLLLLLLLLLLLL 150 
12  : MMMMMMMMMMMMMMMMMM 178 

    0 after: AAAAAAAAAAABBBBB 
    1 after: BBBBBBBCCCCCCCCC 
    2 after: CCCCCCCCCCCCCCCC 
    3 after: DDDDDDDCEEEEEEEE 
    4 after: EFFFFFFFFFFFFFFF 
    5 after: FFFHHHHHHHIIIIIG 
    6 after: IIIIIIIIIIIIIIII 
    7 after: JJJJJJJJJJJJJJJJ 
    8 after: JJKKKKKKKKKKKKKK 
    9 after: LLLLLLLLLLKKKKKK 
10 after: LLLLLLLLLLLLLLLL 
11 after: LLMMMMMMMMMMMMMM 
12 after: MMMM 
Các vấn đề liên quan