getting rid of for loop in a function with long arrays
Show older comments
Here is a small function to calculate the neff of SM fiber. To this function I insert three long arrays: n_core, n_clad and lamda (each can be even 2^17 long array - will need it for FFT later on) If the arrays are very long, the computer get stuck and even if not it's a long processing time. Was wondering if someone knows how to make this more efficient since matlab is more efficient on matrix calculations. Basically get rid of the for loop. Note that this loop exists to solve eq. numerically something I wasn't manage to do with the "solve" function.
Thanks.
Here is the code:
function [neff,betta]=neff_fiber2(n_core,n_clad,core_diameter,lamda)
%a is the radius in um;
%lamda is the wavelenght in um;
a = core_diameter/2; %radius
k0 = 2*pi./lamda;
V = k0.*a.*sqrt(n_core.^2 - n_clad.^2);%V number
betta = zeros(1,length(V));
%%now we want to solve the following
% J0(x) / ( x*J1(x))=K0(sqrt(V^2-x^2) )/(sqrt(V^2-x^2)*K1(sqrt(V^2-x^2)))
% where x=Ka
for ii=1:length(V)
x=linspace(0,V(ii),1e6);
Diff=abs(besselj(0,x)./(x.*besselj(1,x))- ...
besselk(0,sqrt(V(ii).^2-x.^2))./(sqrt(V(ii)^2- ...
x.^2).*besselk(1,sqrt(V(ii)^2-x.^2))));
[~, ind1] = min(Diff(1,:)); % using the index of the minimum value in the row
Kappa = x(ind1)./a;
betta(ii) = sqrt(k0(ii).^2 * n_core(ii)^2 - Kappa.^2);
end
neff=betta./k0;
end
13 Comments
Jan
on 4 Aug 2017
Start with avoiding repeated calculations of the same value. Replace:
Diff=abs(besselj(0,x)./(x.*besselj(1,x))- ...
besselk(0,sqrt(V(ii).^2-x.^2))./(sqrt(V(ii)^2- ...
x.^2).*besselk(1,sqrt(V(ii)^2-x.^2))));
by
a = sqrt(V(ii).^2 - x.^2);
Diff = abs(besselj(0, x) ./ (x .* besselj(1, x)) - ...
besselk(0, a) ./ (a .* besselk(1, a)));
(Much nicer!) But I assume that neither sqrt() is the bottleneck, nor the loop, but the Bessel functions. I do not see a way to reduce the number of calls to besselj and besselk. What about a parfor?
Jan
on 4 Aug 2017
@elis02: I'd expect the parallel loop to accelerate the code by a factor of the number of cores here. 2^17 is only 131'072, such that the data might match into the processor caches.
Is there any chance, that the "x = linspace(0,V(ii),1e6)" reply the same values for the different ii? Then you could reduce the number of Bessel function calls.
Would it help to calculate the values for
x = linspace(0,max(V),1e7);
and then use a linear interpolation of the output for the other V(ii)?
...
x=linspace(0,V(ii),1e6);
followed by besselj/k calls for all x is a lot of function evaluations when multiplied by the loop.
Is not the solution smooth and in the same neighborhood of the first solution as the iteration progresses?
I'd be looking at finding the neighborhood of the solution coarsely and then homing in instead of a global search starting over from scratch every time.(*)
What are typical input values and what does the functional form look like? Give us some help here other than just trying to read code...
What went wrong with fsolve or another builtin solver routine?
(*) Even a binary search to bracket solutions should help markedly rather than just brute-force solution for 1E6 points...
Christos Saragiotis
on 4 Aug 2017
elis02, try using tic toc to see it the elapsed time for each iteration increases, as the iterations increase.
If this is the case, then try the following and check if you see any difference:
- Preallocate Diff before the loop,
Diff = zeros(1,1e6).
- In the loop, when you assign the values to Diff, use
Diff(:) = abs(besselj(0,x)...
The reason why you may see a difference is that when you use "Diff = ", then MATLAB creates a new variable for Diff in each loop (it doesn't matter it has the same name) and tries to find some new memory to store it. This results in slower and slower execution as the number of iterations increases (and eventually most probably MATLAB or your computer will freeze). If you use the "Diff(:) =" , then MATLAB will store the values in the preallocated memory block in every iteration, and every iteration will take the same time ( no new variable creation and no empty memory hunt).
dpb
on 4 Aug 2017
I don't think this is true and a timing test showed no difference in the two assignment statements. Whole array assignment isn't the same as elementally "growing" an array or partial writes that may require a copy.
Walter Roberson
on 4 Aug 2017
The right hand side is always calculated separately and no assignment is done until it is finished calculating. At that point memory containing the values already exists.
If you assign to the whole array then the attributes and data pointer of the symbol table entry for the array are updated to hold the (anonymous) information from the right hand side, and the reference count for the former block on the left is decremented, potentially decrementing its share count to 0, allowing it to be released back to the pool.
If you assign to the the array accessed at (:) then the results must be copied from the new object to the old object (possibly after creating a new copy of the old object if the share count was more than 1) and then the new object has its share count decremented (typically resulting it it being released back to the pool.) Either way something the size of the object gets (likely) released back to the pool, but the assignment to (:) form requires a data copy that the assignment to the whole array did not.
Therefore assignment to (:) does not save any memory and cannot be any faster than assignment to the whole object.
elis02
on 4 Aug 2017
Walter Roberson
on 4 Aug 2017
We recommend that you do not use inline. Use matlabFunction instead.
F = matlabFunction( besselj(0,x)./(x.*besselj(1,x))-besselk(0,sqrt(V.^2-x.^2))./(sqrt(V.^2-x.^2).*besselk(1,sqrt(V.^2-x.^2))));
fsolve(F, 1)
Note there are other solutions such as
fsolve(F, -1)
Walter Roberson
on 4 Aug 2017
Or use
vpasolve(besselj(0,x)./(x.*besselj(1,x))-besselk(0,sqrt(V.^2-x.^2))./(sqrt(V.^2-x.^2).*besselk(1,sqrt(V.^2-x.^2))),x,[0 V])
dpb
on 5 Aug 2017
Yet again, what are some typical input values???
Give us something that actually will run.
elis02
on 5 Aug 2017
Accepted Answer
More Answers (1)
Walter Roberson
on 5 Aug 2017
x=linspace(0,V(ii),1e6);
can be replaced by
x = V(ii) * linspace(0, 1, 1e6);
That in turn leads you to the vectorized
xG = bsxfun( V(:), linspace(0, 1, 1e6) );
and then
temp = sqrt( bsxfun( @minus, V(:).^2, x.^2) );
DiffG = abs(besselj(0, xG) ./ (xG .* besselj(1,xG)) - ...
besselk(0, temp) ./ temp .* besselk(1, temp)));
[~, ind1] = min(DiffG, [], 2); %per_row minima
kappa_ind = sub2ind(size(xG), 1:size(xG,1), ind1); %each ind1 value refers to a different row
kappa = xG(kappa_ind) ./ a; %his is a vector, one for each V
betta = sqrt(k0.^2 .* n_core.^2 - Kappa.^2);
should be the vector solution.
4 Comments
elis02
on 5 Aug 2017
Walter Roberson
on 5 Aug 2017
xG = bsxfun( @times, V(:), linspace(0, 1, 1e6) );
Walter Roberson
on 5 Aug 2017
Ah. In that case you will not be able to vectorize the calculation.
Categories
Find more on Creating and Concatenating Matrices in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!
