Loại câu hỏi này được lặp lại và cần được trả lời rõ ràng hơn "MATLAB sử dụng các thư viện được tối ưu hóa cao" hoặc "MATLAB sử dụng MKL" một lần trên Stack Overflow.
Lịch sử:
Phép nhân ma trận (cùng với phép nhân vectơ, phép nhân vectơ và nhiều phép phân tách ma trận) là (những) vấn đề quan trọng nhất trong đại số tuyến tính. Các kỹ sư đã giải quyết những vấn đề này với máy tính từ những ngày đầu.
Tôi không phải là một chuyên gia về lịch sử, nhưng rõ ràng hồi đó, mọi người chỉ viết lại phiên bản FORTRAN của mình bằng các vòng lặp đơn giản. Một số tiêu chuẩn hóa sau đó đã xuất hiện, với việc xác định "hạt nhân" (các thói quen cơ bản) mà hầu hết các vấn đề đại số tuyến tính cần thiết để được giải quyết. Các hoạt động cơ bản này sau đó đã được chuẩn hóa trong một đặc tả gọi là: Chương trình con đại số tuyến tính cơ bản (BLAS). Các kỹ sư sau đó có thể gọi các thường trình BLAS được kiểm tra tốt này trong mã của họ, giúp công việc của họ dễ dàng hơn nhiều.
BLAS:
BLAS đã phát triển từ cấp 1 (phiên bản đầu tiên xác định các phép toán vectơ vô hướng và vectơ vectơ) sang cấp 2 (hoạt động ma trận vectơ) đến cấp 3 (hoạt động ma trận ma trận) và cung cấp ngày càng nhiều "hạt nhân" được chuẩn hóa hơn và nhiều hơn các hoạt động đại số tuyến tính cơ bản. Các triển khai FORTRAN 77 ban đầu vẫn có sẵn trên trang web của Netlib .
Hướng tới hiệu suất tốt hơn:
Vì vậy, qua nhiều năm (đáng chú ý là giữa các bản phát hành BLAS cấp 1 và cấp 2: đầu thập niên 80), phần cứng đã thay đổi, với sự ra đời của các hoạt động véc tơ và phân cấp bộ đệm. Những phát triển này đã làm tăng đáng kể hiệu năng của các chương trình con BLAS. Các nhà cung cấp khác nhau sau đó đã đi kèm với việc thực hiện các thói quen BLAS ngày càng hiệu quả hơn.
Tôi không biết tất cả các triển khai lịch sử (lúc đó tôi chưa sinh ra hoặc là một đứa trẻ), nhưng hai trong số những điều đáng chú ý nhất đã xuất hiện vào đầu những năm 2000: Intel MKL và GotoBLAS. Matlab của bạn sử dụng Intel MKL, đây là một BLAS rất tốt, được tối ưu hóa và điều đó giải thích hiệu suất tuyệt vời mà bạn thấy.
Chi tiết kỹ thuật về nhân ma trận:
Vậy tại sao Matlab (MKL) lại nhanh như vậy dgemm
(phép nhân ma trận tổng quát ma trận tổng hợp chính xác kép)? Nói một cách đơn giản: bởi vì nó sử dụng vector hóa và lưu trữ dữ liệu tốt. Trong các điều khoản phức tạp hơn: xem bài viết được cung cấp bởi Jonathan Moore.
Về cơ bản, khi bạn thực hiện phép nhân của mình trong mã C ++ mà bạn cung cấp, bạn hoàn toàn không thân thiện với bộ đệm. Vì tôi nghi ngờ bạn đã tạo một mảng các con trỏ tới các mảng hàng, nên các truy cập của bạn trong vòng lặp bên trong của bạn đến cột thứ k của "matice2": matice2[m][k]
rất chậm. Thật vậy, khi bạn truy cập matice2[0][k]
, bạn phải lấy phần tử thứ k của mảng 0 trong ma trận của bạn. Sau đó, trong lần lặp tiếp theo, bạn phải truy cập matice2[1][k]
, đó là phần tử thứ k của mảng khác (mảng 1). Sau đó, trong lần lặp lại tiếp theo, bạn truy cập vào một mảng khác, v.v ... Vì toàn bộ ma trận matice2
không thể phù hợp với bộ nhớ cache cao nhất (nó8*1024*1024
byte lớn), chương trình phải lấy phần tử mong muốn từ bộ nhớ chính, mất rất nhiều thời gian.
Nếu bạn chỉ chuyển đổi ma trận, để các truy cập sẽ ở trong các địa chỉ bộ nhớ liền kề, mã của bạn sẽ chạy nhanh hơn nhiều vì giờ đây trình biên dịch có thể tải toàn bộ các hàng trong bộ đệm cùng một lúc. Chỉ cần thử phiên bản sửa đổi này:
timer.start();
float temp = 0;
//transpose matice2
for (int p = 0; p < rozmer; p++)
{
for (int q = 0; q < rozmer; q++)
{
tempmat[p][q] = matice2[q][p];
}
}
for(int j = 0; j < rozmer; j++)
{
for (int k = 0; k < rozmer; k++)
{
temp = 0;
for (int m = 0; m < rozmer; m++)
{
temp = temp + matice1[j][m] * tempmat[k][m];
}
matice3[j][k] = temp;
}
}
timer.stop();
Vì vậy, bạn có thể thấy làm thế nào chỉ bộ nhớ cache cục bộ tăng hiệu suất mã của bạn khá đáng kể. Bây giờ là thậtdgemm
triển khai thực khai thác đến mức rất rộng: Chúng thực hiện phép nhân trên các khối của ma trận được xác định bởi kích thước của TLB (Bộ đệm dịch thuật, câu chuyện dài: những gì có thể được lưu trữ hiệu quả), để chúng truyền đến bộ xử lý chính xác lượng dữ liệu nó có thể xử lý. Khía cạnh khác là vector hóa, họ sử dụng các hướng dẫn được vector hóa của bộ xử lý cho thông lượng hướng dẫn tối ưu, điều mà bạn thực sự không thể làm được từ mã C ++ đa nền tảng của mình.
Cuối cùng, mọi người cho rằng đó là do thuật toán của Strassen hay Coppersmith Nhận Win giác là sai, cả hai thuật toán này đều không thể thực hiện được trong thực tế, vì những cân nhắc về phần cứng đã đề cập ở trên.