2013-04-06 21 views
5

A hücre dizisinin her bir öğesinin k katsayısıyla çarpmasını istiyorum. Bunu şu şekilde yapabilirim: Bir hücre dizisinin her bir öğesi üzerinde aritmetik işlem gerçekleştirmenin en hızlı yolu nedir?

Ancak bu son derece yavaştır. Daha hızlı ve daha iyi bir yolu var mı? Hücre dizisi öğeleri değişken uzunluklu vektörlerdir, bu nedenle cell2num geçerli değildir.

Düzenleme: For döngüsünün fpe en önerisi dayanarak, aşağıda bir örnek kriter.

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; 

önemli bir gelişme olan 1.67 seconds alır: döngü için bir süre bu verilerle başlangıç ​​

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

cellfun arama Yukarıdaki 9.45 seconds alır. Hala biraz daha büyük bir miktar siparişi tercih ederim. (. Matlab tercüman olarak hızlı döngü Onlar anlamsal olarak aynıdır olarak cellfun arama yapmak edemiyor Ayrıca neden anlamıyorum.)

Düzenleme 2: Amro öneri bir single yapmaya döngü anlamlı olduğu için daha hızlı:

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

1.11 seconds alır ve ben pack önce çalıştırırsanız o hafızayı sadece 0.88 seconds hizalamak için. 0.73 seconds, (0.53 secondspack sonra), çok sayıda küçük matrisleri tahsis Matlab yavaş olduğunu gösterir ki: Bunu yapmak için MEX fonksiyonunu uygulamak

aslında çok daha iyidir. 0.030 seconds: ancak, biraz Hile ve aslında bir yerde hafızayı düzenlemeleri bir MEX işlevi uygulayan

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

operasyon dışarı makul bir performans almak için tek yol olarak görünmektedir. Bu, Amro tarafından önerildiği gibi belgesiz mxUnshareArray kullanır. Ben

Aşağıdaki olsun

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 

:

#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

, yoludur 'for' döngü genellikle en kolay ve en hızlı çözümdür. – fpe

+0

Teşekkürler, bir döngü gerçekten önemli ölçüde daha hızlı (hala çok yavaş olsa da) – digitalvision

+0

digitalvision: Neden bunun çok yavaş olduğunu söylüyorsunuz? Burada hangi boyutlarda bahsediyoruz ve şu anda ne kadar sürüyor (tic/toc)? Ben böyle bir işlem kodunuzdaki darboğaz olduğundan şüphe ediyorum ... Profiler çalıştırın ve diğer gerçek sıcak noktalar optimize etmeye çalışın .. – Amro

cevap

3

Tam olarak değil bir cevap, ama burada her iki yaklaşımlar JIT derleyicisi ve hızlandırıcı etkiler (cellfun vs For döngüsü) görmek için bir yoldur

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. 
Ikinci en iyi duruma getirme ile. Bu arada, paralel parfor, daha da kötüsü (en azından 2 işlem havuz boyutu olan yerel test makinemde) daha da kötü performans gösterdi.Yayınladığınız sonuçları görünce

, MEX fonksiyonlu :) go yeni MATLAB sürümlerde

+0

sormak istediğim bu soruyla ilgili değil, ama belki bir ipucunuz var. Optimizasyonu, yaptığınız gibi açmanın herhangi bir şekilde Choleski factorization (built-in function 'chol') 'da gelişeceğini düşünüyor musunuz? – fpe

+0

@pe: JIT derlemesinin yalnızca yorumlanmış MATLAB kodunu (anında optimize edilmiş kod üreterek) ve yerleşik işlevleri etkilemeyen afaik'i etkilediğini düşünüyorum. Her iki optimizasyon da varsayılan olarak açık, bu yüzden endişelenmenize gerek yok ... Ayrıca veriler [belirli koşullar] karşılandığında (http://www.mathworks.com/support/solutions/en/data/1 -4PG4AN /), 'CHOL' otomatik olarak çok dişlidir. – Amro

+1

@fpe: aynı ruha göre, MATLAB ile -sleCompThread seçeneği ile çok iş parçacığının etkisini karşılaştırmak ve görmek için başlayabilirsiniz. Çoğu yerleşik lineer cebir fonksiyonunun (CHOL gibi), BLAS ve LAPACK'ın (Intel MKL kütüphanesi veya AMD işlemcileri için eşdeğeri) son derece optimize edilmiş uygulamalarını kullandığını söylemeyi unuttum.Böylece en iyi performansı aldığınızdan emin olabilirsiniz. – Amro

İlgili konular