9
votes

I'm optimizing some code at the moment and can't figure out a way to go faster than MATLAB's cell2mat. For now, the multiple uses of cell2mat in my code represent more than 15% of the processing time.

I think it can go faster than that, because i know the structure of the cell arrays i'll pass to the function.

Basically, the cell array is NxN, where :

  1. The top left (N-1)x(N-1) block contains, in each cell, a 6x6 double matrix

  2. The bottom right (N,N) cell is a MxM double matrix.

  3. The other cells have the right dimensions for concatenating, i.e. :

The cells (1:(N-1),N) are 6xM double matrices, the cells (N,1:(N-1)) are Mx6 double matrices. (Image added for the sake of clarity, there N=207 and M=300)

enter image description here

As the cells will always be filled with doubles AND always be of dimension 2, i'm already only using a small piece of the cell2mat code, i.e. :

 function m = myCell2Mat(c)

    rows = size(c,1);


        m = cell(rows,1);
        % Concatenate one dim first
        for n=1:rows
            m{n} = cat(2,c{n,:});% 73% of the time spent here
        end
        % Now concatenate the single column of cells into a matrix
        m = cat(1,m{:});% 25.2% of the time spent there


  end

This doens't change the time spent much as (like one could imagine), the most time is spent on these lines.

My question is : Does anyone have an idea about how to remove the loop there? I tried something along the lines of :

N=207;
M=300;
C=cell(N,N);
for ii=1:N-1
  for jj=1:N-1
C{ii,jj}=rand(6);
  end
end

for kk=1:(N-1)
C{N,kk}=rand(M,6);
C{kk,N}=rand(6,M);
end

C{end}=rand(M,M);

tmp1=cat(1,C{:,1:(end-1)});
LeftPart=reshape(tmp1,[],6*(size(C,2)-1));

RightPart=cat(1,C{:,end});

Res=[LeftPart RightPart];

But it doesn't show any improvment in time.. (And gives a false result as the reshape operates columnwise)

I've thought about using a recursive function aswell but it doesn't seem to be going anywhere.

Thanks in advance!

1
Because most of the MATLAB build-in functions are MEX files and usually you cannot increase the performance by writing a new version of that function as an m-file. - NKN
Actually as a matter of interest your code is very similar to that of cell2mat without the error checking. cell2mat is optimised for 2D cells, so I don't think you will get much of a speed up. - IKavanagh
@NKN Those implemented as .m files can be viewed with edit cell2mat. Those implemented as built-in (C) functions will say they are built-in and you won't be able to view their source. - IKavanagh
Or you could rethink your data structure. The inner (N-1)x(N-1) could be stored as a 4D-Matrix matrix which is typically faster than a cell. - Daniel
No you don't need to delete the question. It is (in my opinion) a good question (although improvement is possible). My final suggestion would be to seriously consider why you are storing the data in this way in the first place and if there is an alternative form you can use that doesn't use cells. - IKavanagh

1 Answers

5
votes

The conclusion from the comments is, that there is no way to improve the performance of cell2mat significantly. Instead I would suggest a better data structure.

You wrote that the data actually represents a 2D-Matrix. Instead of splitting it up into blocks, it's faster to reshape it into a 4D matrix. Let's say your Cell is called C and M is the corresponding 2D matrix

%Old Code
q=C{1,1}
%Faster way to index the same
%1) Convert 2D matrix to 4D
blocksize=6
M=reshape(M,blocksize,size(M,1)/blocksize,blocksize,size(M,2)/blocksize);
%2) Index a block
q=squeeze(M(:,1,:,1))

As reshaping basically takes zero time, you could switch back to the 2D view, for example to process the last row / column. If you want to avoid the conversion back, it is possible to index multiple bocks at once. For the last column use: reshape(M(:,206,:,207:end),6,[]) which corresponds to C{206,207}