2011-10-12 40 views
6

Tôi phải tìm ra sự khác biệt chéo trong một ma trận biểu diễn dưới dạng mảng 2ngày và nguyên mẫu chức năng làCải thiện hiệu suất hoạt động của C với vị trí bộ nhớ cache?

int diagonal_diff(int x[512][512]) 

tôi phải sử dụng một mảng 2d, và các dữ liệu là 512x512. Điều này được thử nghiệm trên một máy SPARC: thời gian hiện tại của tôi là 6ms nhưng tôi cần phải dưới 2ms.

dữ liệu

mẫu:

[3][4][5][9] 
[2][8][9][4] 
[6][9][7][3] 
[5][8][8][2] 

Sự khác biệt là:

|4-2| + |5-6| + |9-5| + |9-9| + |4-8| + |3-8| = 2 + 1 + 4 + 0 + 4 + 5 = 16 

Để làm được điều đó, tôi sử dụng các thuật toán sau đây:

int i,j,result=0; 
for(i=0; i<4; i++) 
    for(j=0; j<4; j++) 
     result+=abs(array[i][j]-[j][i]); 

return result; 

Nhưng thuật toán này giúp truy cập cột, hàng, cột, hàng, v.v ... làm cho việc sử dụng bộ đệm không hiệu quả.

Có cách nào để cải thiện chức năng của tôi không?

+3

Bạn có chuẩn hoặc hồ sơ này? Ma trận thực sự lớn đến mức nào? Bất kỳ ma trận 4 x 4 nào cũng phù hợp với bộ nhớ đệm và không liên quan đến thứ tự bạn truy cập vào các mục. –

+0

Thậm chí nếu bạn thực hiện điều này 50.000.000 lần mỗi giây, thậm chí ngay cả một CPU hiện đại cấp thấp cũng sẽ không đổ mồ hôi. Ngay cả hàm gọi hàm 'abs()' sẽ được tối ưu hóa như bản chất của hầu hết các trình biên dịch (bao gồm GCC và VC++.) –

+0

kích thước của mảng là 512x512 và tôi phải sử dụng một mảng 2D. thông số kỹ thuật giao diện được cố định, tôi chỉ phải điền vào implementations.int diagonal_diff (int x [512] [512], int y [512] [512]) –

Trả lời

7

EDIT: Tại sao phương pháp tiếp cận theo khối được định hướng nhanh hơn? Chúng tôi đang tận dụng bộ nhớ cache dữ liệu của CPU bằng cách đảm bảo rằng chúng ta lặp qua một khối theo hàng hoặc theo cột, chúng tôi đảm bảo rằng toàn bộ khối khớp với bộ nhớ cache.

Ví dụ: nếu bạn có một dòng bộ nhớ cache 32 byte và int là 4 byte, bạn có thể phù hợp với ma trận 8x8 int thành 8 dòng bộ nhớ cache. Giả sử bạn có một bộ nhớ cache dữ liệu đủ lớn, bạn có thể lặp qua ma trận đó theo hàng hoặc theo cột và được đảm bảo rằng bạn không thrash cache. Một cách khác để suy nghĩ về nó là nếu ma trận của bạn phù hợp trong bộ nhớ cache, bạn có thể đi qua nó bất kỳ cách nào bạn muốn.

Nếu bạn có ma trận lớn hơn nhiều, hãy nói 512x512, thì bạn cần điều chỉnh quá trình truyền tải ma trận sao cho bạn không làm hỏng bộ nhớ cache. Ví dụ, nếu bạn đi qua ma trận theo thứ tự ngược lại của bố trí của ma trận, bạn sẽ hầu như luôn luôn bỏ lỡ bộ nhớ cache trên mọi phần tử bạn truy cập.

Cách tiếp cận theo hướng khối đảm bảo rằng bạn chỉ bị thiếu bộ nhớ cache cho dữ liệu mà cuối cùng bạn sẽ truy cập trước khi CPU phải xóa dòng bộ nhớ cache đó. Nói cách khác, một phương pháp tiếp cận theo định hướng khối được điều chỉnh theo kích thước đường bộ nhớ cache sẽ đảm bảo bạn không làm đổ bộ nhớ cache.

Vì vậy, nếu bạn đang cố gắng để tối ưu hóa cho kích thước dòng bộ nhớ cache của máy bạn đang chạy trên, bạn có thể lặp qua các ma trận ở dạng khối và đảm bảo bạn chỉ ghé thăm mỗi phần tử ma trận một lần:

int sum_diagonal_difference(int array[512][512], int block_size) 
{ 
    int i,j, block_i, block_j,result=0; 

    // sum diagonal blocks 
    for (block_i= 0; block_i<512; block_i+= block_size) 
     for (block_j= block_i + block_size; block_j<512; block_j+= block_size) 
      for(i=0; i<block_size; i++) 
       for(j=0; j<block_size; j++) 
        result+=abs(array[block_i + i][block_j + j]-array[block_j + j][block_i + i]); 

    result+= result; 

    // sum diagonal 
    for (int block_offset= 0; block_offset<512; block_offset+= block_size) 
    { 
     for (i= 0; i<block_size; ++i) 
     { 
      for (j= i+1; j<block_size; ++j) 
      { 
       int value= abs(array[block_offset + i][block_offset + j]-array[block_offset + j][block_offset + i]); 
       result+= value + value; 
      } 
     } 
    } 

    return result; 
} 

Bạn nên thử nghiệm với các giá trị khác nhau cho block_size. Trên máy của tôi, 8 dẫn đến tốc độ lớn nhất lên (2.5x) so với block_size của 1 (và ~ 5x so với phiên bản gốc trên toàn bộ ma trận). block_size lý tưởng là cache_line_size_in_bytes/sizeof(int).

+0

Trên máy tính cụ thể của tôi, điều này chạy nhanh hơn 50% so với phiên bản không biết cache (Blastfurnace's) với 'blocksize = 8', đây là tốc độ nhanh nhất tôi có thể nhận được. Cũng đi vào chỉ dưới nửa mili giây để thực thi. –

+0

phương pháp này hoạt động! cảm ơn rất nhiều ! có rất ít lỗi kết quả, do: kết quả + = kết quả; trong dòng 12 và: kết quả + = giá trị + giá trị; trong dòng 22. Tôi thay đổi nó để sử dụng kết quả duy nhất chứ không phải là gấp đôi (đó là những gì @MSN đã làm) và nó hoạt động hoàn hảo. –

+0

Trong thử nghiệm của tôi, block_size = 16 là nhanh nhất tôi có thể nhận được. Phương pháp này nhanh hơn ~ 80% so với phương pháp gốc. –

0

Với một thay đổi nhỏ, bạn có thể có vòng lặp của bạn chỉ hoạt động trên các chỉ mục mong muốn. Tôi vừa thay đổi khởi tạo vòng lặp j.

int i, j, result = 0; 
for (i = 0; i < 4; ++i) { 
    for (j = i + 1; j < 4; ++j) { 
     result += abs(array[i][j] - array[j][i]); 
    } 
} 
+0

Tôi đã làm điều đó nhưng điều đó không cải thiện nhiều. Tôi cũng cố gắng sao chép cột sang mảng 1-dimenstional khác trước khi so sánh. thời gian khoảng 4ms –

+2

'i! = j' là không cần thiết ở đây. Bởi vì bạn khởi tạo 'j = i + 1', j không bao giờ có thể bằng i. –

+0

@Mike Bantegui, bạn đúng và tôi cảm thấy một chút ngu ngốc. Cảm ơn. – Blastfurnace

3

Nếu bạn có thư viện vector/ma trận tốt như intel MKL, hãy thử cách vectorized.

rất đơn giản trong MATLAB: result = sum (sum (abs (x-x ')));

tôi sao chép phương pháp Hans và phương pháp của MSN trong matlab quá, và kết quả là:

Elapsed time is 0.211480 seconds. (Hans) 

Elapsed time is 0.009172 seconds. (MSN) 

Elapsed time is 0.002193 seconds. (Mine) 
Các vấn đề liên quan