2013-04-06 12 views
5

Giả sử tôi muốn nhân mỗi phần tử của mảng ô A với hệ số k có hệ số. Tôi có thể làm điều đó bằng cách:Cách nhanh nhất để thực hiện các phép tính số học trên mỗi phần tử của một mảng ô là gì?

A = cellfun(@(x) k*x, A, 'UniformOutput', false) 

Nhưng điều này cực kỳ chậm. Có cách nào nhanh hơn và tốt hơn không? Các phần tử mảng ô là các vectơ có chiều dài thay đổi, vì vậy cell2num không áp dụng.

Chỉnh sửa: Dựa trên đề xuất của fpe's đề xuất cho vòng lặp for, đây là điểm chuẩn mẫu. Bắt đầu với dữ liệu này

A = arrayfun(@(n) rand(n,1), randi(5,1000,1000), 'UniformOutput',false); 

Cuộc gọi cellfun trên diễn 9.45 seconds, trong khi một vòng lặp for:

A2 = cell(size(A)); 
for i = 1:size(A,1), for j = 1:size(A,2), A2{i,j} = A{i,j}*k; end; end 
A = A2; 

mất 1.67 seconds, đó là một sự cải thiện đáng kể. Tôi vẫn muốn cái gì đó có một vài đơn đặt hàng của cường độ nhanh hơn. (Tôi cũng không hiểu lý do tại sao trình thông dịch Matlab không thể thực hiện cuộc gọi cellfun nhanh như vòng lặp. Chúng giống hệt nhau về mặt ngữ nghĩa.)

Chỉnh sửa 2: Đề xuất của Amro để tạo thành một vòng lặp là đáng kể nhanh hơn:

for i = 1:numel(A), A{i} = A{i}*k; end 

mất 1.11 seconds, và nếu tôi chạy pack trước khi nó để sắp xếp bộ nhớ chỉ 0.88 seconds.

Thực hiện chức năng MEX để thực hiện điều này thực sự không tốt hơn nhiều: 0.73 seconds, (0.53 seconds sau pack), cho biết phân bổ nhiều ma trận nhỏ chậm trong Matlab.

#include "mex.h" 

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 
    if (nrhs != 2) 
     mexErrMsgTxt("need 2 arguments (Cell, Coefficient)"); 

    mwSize const* size = mxGetDimensions(prhs[0]); 
    int N = mxGetNumberOfDimensions(prhs[0]); 

    if (mxGetNumberOfElements(prhs[1]) != 1) 
     mexErrMsgTxt("second argument to multcell must be a scalar"); 

    double coefficient = *mxGetPr(prhs[1]); 

    plhs[0] = mxCreateCellArray(N, size); 

    int M = mxGetNumberOfElements(prhs[0]); 

    for (int i = 0; i < M; i++) { 
     mxArray *r = mxGetCell(prhs[0], i); 
     mxArray *l = mxCreateNumericArray(mxGetNumberOfDimensions(r), 
              mxGetDimensions(r), 
              mxDOUBLE_CLASS, 
              mxREAL); 
     double *rp = mxGetPr(r); 
     double *lp = mxGetPr(l); 
     int num_elements = mxGetNumberOfElements(r); 
     for (int i = 0; i < num_elements; i++) 
      lp[i] = rp[i] * coefficient; 
     mxSetCell(plhs[0], i, l); 
    } 
} 

Gian lận một chút, tuy nhiên, và thực hiện một chức năng MEX mà thực sự chỉnh sửa bộ nhớ tại chỗ có vẻ là cách duy nhất để có được hiệu suất hợp lý ra các hoạt động: 0.030 seconds. Điều này sử dụng mxUnshareArray không có giấy tờ theo đề xuất của Amro.

#include "mex.h" 

extern "C" bool mxUnshareArray(mxArray *array_ptr, bool noDeepCopy); 

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 
    if (nrhs != 2) 
     mexErrMsgTxt("need 2 arguments (Cell, Coefficient)"); 

    mwSize const* size = mxGetDimensions(prhs[0]); 
    int N = mxGetNumberOfDimensions(prhs[0]); 

    if (mxGetNumberOfElements(prhs[1]) != 1) 
     mexErrMsgTxt("second argument to multcell must be a scalar"); 

    double coefficient = *mxGetPr(prhs[1]); 

    mxUnshareArray(const_cast<mxArray *>(prhs[0]), false); 
    plhs[0] = const_cast<mxArray *>(prhs[0]); 

    int M = mxGetNumberOfElements(prhs[0]); 

    for (int i = 0; i < M; i++) { 
     mxArray *r = mxGetCell(prhs[0], i); 
     double *rp = mxGetPr(r); 
     int num_elements = mxGetNumberOfElements(r); 
     for (int i = 0; i < num_elements; i++) 
      rp[i] = rp[i] * coefficient; 
    } 
} 
+1

trong bản phát hành MATLAB mới nhất, vòng lặp 'for' nói chung là giải pháp dễ nhất và nhanh nhất. – fpe

+0

cảm ơn bạn, vòng lặp for thực sự nhanh hơn đáng kể (mặc dù vẫn rất chậm) – digitalvision

+0

digitalvision: tại sao bạn nói nó cực kỳ chậm? chúng ta đang nói về kích thước nào, và nó hiện đang dùng bao lâu (tic/toc)? Tôi nghi ngờ một hoạt động như vậy là nút cổ chai trong mã của bạn ... Chạy hồ sơ và cố gắng tối ưu hóa các điểm nóng thực tế khác .. – Amro

Trả lời

3

Không chính xác một câu trả lời, nhưng đây là một cách để xem ảnh hưởng của trình biên dịch JIT và gia tốc trong cả hai phương pháp (cellfun vs cho-loop):

feature('jit', 'off'); feature('accel', 'off'); 
tic, A = cellfun(@(x) k*x, A, 'UniformOutput', false); toc 
tic, for i=1:numel(A), A{i} = A{i}*k; end, toc 

feature('jit', 'on'); feature('accel', 'on'); 
tic, A = cellfun(@(x) k*x, A, 'UniformOutput', false); toc 
tic, for i=1:numel(A), A{i} = A{i}*k; end, toc 

tôi nhận được sau

Elapsed time is 25.913995 seconds. 
Elapsed time is 13.050288 seconds. 

vs

Elapsed time is 10.053347 seconds. 
Elapsed time is 1.978974 seconds. 

với tối ưu hóa được bật trong giây.

Bằng cách này, song song parfor hoạt động kém hơn nhiều (ít nhất là trên máy thử nghiệm cục bộ của tôi với kích thước hồ bơi là 2 quy trình).

Xem kết quả bạn đã đăng, chức năng MEX là cách để đi :)

+0

những gì tôi sẽ hỏi là không liên quan đến câu hỏi này, nhưng có thể bạn có đầu mối. Bạn có nghĩ rằng việc bật tối ưu hóa như bạn đã làm sẽ cải thiện theo bất kỳ cách nào mà hệ số Choleski (tích hợp chức năng 'chol')? – fpe

+0

@pe: Tôi nghĩ rằng quá trình biên dịch JIT chỉ ảnh hưởng đến mã MATLAB được giải thích (bằng cách tạo mã được tối ưu hóa nhanh chóng) và afaik sẽ không ảnh hưởng đến các hàm dựng sẵn. Dù sao cả hai tối ưu hóa được bật theo mặc định, do đó bạn không phải lo lắng về điều đó ... Cộng với khi dữ liệu đáp ứng [các điều kiện nhất định] (http://www.mathworks.com/support/solutions/en/data/1 -4PG4AN /), 'CHOL' cũng tự động là đa luồng. – Amro

+1

@fpe: trong cùng một tinh thần, bạn có thể bắt đầu MATLAB với tùy chọn '-singleCompThread' để so sánh và xem hiệu quả của đa luồng. Tôi quên đề cập đến rằng hầu hết các hàm đại số tuyến tính dựng sẵn (như CHOL) sử dụng các triển khai tối ưu hóa cao của BLAS và LAPACK (thư viện Intel MKL, hoặc tương đương với bộ vi xử lý AMD).Vì vậy, bạn có thể nghỉ ngơi đảm bảo bạn đang nhận được hiệu suất tốt nhất .. – Amro