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;
}
}
in den neuesten MATLAB Versionen 'for' Schleife sind in der Regel die einfachste und schnellste Lösung. – fpe
danke, eine for-Schleife ist in der Tat deutlich schneller (immer noch sehr langsam) – digitalvision
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