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;
}
}
w najnowszych wydaniach MATLAB, 'for' pętli są zazwyczaj najprostszym i najszybszym rozwiązaniem. – fpe
dziękuję, pętla for jest rzeczywiście znacznie szybsza (nadal bardzo powolna) – digitalvision
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