2012-02-28 27 views
5

Tôi gặp một vấn đề hiệu suất lạ trong một ma trận nhân chuẩn (matrix_mult trong Metis từ bộ MOSBENCH). Điểm chuẩn được tối ưu hóa để xếp các dữ liệu sao cho bộ làm việc đang hoạt động là 12kb (3 ô 32x32 ints) và phù hợp với bộ đệm L1. Để làm cho một câu chuyện dài ngắn, trao đổi hai vòng bên trong nhất có một sự khác biệt hiệu suất gần như 4x trên một số kích cỡ đầu vào mảng (4096, 8192) và khoảng 30% sự khác biệt trên những người khác. Vấn đề cơ bản là đi xuống để truy cập các phần tử tuần tự so với trong một mô hình sải chân. Một số kích thước mảng mà tôi nghĩ đã tạo ra một truy cập sải chân xấu tạo ra nhiều xung đột dòng bộ nhớ cache. Sự khác biệt hiệu suất là đáng kể ít hơn khi thay đổi từ 2-way kết hợp L1 đến một 8-way kết hợp L1.trình biên dịch tối ưu hóa vòng lặp lồng nhau để truy cập bộ nhớ tuần tự.

Câu hỏi của tôi là tại sao gcc không tối ưu hóa thứ tự vòng lặp để tối đa hóa truy cập bộ nhớ tuần tự?

Dưới đây là phiên bản đơn giản của sự cố (lưu ý rằng thời gian thực hiện phụ thuộc nhiều vào cấu hình L1. Các số được chỉ ra dưới đây là từ 2.3 GHZ AMD với 64K L1 liên kết 2 chiều được biên dịch với -O3).

N = ARRAY_SIZE // 1024 
int* mat_A = (int*)malloc(N*N*sizeof(int)); 
int* mat_B = (int*)malloc(N*N*sizeof(int)); 
int* mat_C = (int*)malloc(N*N*sizeof(int)); 

// Elements of mat_B are accessed in a stride pattern of length N 
// This takes 800 msec 
for (int t = 0; t < 1000; t++) 
    for (int a = 0; a < 32; a++) 
     for (int b = 0; b < 32; b++) 
     for (int c = 0; c < 32; c++) 
      mat_C[N*a+b] += mat_A[N*a+c] * mat_B[N*c+b]; 

// Inner two loops are swapped 
// Elements are now accessed sequentially in inner loop 
// This takes 172 msec 
for (int t = 0; t < 1000; t++) 
    for (int a = 0; a < 32; a++) 
     for (int c = 0; c < 32; c++) 
     for (int b = 0; b < 32; b++) 
      mat_C[N*a+b] += mat_A[N*a+c] * mat_B[N*c+b]; 
+4

Bởi vì rất khó để chứng minh cho trình biên dịch rằng thay đổi như vậy không làm thay đổi ngữ nghĩa của thao tác. – Sven

+0

có thể bạn thấy nó thú vị với google gcc + graphite, vốn là một nhánh đã được hợp nhất cho các phép biến đổi vòng lặp dựa trên mô hình đa diện. Có một nơi nào đó một danh sách các biến đổi có thể. –

+0

Tôi nghĩ rằng kể từ khi bổ sung số nguyên là giao hoán mà nó sẽ khá đơn giản cho một trình biên dịch để chứng minh rằng các hoạt động là bất biến để đặt hàng looper. Tôi tự hỏi nó là gì sau đó về ví dụ mã mà làm cho nó không tầm thường để tối ưu hóa. – Mark

Trả lời

1
  1. gcc có thể không có khả năng chứng minh rằng các con trỏ không chồng chéo. Nếu bạn sử dụng các tiện ích mở rộng không chuẩn, bạn có thể thử sử dụng __restrict.
  2. gcc không tận dụng tối đa kiến ​​trúc của bạn để tránh sự cần thiết phải biên dịch lại cho mọi bộ xử lý. Sử dụng tùy chọn -march với giá trị phù hợp cho hệ thống của bạn có thể hữu ích.
+0

Cần lưu ý rằng hạn chế _is_ tiêu chuẩn cho C99, không chỉ cho C++. Xem thêm http://stackoverflow.com/questions/776283/what-does-the-restrict-keyword-mean-in-c – torek

+0

@torek Tôi biết, chỉ muốn nói rõ rằng đó là tiện ích không chuẩn nhưng được hỗ trợ bởi nhiều trình biên dịch. – taurdir

0

gcc có một loạt các tối ưu hóa chỉ làm những gì bạn muốn.

Tra cứu các tùy chọn trình biên dịch -floop-strip-mine và -floop-block.

Trích dẫn từ hướng dẫn:

Thực hiện vòng lặp chặn biến đổi trên vòng. Chặn các mỏ dải mỗi vòng lặp trong tổ vòng lặp sao cho các truy cập bộ nhớ của các vòng phần tử vừa với bộ đệm. Chiều dài dải có thể được thay đổi bằng cách sử dụng tham số thông số kích thước khối-khối ngói.

+0

Cảm ơn, mặc dù vấn đề không phải là do các vòng bên trong không phù hợp trong bộ nhớ cache, vì tối ưu hóa đó đã được thực hiện bằng tay trong mã. Các vòng bên trong chỉ có một dấu chân dữ liệu 12kb. 3 * 1024 giá trị int. – Mark

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