Bạn có thể lấy ý tưởng bằng cách chạy các phiên bản mã khác của mình. Cân nhắc viết ra các phép tính một cách rõ ràng, thay vì sử dụng một hàm trong vòng lặp của bạn
tic
Soln3 = ones(T, N);
for t = 1:T
for n = 1:N
Soln3(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
Đã đến lúc tính toán trên máy tính của tôi:
Soln1 1.158446 seconds.
Soln2 10.392475 seconds.
Soln3 0.239023 seconds.
Oli 0.010672 seconds.
Bây giờ, trong khi giải pháp 'vectơ hóa đầy đủ' rõ ràng là nhanh nhất, bạn có thể thấy rằng việc xác định một hàm được gọi cho mọi mục nhập x là một chi phí lớn . Chỉ cần viết ra tính toán một cách rõ ràng đã giúp chúng tôi tăng tốc hệ số 5. Tôi đoán điều này cho thấy rằng trình biên dịch MATLABs JIT không hỗ trợ các hàm nội tuyến . Theo câu trả lời của gnovice ở đó, thực sự tốt hơn nên viết một hàm bình thường hơn là một hàm ẩn danh. Thử nó.
Bước tiếp theo - loại bỏ (vectơ hóa) vòng lặp bên trong:
tic
Soln4 = ones(T, N);
for t = 1:T
Soln4(t, :) = 3*x(t, :).^2 + 2*x(t, :) - 1;
end
toc
Soln4 0.053926 seconds.
Một yếu tố thứ 5 khác là tăng tốc: có điều gì đó trong những câu nói đó nói rằng bạn nên tránh các vòng lặp trong MATLAB ... Hay là có thực sự? Hãy xem cái này sau đó
tic
Soln5 = ones(T, N);
for n = 1:N
Soln5(:, n) = 3*x(:, n).^2 + 2*x(:, n) - 1;
end
toc
Soln5 0.013875 seconds.
Gần hơn nhiều so với phiên bản vector hóa 'đầy đủ'. Matlab lưu trữ ma trận theo cột. Bạn nên luôn (khi có thể) cấu trúc các tính toán của mình thành vectơ hóa 'cột khôn ngoan'.
Chúng ta có thể quay lại Soln3 ngay bây giờ. Thứ tự vòng lặp ở đó là 'hàng khôn ngoan'. Hãy thay đổi nó
tic
Soln6 = ones(T, N);
for n = 1:N
for t = 1:T
Soln6(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
Soln6 0.201661 seconds.
Tốt hơn, nhưng vẫn rất tệ. Vòng lặp đơn - tốt. Vòng lặp kép - xấu. Tôi đoán MATLAB đã làm một số công việc tốt trong việc cải thiện hiệu suất của các vòng lặp, nhưng chi phí vòng lặp vẫn ở đó. Nếu bạn có một số công việc nặng hơn bên trong, bạn sẽ không nhận thấy. Nhưng vì tính toán này bị giới hạn băng thông bộ nhớ, bạn sẽ thấy chi phí vòng lặp. Và bạn thậm chí sẽ thấy rõ hơn chi phí của việc gọi Func1 ở đó.
Vậy có vấn đề gì với arrayfun? Không có chức năng nào ở đó, vì vậy rất nhiều chi phí. Nhưng tại sao lại tệ hơn nhiều so với vòng lặp lồng nhau? Trên thực tế, chủ đề sử dụng cellfun / arrayfun đã được thảo luận rộng rãi nhiều lần (ví dụ: đây , đây , đây và đây ). Các chức năng này chỉ đơn giản là chậm, bạn không thể sử dụng chúng cho các tính toán chi tiết như vậy. Bạn có thể sử dụng chúng để viết mã ngắn gọn và chuyển đổi lạ mắt giữa các ô và mảng. Nhưng hàm cần nặng hơn những gì bạn đã viết:
tic
Soln7 = arrayfun(@(a)(3*x(:,a).^2 + 2*x(:,a) - 1), 1:N, 'UniformOutput', false);
toc
Soln7 0.016786 seconds.
Lưu ý rằng Soln7 là một tế bào bây giờ .. đôi khi điều đó hữu ích. Hiệu suất mã hiện khá tốt và nếu bạn cần ô làm đầu ra, bạn không cần phải chuyển đổi ma trận của mình sau khi bạn đã sử dụng giải pháp vector hóa hoàn toàn.
Vậy tại sao arrayfun lại chậm hơn cấu trúc vòng lặp đơn giản? Thật không may, chúng tôi không thể nói chắc chắn, vì không có mã nguồn nào. Bạn chỉ có thể đoán rằng vì arrayfun là một hàm mục đích chung, xử lý tất cả các loại cấu trúc dữ liệu và đối số khác nhau, nó không nhất thiết phải rất nhanh trong các trường hợp đơn giản, mà bạn có thể trực tiếp biểu thị dưới dạng tổ vòng lặp. Chúng tôi không thể biết chi phí đến từ đâu. Có thể tránh được chi phí bằng cách triển khai tốt hơn không? Có thể không. Nhưng không may, điều duy nhất chúng ta có thể làm là nghiên cứu hiệu suất để xác định các trường hợp, trường hợp nào hoạt động tốt và trường hợp nào không.
Cập nhật Vì thời gian thực hiện bài kiểm tra này ngắn, để có được kết quả đáng tin cậy, tôi đã thêm một vòng lặp xung quanh các bài kiểm tra:
for i=1:1000
% compute
end
Một số lần đưa ra dưới đây:
Soln5 8.192912 seconds.
Soln7 13.419675 seconds.
Oli 8.089113 seconds.
Bạn thấy rằng arrayfun vẫn tệ, nhưng ít nhất không kém hơn ba bậc độ lớn so với giải pháp vector hóa. Mặt khác, một vòng lặp đơn với các phép tính theo cột cũng nhanh như phiên bản được vector hóa hoàn toàn ... Tất cả đều được thực hiện trên một CPU duy nhất. Kết quả cho Soln5 và Soln7 không thay đổi nếu tôi chuyển sang 2 lõi - Trong Soln5, tôi sẽ phải sử dụng một parfor để ghép nó song song. Quên về tăng tốc độ ... Soln7 không chạy song song vì arrayfun không chạy song song. Mặt khác, phiên bản vector hóa của Olis:
Oli 5.508085 seconds.