2013-04-06 11 views
5

Angenommen, ich möchte jedes Element eines Zellarrays A mit einem Koeffizienten k multiplizieren. Ich kann das tun durch:Was ist der schnellste Weg, arithmetische Operationen für jedes Element eines Zellenarrays durchzuführen?

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

Aber das ist extrem langsam. Gibt es einen schnelleren und besseren Weg? Die Zellenfeldelemente sind Vektoren mit variabler Länge, so dass cell2num nicht gilt.

Edit: Basierend auf der fpe Empfehlung einer for-Schleife, hier ist ein Beispiel Benchmark. Ab diesen Daten

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

Der cellfun Anruf dauert über 9.45 seconds, während ein for-Schleife:

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; 

1.67 seconds nimmt, was eine wesentliche Verbesserung ist. Ich würde immer noch etwas ein paar Größenordnungen schneller bevorzugen. (Ich verstehe auch nicht, warum das Matlab-Interpreter nicht in der Lage ist, so schnell den cellfun Anruf zu machen, wie die for-Schleifen Sie sind semantisch identisch..)

Edit 2: Amro Vorschlag eine einzigen zu machen für Schleife signifikant ist schneller:

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

nimmt 1.11 seconds, und wenn ich pack bevor es laufen die Speicher nur 0.88 seconds auszurichten.

eine MEX-Funktion implementieren, dies zu tun ist eigentlich nicht viel besser: 0.73 seconds, (0.53 seconds nach pack), die das in Matlab langsam viele kleine Matrizen Zuweisung anzeigt.

#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); 
    } 
} 

ein bisschen Schummeln, aber, und eine MEX Funktion implementiert, die den Speicher anstelle tatsächlich bearbeitet scheint der einzige Weg, um eine angemessene Leistung aus dem Betrieb zu erhalten: 0.030 seconds. Dies verwendet die undokumentierten mxUnshareArray wie von Amro vorgeschlagen.

#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

in den neuesten MATLAB Versionen 'for' Schleife sind in der Regel die einfachste und schnellste Lösung. – fpe

+0

danke, eine for-Schleife ist in der Tat deutlich schneller (immer noch sehr langsam) – digitalvision

+0

digitalvision: warum sagst du, es ist extrem langsam? Von welchen Größen reden wir hier und wie lange dauert es (Tic/Toc)? Ich bezweifle, dass eine solche Operation der Flaschenhals in Ihrem Code ist ... Führen Sie den Profiler und versuchen Sie, andere tatsächliche Hot-Spots zu optimieren. – Amro

Antwort

3

Nicht gerade eine Antwort, aber hier ist ein Weg, der von JIT-Compiler und Beschleunigern in beiden Ansätzen (cellfun vs. for-Schleife) beeinflussen, um zu sehen:

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 

ich folgende

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

gegen

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

mit optimierter Optimierung in der Sekunde.

Übrigens, parallel parfor durchgeführt viel schlechter (zumindest auf meiner lokalen Testmaschine mit einer Poolgröße von 2 Prozessen).

die Ergebnisse sehen Sie auf dem Laufenden, MEX-Funktion ist der Weg zu gehen :)

+0

was ich fragen werde, ist nicht mit dieser Frage verbunden, aber vielleicht haben Sie Ahnung. Glauben Sie, dass das Optimieren der Optimierung, wie Sie es getan haben, in irgendeiner Weise eine Cholesker-Faktorisierung verbessern würde (eingebaute Funktion 'chol')? – fpe

+0

@pe: Ich denke, dass die JIT-Kompilierung nur den interpretierten MATLAB-Code beeinflusst (indem optimierter Code im laufenden Betrieb erzeugt wird) und afaik, der die eingebauten Funktionen nicht beeinflusst. Wie auch immer, beide Optimierungen sind standardmäßig aktiviert, so dass Sie sich keine Gedanken darüber machen müssen ... Außerdem, wenn die Daten [bestimmte Bedingungen] erfüllen (http://www.mathworks.com/support/solutions/en/data/1 -4PG4AN /), 'CHOL' wird automatisch ebenfalls multi-threaded. – Amro

+1

@fpe: im gleichen Sinne könnten Sie MATLAB mit der Option "-singleCompThread" starten, um zu vergleichen und den Effekt von Multi-Threading zu sehen. Ich habe vergessen zu erwähnen, dass die meisten eingebauten linearen Algebra-Funktionen (wie CHOL) hoch optimierte Implementierungen von BLAS und LAPACK verwenden (Intel MKL-Bibliothek oder das Äquivalent für AMD-Prozessoren).So können Sie sich darauf verlassen, dass Sie die beste Leistung erhalten. – Amro

Verwandte Themen