From: Thomas Clark on
Hello all.

Sorry, this is more of an improvement request than a call for help - but thought it might be useful for some people to know...


I noticed recently that you can call (in R2010, at least) a function using the syntax:

[~, output_args] = someFunction(input_args).

In a trivial example, the following code is valid:
[~, I] = max([10 11])

This can be useful when we're only interested in some of the output arguments: The clarity of code is improved, and argument represented by ~ is not loaded into the caller's workspace - handy for memory conservation, maybe.

Let's say we use a more complicated function than max(), in which we don't necessarily need all the outputs each time we call, but would be inconvenient to re-code so that we only compute relevant data.

[~, arg] = myComplicatedFcn()

I had hoped that the JIT compiler would detect unnecessary code in myComplicatedFcn, and therefore not calculate the unwanted outputs.

That's not the case, as I discovered using a quick test function:


function [a b] = trickTest()
b = 10; % NB second argument assigned first
a = 1;
for i = 1:1:100000000
a = a^1.00001;
end



.... With the following results:

>> tic; [a b] = trickTest; toc
Elapsed time is 4.649376 seconds.
>> tic; [~, b] = trickTest; toc
Elapsed time is 4.648470 seconds.


Does anyone know whether this is even possible? Or perhaps whether it's planned?
From: us on
"Thomas Clark" <t.clark(a)remove.spamcantab.net> wrote in message <i24nbg$p23$1(a)fred.mathworks.com>...
> Hello all.
>
> Sorry, this is more of an improvement request than a call for help - but thought it might be useful for some people to know...
>
>
> I noticed recently that you can call (in R2010, at least) a function using the syntax:
>
> [~, output_args] = someFunction(input_args).
>
> In a trivial example, the following code is valid:
> [~, I] = max([10 11])
>
> This can be useful when we're only interested in some of the output arguments: The clarity of code is improved, and argument represented by ~ is not loaded into the caller's workspace - handy for memory conservation, maybe.
>
> Let's say we use a more complicated function than max(), in which we don't necessarily need all the outputs each time we call, but would be inconvenient to re-code so that we only compute relevant data.
>
> [~, arg] = myComplicatedFcn()
>
> I had hoped that the JIT compiler would detect unnecessary code in myComplicatedFcn, and therefore not calculate the unwanted outputs.
>
> That's not the case, as I discovered using a quick test function:
>
>
> function [a b] = trickTest()
> b = 10; % NB second argument assigned first
> a = 1;
> for i = 1:1:100000000
> a = a^1.00001;
> end
>
>
>
> ... With the following results:
>
> >> tic; [a b] = trickTest; toc
> Elapsed time is 4.649376 seconds.
> >> tic; [~, b] = trickTest; toc
> Elapsed time is 4.648470 seconds.
>
>
> Does anyone know whether this is even possible? Or perhaps whether it's planned?

yes, a well-known example of this unnecessary enhancement, which is mere baloney...
a nice character wasted for nothing substantial - could have been used for a more intelligent syntactical addition...

us
From: Walter Roberson on
Thomas Clark wrote:

> I had hoped that the JIT compiler would detect unnecessary code in
> myComplicatedFcn, and therefore not calculate the unwanted outputs.

In my experience, at least up through 2008b, the JIT does not do that kind of
optimization.

In order to do this kind of optimization properly, you need to track all
possible mechanisms by which a value might be used without it being
immediately obvious.

If your function calls a routine that *might* call evalin('caller'), then any
values you compute before that point have to be available because they might
be used by the evalin() call. evalin() can take the guise of eval() or evalc()
or evalin() or input() or str2num(), or possibly other routines:

str2num('evalin(''caller'',b)')

Likewise, if your routine calls something that *might* call
assignin('caller'), then a condition that _seems_ impossible to meet might
become true, leading to the need for a value that appeared dead.


The SGI (Silicon Graphics) "MIPSPro" series of compilers used to provide a
variant of this kind of analysis, in that they used to track which variables
might be written to, and used to do that tracking through multiple levels of
calls. The necessary implementation rule for those compilers was that if you
could not _prove_ that a variable was not written to, that you had to assume
that it might be changed -- the variable had to be marked "dirty" in the
linkage information for the routine; likewise, if your code appeared to call a
routine in which the variable was "dirty" then unless the optimizer could
_prove_ the call would never be made, that the current routine had to mark the
variable as "dirty" as well. Of course, C and C++ with their pointers provide
many subtle ways for variables to be written to, so _proving_ that a variable
was not changed could often only be done with fairly simple routines or fairly
well designed code. A lot of preprocessing work -- but in those days, the
resulting efficiency gains were important to SGI's target markets.


Matlab's JIT compiler does not do the kind of multi-level analysis I described
above. They do not explicitly document that they *don't* do it, so it could
happen in the future, but we can infer that it is not done. The way we can
infer that is that if you change a function, it is documented that the
function will be re-JIT'd [implicit in the documentation of clear() ] --
where-as if Matlab was doing multi-level analysis, then it would potentially
be necessary for a number of functions to be re-JIT'd (because a variable that
was unneeded before might become needed, and that could potentially cascade
through several levels.)
From: Thomas Clark on
us and Walter,

Thanks for your analyses. I clearly hadn't thought it all the way through. In light of what you've said, it sounds like it would be very difficult!

Thanks for responding, that's cleared it right up.

Tom




Walter Roberson <roberson(a)hushmail.com> wrote in message <i24q56$len$1(a)canopus.cc.umanitoba.ca>...
> Thomas Clark wrote:
>
> > I had hoped that the JIT compiler would detect unnecessary code in
> > myComplicatedFcn, and therefore not calculate the unwanted outputs.
>
> In my experience, at least up through 2008b, the JIT does not do that kind of
> optimization.
>
> In order to do this kind of optimization properly, you need to track all
> possible mechanisms by which a value might be used without it being
> immediately obvious.
>
> If your function calls a routine that *might* call evalin('caller'), then any
> values you compute before that point have to be available because they might
> be used by the evalin() call. evalin() can take the guise of eval() or evalc()
> or evalin() or input() or str2num(), or possibly other routines:
>
> str2num('evalin(''caller'',b)')
>
> Likewise, if your routine calls something that *might* call
> assignin('caller'), then a condition that _seems_ impossible to meet might
> become true, leading to the need for a value that appeared dead.
>
>
> The SGI (Silicon Graphics) "MIPSPro" series of compilers used to provide a
> variant of this kind of analysis, in that they used to track which variables
> might be written to, and used to do that tracking through multiple levels of
> calls. The necessary implementation rule for those compilers was that if you
> could not _prove_ that a variable was not written to, that you had to assume
> that it might be changed -- the variable had to be marked "dirty" in the
> linkage information for the routine; likewise, if your code appeared to call a
> routine in which the variable was "dirty" then unless the optimizer could
> _prove_ the call would never be made, that the current routine had to mark the
> variable as "dirty" as well. Of course, C and C++ with their pointers provide
> many subtle ways for variables to be written to, so _proving_ that a variable
> was not changed could often only be done with fairly simple routines or fairly
> well designed code. A lot of preprocessing work -- but in those days, the
> resulting efficiency gains were important to SGI's target markets.
>
>
> Matlab's JIT compiler does not do the kind of multi-level analysis I described
> above. They do not explicitly document that they *don't* do it, so it could
> happen in the future, but we can infer that it is not done. The way we can
> infer that is that if you change a function, it is documented that the
> function will be re-JIT'd [implicit in the documentation of clear() ] --
> where-as if Matlab was doing multi-level analysis, then it would potentially
> be necessary for a number of functions to be re-JIT'd (because a variable that
> was unneeded before might become needed, and that could potentially cascade
> through several levels.)