2011-01-18 14 views
5

Tôi đang nghiên cứu cho Đại học của mình liên quan đến thuật toán tái tạo hình ảnh để sử dụng y tế.cải thiện địa phương và giảm ô nhiễm bộ nhớ cache trong việc thực hiện tái tạo hình ảnh y tế

tôi bị mắc kẹt trong một cái gì đó lên đến 3 tuần, tôi cần phải cải thiện hiệu suất của đoạn mã sau:

for (lor=lor0[mypid]; lor <= lor1[mypid]; lor++) 
{ 
    LOR_X = P.symmLOR[lor].x; 
    LOR_Y = P.symmLOR[lor].y; 
    LOR_XY = P.symmLOR[lor].xy; 
    lor_z = P.symmLOR[lor].z; 
    LOR_Z_X = P.symmLOR[lor_z].x; 
    LOR_Z_Y = P.symmLOR[lor_z].y; 
    LOR_Z_XY = P.symmLOR[lor_z].xy; 

    s0 = P.a2r[lor]; 
    s1 = P.a2r[lor+1]; 

    for (s=s0; s < s1; s++) 
    { 
    pixel  = P.a2b[s]; 
    v   = P.a2p[s]; 

    b[lor] += v * x[pixel]; 

    p   = P.symm_Xpixel[pixel]; 
    b[LOR_X] += v * x[p]; 

    p   = P.symm_Ypixel[pixel]; 
    b[LOR_Y] += v * x[p]; 

    p   = P.symm_XYpixel[pixel]; 
    b[LOR_XY] += v * x[p]; 


    // do Z symmetry. 
    pixel_z = P.symm_Zpixel[pixel]; 
    b[lor_z] += v * x[pixel_z]; 


    p   = P.symm_Xpixel[pixel_z]; 
    b[LOR_Z_X] += v * x[p]; 


    p   = P.symm_Ypixel[pixel_z]; 
    b[LOR_Z_Y] += v * x[p]; 

    p   = P.symm_XYpixel[pixel_z]; 
    b[LOR_Z_XY] += v * x[p]; 

    } 

} 

cho bất cứ ai muốn biết, mã này thực hiện các chức năng MLEM mong và tất cả các biến là FLOAT.

Sau nhiều lần kiểm tra, tôi đã nhận thấy rằng độ trễ lớn là trên phần mã này. (bạn biết đấy, quy tắc 90 - 10).

Sau đó, tôi đã sử dụng Papi (http://cl.cs.utk.edu/papi/) để đo bộ nhớ cache L1D bị thiếu. Như tôi đã nghĩ, Papi xác nhận rằng hiệu suất giảm do số lượng lỗi cao hơn, đặc biệt đối với truy cập ngẫu nhiên vào b vector (kích thước lớn).

Đọc thông tin trên Internet Tôi chỉ biết hai tùy chọn để cải thiện hiệu suất cho đến nay: cải thiện vị trí dữ liệu hoặc giảm ô nhiễm dữ liệu.

Để thực hiện cải tiến đầu tiên, tôi sẽ cố gắng thay đổi mã để nhận biết về bộ nhớ cache, giống như được đưa lên bởi Ulrich Drepper trên Mọi lập trình viên nên biết gì về bộ nhớ (www.akkadia.org/drepper/ cpumemory.pdf) A.1 Phép nhân ma trận.

Tôi tin rằng việc chặn SpMV (phép nhân vectơ vectơ thưa thớt) sẽ cải thiện hiệu suất.

Mặt khác, mỗi khi chương trình tìm cách truy cập vectơ b, chúng tôi có một ô nhiễm bộ nhớ cache là .

Có cách nào để tải một giá trị từ véc tơ b bằng lệnh SIMD mà không cần sử dụng Cache không?

Ngoài ra, có thể sử dụng hàm như void _mm_stream_ps (float * p, __m128 a) để lưu trữ MỘT giá trị float trên véc tơ b mà không làm ô nhiễm Cache?

Tôi không thể sử dụng _mm_stream_ps vì luôn lưu trữ 4 phao nhưng truy cập vào vectơ b rõ ràng là ngẫu nhiên.

Tôi hy vọng sẽ rõ ràng trong tình trạng khó xử của tôi.

Thông tin thêm: v là giá trị cột của cửa hàng Ma trận Sparse với định dạng CRS. Tôi nhận ra rằng tối ưu hóa khác có thể được thực hiện nếu tôi cố thay đổi định dạng CRS sang định dạng khác, tuy nhiên, như tôi đã nói trước đây, tôi đã thực hiện một vài thử nghiệm trong nhiều tháng và tôi biết rằng hiệu suất giảm có liên quan đến truy cập ngẫu nhiên trên vectơ b. từ 400.000.000 L1D Misses tôi có thể đi đến 100 ~ Misses khi tôi không lưu trữ trong vector b.

Cảm ơn.

+0

+1 cho câu hỏi đúng ngữ pháp với nhiều thông tin cơ bản và chi tiết về những gì bạn đã thử cho đến thời điểm này. –

Trả lời

2

Tôi muốn nói, lúc đầu hãy cố gắng giúp trình biên dịch của bạn một chút.

  • Khai báo các giới hạn cho vòng ngoài là const trước vòng lặp.
  • Khai báo tất cả các biến mà bạn có thể (tất cả các LOR_..) như các biến địa phương, cái gì đó như: float LOR_X = P.symmLOR[lor].x; hoặc size_t s0 = P.a2r[lor];
  • này cũng đặc biệt đối với vòng lặp biến nếu bạn xảy ra để có hiện đại, C99 tuân thủ, trình biên dịch: for (size_t s=s0; s < s1; s++)
  • Tải và lưu trữ riêng biệt cho b vectơ của bạn. Vị trí của các mục mà bạn truy cập, ở đó, không phụ thuộc trên s.Vì vậy, tạo một biến địa phương để giữ giá trị thực tế cho tất cả các trường hợp riêng biệt mà bạn xử lý trước vòng lặp bên trong, cập nhật các biến cục bộ bên trong vòng lặp bên trong, và lưu trữ các kết quả sau khi vòng lặp bên trong .
  • Có thể tách vòng lặp bên trong của bạn theo số một số. Các tính toán chỉ mục tương đối rẻ và sau đó hệ thống của bạn có thể nhận dạng tốt hơn quyền truy cập trực tuyến vào một số vectơ của bạn.
  • Nhìn vào bộ kết hợp mà trình biên dịch của bạn tạo và xác định mã cho vòng lặp bên trong. Thử nghiệm một bit để di chuyển càng nhiều tải "xa" và các cửa hàng ra khỏi vòng lặp đó.

Chỉnh sửa: sau khi đọc lại câu trả lời gravitron và nhận xét của bạn, điều quan trọng ở đây thực sự là để khai báo các biến như địa phương càng tốt để kiểm tra lắp ráp mà trình biên dịch thành công trong việc có tải bộ nhớ cache mất tích và lưu trữ bên ngoài vòng lặp bên trong.

+0

Hey Jens, những thay đổi bạn đề xuất giúp tôi vì họ đã tăng hiệu suất, tuy nhiên, các lần xóa trong Cache không giảm. Tôi không biết mối quan hệ giữa những gợi ý của trình biên dịch và hiệu suất. Có lẽ đó là thời gian để so sánh lắp ráp với objdump, Tôi có phải không? – Fbarisio

+0

Có và không, không sử dụng objdump. Chỉ cần biên dịch trực tiếp với '-S' hoặc tùy chọn tương tự để có được trình biên dịch. Điều này sẽ dễ đọc hơn việc xây dựng lại mà objdump sẽ có thể làm. –

2

Một tối ưu hóa đơn giản để giảm truy cập ngẫu nhiên trên vectơ b sẽ không bao giờ được ghi vào vectơ b bên trong vòng lặp.

Thay vào đó tải tất cả các giá trị cần thiết từ vector B vào biến tạm thời, làm toàn bộ bên trong vòng lặp for trong khi cập nhật các biến tạm thời, sau đó viết các biến tạm thời trở lại vào vector B.

Biến tạm thời sẽ tồi tệ nhất được đặt trên cùng một dòng bộ nhớ cache, tùy thuộc vào trình biên dịch và môi trường của bạn, bạn cũng có thể gợi ý trình biên dịch sử dụng thanh ghi cho các biến này.

+0

Tôi đã thử nhưng không hoạt động, tôi không biết tại sao nhưng nếu tôi thực hiện việc gán bên ngoài bên trong thì tôi đã nhận được gần như cùng một số lượng Cache bị lỗi so với nếu tôi thực hiện phép gán bên trong vòng lặp bên trong. Cảm ơn! – Fbarisio

+0

bạn có thể đăng số lần mà mỗi vòng lặp được chạy trung bình không? nếu vòng lặp bên trong được thực hiện một số lần thấp thì nó sẽ không phải là một khoản tiết kiệm lớn. IE là giá trị trung bình cho s0, s1, lor0 [mypid], lor1 [mypid]. – gravitron

+0

Việc thực hiện trung bình của vòng lặp bên trong là 1136 và 72191 cho vòng lặp ngoài. Tôi muốn cảm ơn bạn đã nói về vấn đề này, tôi đã không nhận ra rằng có 35328 chu kỳ bên trong với s0 bằng với s1. Có thể đó là lý do vì không có khoản tiết kiệm lớn nào làm những gì bạn đề xuất. Đối với 72191 lần chạy vòng ngoài, chỉ 72191 - 35328 = 36863 lần mã thực thi phần bên trong. – Fbarisio

2

tôi thậm chí sẽ không giả vờ rằng tôi biết những gì các mã đang làm :) Nhưng có một nguyên nhân có thể đối với một số truy cập bộ nhớ thêm được aliasing: nếu trình biên dịch không thể chắc chắn rằng b, x, và khác nhau P.symm mảng không trùng lặp, sau đó ghi vào b sẽ ảnh hưởng đến cách đọc từ xP.symm có thể được lên lịch. Nếu trình biên dịch đặc biệt bi quan, nó thậm chí có thể buộc các trường của P được lấy lại từ bộ nhớ. Tất cả điều này sẽ góp phần vào bộ nhớ cache mà bạn đang thấy. Hai cách đơn giản để cải thiện điều này là:

  1. Sử dụng __restrict on b. Điều này đảm bảo rằng b không trùng lặp với các mảng khác, do đó ghi vào nó sẽ không ảnh hưởng đến lần đọc (hoặc ghi) từ các mảng khác.

  2. Sắp xếp lại mọi thứ để tất cả các lần đọc từ P.symm là lần đầu tiên, sau đó đọc từ x, sau đó cuối cùng tất cả ghi vào b. Điều này sẽ phá vỡ một số phụ thuộc trong lần đọc và trình biên dịch lịch trình đọc từ P.symm 's song song, sau đó đọc từ x song song, và hy vọng làm các ghi để b hợp lý.

Một điều phong cách khác (giúp ích cho điểm số 2) là không sử dụng lại các biến số p quá nhiều. Không có lý do gì bạn không thể có, ví dụ: p_x, p_y, p_xy, v.v. và nó sẽ làm cho việc sắp xếp lại mã dễ dàng hơn.

Khi đã sẵn sàng, bạn có thể bắt đầu sprinkling hướng dẫn tìm nạp trước (ví dụ: __builtin_prefetch trên gcc) trước bộ nhớ cache đã biết.

Hy vọng điều đó sẽ hữu ích.

+0

Cảm ơn sự giúp đỡ của bạn! Tôi sẽ thử thay đổi hạn chế và sau đó tôi sẽ đặt kết quả. – Fbarisio

+0

@ user580248 - may mắn nào? – celion

0

Đây là những câu trả lời hay và tôi sẽ hỏi tại sao có quá nhiều chỉ mục? theo giá trị chỉ mục không thay đổi nhiều cục bộ?

Ngoài ra, nó sẽ không giết bạn để làm một vài tạm dừng ngẫu nhiên để xem nó thường ở đâu.

Các vấn đề liên quan