MATLAB: how to divide and distribute a list of cell arrays over workers in a parallel loop? - matlab

I have a question about parallelizing code in MATLAB. I use MATLAB 2017a.
Let's say I have a cell array:
A = { A1, ..., A10}
and these matrices are quite big ( size > 10000 ). Now I want to start manipulating these matrices in a parallelpool. In fact, ther first worker needs only A1, the second worker needs only A2 and so on.
I have now this code;
parfor i = 1:10
matrix = A{i};
blabla = manipulate(Ai);
save(blabla);
end
I think that MATLAB gives every worker all the matrices in A but this is not really needed. Is there a way to say:
"Give i-th worker only matrix Ai"?

Based on the documentation for variables in parfor loops, specifically sliced variables, it appears as though the cell array A in your example meets the criteria to be treated as a sliced variable by default. You shouldn't have to do anything special. You may want to confirm that all the listed criteria are met, and take a look at each variable to see how they are used both inside and outside your parfor loop.

You want spmd blocks. That way, you explicitly handle the slicing of the parallel data, rather than letting Matlab do it automagically with the parfor block.
parpool('myprofile',10)
spmd
i = labindex;
B = foo(A{i});
end
for i = 1:10
bar(B{i});
end

Related

indexing within parfor loop - matlab

I'm trying to run this code in Matlab
a = ones(4,4);
b=[1,0,0,1;0,0,0,1;0,1,0,0;0,0,0,0];
b(:,:,2)=[0,1,1,0;1,1,1,0;1,0,1,1;1,1,1,1];
parfor i = 1:size(b,3)
c = b(:,:,i)
a(c) = i;
end
but get the error:
Error: The variable a in a parfor cannot be classified.
See Parallel for Loops in MATLAB, "Overview".
There are restrictions in how you can write into arrays inside the body of a parfor loop. In general, you will need to use sliced arrays.
The reason behind this issue is that Matlab needs to prevent that different worksers access the same data, leading to unpredictable results (as the timely order in which the parfor loops through i is not detemined).
So, although in your example the workers don't operate on the same entries of a, due to the way how you index a (with an array of logicals), it is currently not possible for Matlab to decide if this is the case or not (in other words, Matlab cannot classify a).
Edit: For completeness I add some code that is equivalent to your example, although I assume that your actual problem involves more complicated logical indexing?
a = ones(4,4,4);
parfor i = 1:size(a,1)
a(i, :, :) = zeros(4, 4) + i; % this is sliced indexing
end
Edit: As the OP example was modified, the above code is not equivalent to the example anymore.

Parallel computation with cell array

A,B,C: cell arrays of size 100 x 1.
Each cell of A is a matrix. All have same size.
B and C contain vectors.
I need to create a cell array D of size 100 x 1. Serial code will look something like:
for i=1:100
D{i}=my_func(A{i},B{i},C{i});
end
where my_func is a function that takes input of a matrix and vectors, producing a vector.
I want to use parfor (or spmd) to make things faster. However, A has large size so I don't want to broadcast A to all workers. Is there a way to do this efficiently, given that my_func takes sometime? If anyone can give me a small example, I will appreciate it.
It looks like your cell arrays are all sliced variables. This means they will not be broadcast to all workers, only those segments each worker needs.
Thus, you can safely replace the for with parfor:
parfor i = 1:100
D{i} = my_func(A{i}, B{i}, C{i});
end

how can I make these four loop compute paralleling?

I have a problem with MathWorks Parallel Computing Toolbox in Matlab. See my code below
for k=1:length(Xab)
n1=length(Z)*(k-1)+1:length(Z)*k;
MX_j(1,n1)=JXab{k};
MY_j(1,n1)=JYab{k};
MZ_j(1,n1)=Z;
end
for k=length(Xab)+1:length(Xab)+length(Xbc)
n2=length(Z)*(k-1)+1:length(Z)*k;
MX_j(1,n2)=JXbc{k-length(Xab)};
MY_j(1,n2)=JYbc{k-length(Yab)};
MZ_j(1,n2)=Z;
end
for k=length(Xab)+length(Xbc)+1:length(Xab)+length(Xbc)+length(Xcd)
n3=length(Z)*(k-1)+1:length(Z)*k;
MX_j(1,n3)=JXcd{k-length(Xab)-length(Xbc)};
MY_j(1,n3)=JYcd{k-length(Yab)-length(Ybc)};
MZ_j(1,n3)=Z;
end
for k=length(Xab)+length(Xbc)+length(Xcd)+1:length(Xab)+length(Xbc)+length(Xcd)+length(Xda)
n4=length(Z)*(k-1)+1:length(Z)*k;
MX_j(1,n4)=JXda{k-length(Xab)-length(Xbc)-length(Xcd)};
MY_j(1,n4)=JYda{k-length(Yab)-length(Ybc)-length(Ycd)};
MZ_j(1,n4)=Z;
end
If I change the for-loop to parfor-loop, matlab warns me that MX_j is not an efficient variable. I have no idea how to solve this and how to make these for loops compute in parallel?
For me, it looks like you can combine it to one loop. Create combined cell arrays.
JX = cat(2,JXab, JXbc, JXcd, JXda);
JY = cat(2,JYab, JYbc, JYcd, JYda);
Check for the right dimension here. If your JXcc arrays are column arrays, use cat(1,....
After doing that, one single loop should do it:
n = length(Xab)+length(Xbc)+length(Xcd)+length(Xda);
for k=1:n
k2 = length(Z)*(k-1)+1:length(Z)*k;
MX_j(1,k2)=JX{k};
MY_j(1,k2)=JY{k};
MZ_j(1,k2)=Z;
end
Before parallizing anything, check if this still valid. I haven't tested it. If everything's nice, you can switch to parfor.
When using parfor, the arrays must be preallocated. The following code could work (untested due to lack of test-data):
n = length(Xab)+length(Xbc)+length(Xcd)+length(Xda);
MX_j = zeros(1,n*length(Z));
MY_j = MX_j;
MZ_j = MX_j;
parfor k=1:n
k2 = length(Z)*(k-1)+1:length(Z)*k;
MX_j(1,k2)=JX{k};
MY_j(1,k2)=JY{k};
MZ_j(1,k2)=Z;
end
Note: As far as I can see, the parfor loop will be much slower here. You simply assign some values... no calculation at all. The setup of the worker pool will take 99.9% of the total execution time.

Fill an array with spmd in Matlab

I have a 1-by-p array R in Matlab (with p large). I initialize this array with all its entries equal to 0 and the i-th element of the array R shall receive the output from myfunction, applied to parameters(i). In other words :
R=zeros(p,1);
for i=1:p
R(i)=myfunction(parameters(i));
end
The same function myfunction is applied multiple times with different input. And because p might become large, I recognized a spmd problem (single program, multiple data) and thought that using the spmd construct would help the previous code run faster.
If I run matlabpool, I obtain n_workers different labs. My idea is to break the array R into n_workers different parts and ask each available worker to fill a part of the array. I would like to do something like this :
q=((p-1)-mod(p-1,n_workers-1))/(n_workers-1);
lab 1:
for j=1:q
R(j) = myfunction(parameters(j));
end
lab 2:
for j=(q+1):(2*q+1)
R(j) = myfunction(parameters(j));
end
...
lab n_workers:
for j=( q*(n_workers-1)+1 ):p
R(j) = myfunction(parameters(j));
end
However, since I'm new to the parallel programming, I don't know how to write this properly in Matlab. Instead of subdividing myself the array R, could I use a coditributed array instead ?
Firstly, if your function evaluations are independent, you might well be better off using parfor, like so:
R=zeros(p,1);
parfor i=1:p
R(i)=myfunction(parameters(i));
end
spmd is generally only useful when you need communication between iterations. In any case, you can run this sort of thing inside spmd using the for-drange construct like so:
spmd
R = codistributed.zeros(p, 1)
for i = drange(1:p)
R(i) = myfunction(parameters(i));
end
end
In that case, you'd probably also want to make parameters be a distributed array too to avoid having multiple copies in memory. (parfor automatically avoids that problem by "slicing" both R and parameters)

Error when trying to use parfor (parallel for loop) in MATLAB

I am dealing with a very huge matrix and so wanted to use parallel computing in MATLAB to run in clusters. Here I have created a sparse matrix using:
Ad = sparse(length(con)*length(uni_core), length(con)*length(uni_core));
I have a written function adj using which I can fill the matrix Ad.
Every time the loop runs, from the function adj I get a square symmetric matrix which is to be assigned to the Ad from 3682*(i-1)+1 to 3682 *(i-1)+3682 in the first index and similarly in the second index. This is shown here:
parfor i = 1:length(con)
Ad((3682*(i-1))+1:((3682*(i-1))+3682), ...
(3682*(i-1))+1:((3682*(i-1))+3682)) = adj(a, b, uni_core);
end
In a normal for loop it is running without any problem. But in parfor in parallel computing I am getting an error that there is a problem in using the sliced arrays with parfor.
Outputs from PARFOR loops must either be reduction variables (e.g. calculating a summation) or "sliced". See this page in the doc for more.
In your case, you're trying to form a "sliced" output, but your indexing expression is too complicated for PARFOR. In a PARFOR, a sliced output must be indexed by: the loop variable for one subscript, and by some constant expression for the other subscripts. The constant expression must be either :, end or a literal scalar. The following example shows several sliced outputs:
x3 = zeros(4, 10, 3);
parfor ii = 1:10
x1(ii) = rand;
x2(ii,:) = rand(1,10);
x3(:,ii,end) = rand(4,1);
x4{ii} = rand(ii);
end
In your case, your indexing expression into Ad is too complicated for PARFOR to handle. Probably the simplest thing you can do is return the calculations as a cell array, and then inject them into Ad on the host side using a regular FOR loop, like so:
parfor i = 1:length(con)
tmpout{i} = ....;
end
for i = 1:length(con)
Ad(...) = tmpout{i};
end
Edric has already explained why you're getting an error, but I wanted to make another suggestion for a solution. The matrix Ad you are creating is made up of a series of 3682-by-3682 blocks along the main diagonal, with zeroes everywhere else. One solution is to first create your blocks in a PARFOR loop, storing them in a cell array. Then you can combine them all into one matrix with a call to the function BLKDIAG:
cellArray = cell(1,length(con)); %# Preallocate the cell array
parfor i = 1:length(con)
cellArray{i} = sparse(adj(a,b,uni_core)); %# Compute matrices in parallel
end
Ad = blkdiag(cellArray{:});
The resulting matrix Ad will be sparse because each block was converted to a sparse matrix before being placed in the cell array.