2013-04-06 12 views
5

Powiedz, że chcę pomnożyć każdy element tablicy komórek A o współczynniku k. Mogę to zrobić przez:Jaki jest najszybszy sposób wykonywania operacji arytmetycznych na każdym elemencie tablicy komórki?

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

Ale to bardzo wolno. Czy istnieje szybszy i lepszy sposób? Elementy tablicy komórki są wektorami o zmiennej długości, więc cell2num nie ma zastosowania.

Edit: podstawie FPE za rekomendacji dla pętli, tutaj jest przykładem odniesienia. Począwszy od tych danych

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

cellfun wezwanie powyżej bierze 9.45 seconds, podczas gdy dla pętli:

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; 

trwa 1.67 seconds, która jest znaczna poprawa. Wciąż wolałbym coś o kilka rzędów wielkości szybciej. (Ja też nie rozumiem, dlaczego interpreter Matlab nie jest w stanie nawiązać połączenie cellfun tak szybko jak dla pętli Są one semantycznie identyczne.).

Edit 2: sugestia Amro złożenia jednego do pętli jest znacznie szybciej:

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

trwa 1.11 seconds, a jeśli biegnę pack przed jej wyrównać pamięć tylko 0.88 seconds.

Implementacja funkcji MEX to zrobić w rzeczywistości nie jest dużo lepiej: 0.73 seconds (0.53 seconds po pack), co wskazuje, że przydzielanie wielu małych matryc jest powolny w 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); 
    } 
} 

Oszukiwanie trochę, jednak i realizuje funkcję MEX że faktycznie edytuje pamięć w miejscu wydaje się być jedynym sposobem, aby uzyskać wystarczającą wydajność z operacji: 0.030 seconds. To wykorzystuje nieudokumentowane mxUnshareArray zgodnie z sugestią 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

w najnowszych wydaniach MATLAB, 'for' pętli są zazwyczaj najprostszym i najszybszym rozwiązaniem. – fpe

+0

dziękuję, pętla for jest rzeczywiście znacznie szybsza (nadal bardzo powolna) – digitalvision

+0

digitalvision: dlaczego mówisz, że jest bardzo powolny? o jakich rozmiarach mówimy tutaj i jak długo trwa (tic/toc)? Wątpię, aby taka operacja była wąskim gardłem w twoim kodzie ... Uruchom profilera i spróbuj zoptymalizować inne aktualne hot-spoty. – Amro

Odpowiedz

3

Niezupełnie odpowiedź, ale tutaj jest sposób, aby zobaczyć wpłynąć JIT kompilator i akcelerator w obu podejściach (cellfun porównaniu do pętli):

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 

otrzymuję następujący

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. 

z optymalizacją włączoną w drugim.

Przy okazji, równolegle parfor wykonano znacznie gorzej (przynajmniej na mojej lokalnej maszynie testowej o wielkości puli 2 procesów).

Widząc rezultaty zaksięgowane, MEX-funkcja jest droga :)

+0

o co zapytam nie jest związany z tym pytaniem, ale może masz wskazówkę. Czy myślisz, że włączenie optymalizacji tak, jak zrobiłeś, poprawiłoby w jakikolwiek sposób faktoryzację Choleskiego (wbudowana funkcja 'chol ')? – fpe

+0

@pe: Myślę, że kompilacja JIT wpływa tylko na zinterpretowany kod MATLAB (generując zoptymalizowany kod w locie) i afaik, który nie wpłynąłby na wbudowane funkcje. W każdym razie obie optymalizacje są domyślnie włączone, więc nie musisz się o to martwić ... Plus, gdy dane spełniają [pewne warunki] (http://www.mathworks.com/support/solutions/en/data/1 -4PG4AN /), 'CHOL' jest również automatycznie wielowątkowy. – Amro

+1

@fpe: w tym samym duchu możesz uruchomić MATLAB z opcją '-singleCompThread', aby porównać i zobaczyć efekt wielowątkowości. Zapomniałem wspomnieć, że większość wbudowanych funkcji algebry liniowej (takich jak CHOL) używa wysoce zoptymalizowanych implementacji BLAS i LAPACK (biblioteka Intel MKL lub odpowiednik dla procesorów AMD).Dzięki temu możesz mieć pewność, że uzyskasz najlepszą wydajność. – Amro

Powiązane problemy