1
$\begingroup$

This may not be so much of a scientific computing question but more of a MATLAB question, if that is the case, please feel free to close or migrate the question.

Root-finding problems are commonly encountered in scientific computing in various shapes and forms; but in this case, the goal is to solve a non-linear time-dependent PDE using operator splitting techniques. Without going into the details of the PDE problem (since it is irrelevant to my main question), the following lines are executed each time step:

S = arrayfun(@(x,y) fsolve(@(c)[x-c(1)-c(1)*c(2);y-c(1)*c(2)],rand(1,2),options),u,v, ... 
      'UniformOutput',false);

options is a structure to set the parameters of fsolve, u and v are column vectors of medium size (~8k or larger) and fsolve returns a vector (1 row by 2 columns) which necessitates the option (for arrayfun) UniformOutput to be set false. I can replace these two lines with the following for loop:

for i=1:length(u)
    S(i,:) = fsolve(@(c)[u(i)-c(1)-c(1)*c(2);v(i)-c(1)*c(2)],rand(1,2),options);
end

This excerpt runs two times faster than the previous. The time difference even more significant if S is initialized ahead of time and transposed (so it aligns with column-major ordering of MATLAB). Apparently, this behaviour is expected since arrayfun just “hides” (internalizes) the for loop, and we have additional overhead introduced by the extra function handle.

Under this observation, what is the correct use case for the function arrayfun? If for loops are faster, why would someone use arrayfun? Especially when (arguably) for loops are easier to read?

Note: There are some nuances. For example, S as the output of arrayfun is a cell array in contrast to the n-by-2 matrix S we get from the for loop approach. So let's say that I don't care about the output, it could as well be dumped after computation. I am more interested in the reasons why someone would use arrayfun over a for loop given that arrayfun is 2x slower.

Edit 1: Octave gives the following justification for arrayfun's existence: "This is useful for functions that do not accept array arguments.". But then follows up with "If the function does accept array arguments it is better to call the function directly. ", since they also only internalize the for loop. And at the end of the documentation, they say that the built-in @plus function is 60% faster than the anonymous function @(x,y) x+y. This is probably because @plus behaves more like C macros, so there is no additional function call overhead.

$\endgroup$
1
$\begingroup$

I think the main use of it is abstraction and the advantages that come with it:

  • Applying a function to each element of an array comes up in many situations. Wrapping this pattern into a function raises the level of abstraction: you do not need to think in loops, your intent is probably clearer. If you are familiar with Python, comprehensions do this, as well as the built-in map function. NumPy also applies a function on each element of an array.
  • It allows MathWorks to implement this pattern efficiently. Although arrayfun is currently implemented with a for loop, this might change in the future. If MathWorks later decides to change the implementation, as soon as the function signature remains the same, no backward compatibility issue arises.
  • It allows third-party to reimplement it, as it is already done in the Parallel Computing Toolbox. You would need to rewrite your code with parfor to achieve the same.
$\endgroup$
9
  • $\begingroup$ I am not that familiar with Python, so I may be wrong. But afaik, both map and list comprehensions are abused to a point by some programmers that it is becoming a contentious issue. Especially the criticism focuses on inefficiency and unreadability (as is my issue with arrayfun). The case in this question seems to be a proper use case though. So I am going to agree that it may be beneficial if you know what these things are already; but if you don't then it is just cryptic electric boogaloo pants in trousers, or simply makes the code unreadable like cryptic electric boogaloo pants in trousers. $\endgroup$ – Abdullah Ali Sivas May 30 at 13:55
  • $\begingroup$ Regarding MATLAB implementing arrayfun with a for loop: it has to be done that way at the lower levels. However, JIT compiler seems to be smart enough to efficiently translate my for loop to a machine level loop. So that is not a real benefit in my view. Though, in the future they potentially can figure out a way to remove the overhead. In that case, it would become an issue of readability (unless, they don't make the JIT compiler worse in the process so that arrayfun gains an edge) $\endgroup$ – Abdullah Ali Sivas May 30 at 13:59
  • $\begingroup$ I can see the parallel computing angle; however, for in the for loop I wrote above can be simply replaced by parfor. That would be the degree of rewriting I would need. Additionally, on a single core, there is no difference between for and parfor in MATLAB. So, if the goal is future proofing I could as well use parfor from the beginning. I guess you could argue that arrayfun can target GPUs and CPUs with a simple switch, but that would require significant rewrite by itself. For example, I would have to make all my arrays in to gpuArrays (which is more than adding par to for). $\endgroup$ – Abdullah Ali Sivas May 30 at 14:00
  • $\begingroup$ Nevertheless, I am giving you an upvote, because the answer is actually useful and might be the point of view of the Mathworks developers. $\endgroup$ – Abdullah Ali Sivas May 30 at 14:00
  • $\begingroup$ "both map and list comprehensions are abused to a point by some programmers that it is becoming a contentious issue. Especially the criticism focuses on inefficiency and unreadability" Python's list comprehensions are more efficient than bare for loops (in CPython, for loops are not JIT compiled, as opposed to MATLAB). The fact that a certain language feature is abused is not the fault of the language. For instance, old time MATLAB users tend to vectorize everything to a point when it becomes unreadable (they don't know about JIT compilation). Similar observation for function handles. $\endgroup$ – Zoltán Csáti May 30 at 19:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.