Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

use of optional parameters

25 views
Skip to first unread message

Phillip Helbig---undress to reply

unread,
Apr 7, 2012, 7:54:40 AM4/7/12
to
I suppose most people use optional output parameters to avoid executing
code which is not needed or even impossible (depending on optional input
parameters, say). Another use is to make things easier for the user,
e.g. a subroutine with many optional output parameters and the user
specifies only those he is interested in. Of course, this could
correspond to the first use, i.e. only those specified are calculated.
However, presumably one could always calculate all output parameters and
only those the user specified are returned. Right?

If the above is true, one could have the case where there are two calls
to the subroutine with the same input parameters but different optional
output parameters. If the output parameters don't depend on anything
except the input parameters (i.e. they are "pure"), then, assuming they
are SAVEd parameters, one doesn't have to calculate them again. So,
would pseudocode like the following work?

IF (same input parameters as last time) THEN RETURN

I'm not interested in whether it works with my compiler (unless my
compiler violates the standard in this respect) but whether the standard
implies that one can rely on this behaviour.

A related issue might be optimization. If an output parameter is not
specified, presumably its calculation could be optimized away. Could
this be avoided by the following pseudocode?

IF (same input parameters as last time) THEN GOTO 100

<calculate variables declared in the module which contains this routine>

100 CONTINUE

output1 = internal1
output2 = internal2

or

IF (not same parameters as last time) THEN CALL CALCULATE ! after CONTAINS in this routine

output1 = internal1
output2 = internal2

CONTAINS

internal1 = blabla
internal2 = blabla

In other words, if variables are module variables rather than local
variables in the subroutine, does this prevent them from being optimized
away?

If not, would separate compilation prevent them from being optimized
away? That is, first compile the module containing the subroutine then
link it to a separately compiled caller program?

Michael

unread,
Apr 7, 2012, 9:48:21 AM4/7/12
to
On Apr 7, 6:54 am, hel...@astro.multiCLOTHESvax.de (Phillip Helbig---
What is exactly that you are trying to accomplish? speed, clarity,
robustness? why would you use GOTO statements? You can avoid a large
list of arguments in a subroutine by using structures.. what is an
"optimized variable"?

Phillip Helbig---undress to reply

unread,
Apr 7, 2012, 10:45:12 AM4/7/12
to
In article
<1c75384d-c73a-436f...@h20g2000yqd.googlegroups.com>,
Michael <michael.c...@gmail.com> writes:

> What is exactly that you are trying to accomplish? speed, clarity,
> robustness?

All three.

The idea is a very general routine which calculates many things. It
isn't a good idea to have IF (PRESENT(X)) in the code since a) the
calculation is rather quick and b) some things are needed internally
even if the user doesn't want them returned. I want to avoid a long
parameter list; the user should just specify what he wants, maybe just
one parameter from, say, 20. I don't like the idea of global variables.

> why would you use GOTO statements? You can avoid a large
> list of arguments in a subroutine by using structures..

Not sure what you mean here. For the user, it should be easy to use,
just CALL SUB(input1=x,input2=y,output1=a,output2=b) etc where the user
needs to declare just these arguments.

> what is an
> "optimized variable"?

I meant a variable the calculation of which has been optimized away.

gmail-unlp

unread,
Apr 7, 2012, 11:05:52 AM4/7/12
to
On Apr 7, 11:45 am, hel...@astro.multiCLOTHESvax.de (Phillip Helbig---
undress to reply) wrote:
> In article
> <1c75384d-c73a-436f-850b-a46b7eb1a...@h20g2000yqd.googlegroups.com>,
>
> Michael <michael.caracots...@gmail.com> writes:

[snip]

> The idea is a very general routine which calculates many things.

Hmmm... I usually try to do the opposite: only one routine for only
one calculation... but maybe I'm losing something, do you have some
specific example at hand?

Fernando.

[snip]

Louisa

unread,
Apr 7, 2012, 11:35:23 AM4/7/12
to
On Apr 8, 12:45 am, hel...@astro.multiCLOTHESvax.de (Phillip Helbig---
undress to reply) wrote:
> In article
> <1c75384d-c73a-436f-850b-a46b7eb1a...@h20g2000yqd.googlegroups.com>,
>
> Michael <michael.caracots...@gmail.com> writes:
> > What is exactly that you are trying to accomplish? speed, clarity,
> > robustness?
>
> All three.
>
> The idea is a very general routine which calculates many things.  It
> isn't a good idea to have IF (PRESENT(X)) in the code since a) the
> calculation is rather quick and b) some things are needed internally
> even if the user doesn't want them returned.  I want to avoid a long
> parameter list; the user should just specify what he wants, maybe just
> one parameter from, say, 20.  I don't like the idea of global variables.
>
> > why would you use GOTO statements? You can avoid a large
> > list of arguments in a subroutine by using structures..
>
> Not sure what you mean here.  For the user, it should be easy to use,
> just CALL SUB(input1=x,input2=y,output1=a,output2=b) etc where the user
> needs to declare just these arguments.

Another possibility is to use generic procedures to deal with the
different numbers of arguments.

Phillip Helbig---undress to reply

unread,
Apr 7, 2012, 11:45:09 AM4/7/12
to
In article
<a689149e-04f3-40d3...@m13g2000yqi.googlegroups.com>,
gmail-unlp <ftin...@gmail.com> writes:

> > The idea is a very general routine which calculates many things.
>
> Hmmm... I usually try to do the opposite: only one routine for only
> one calculation... but maybe I'm losing something, do you have some
> specific example at hand?

Too complicated to post here. Imagine one quantity which is relatively
difficult to calculate then many quantities which are related to it and
one another which are easy to calculate. Standalone routines would
duplicate code and cause things to be calculated too often if more than
one of the derived quantities is needed. One could have a routine for
each derived quantity which then derives the quantity from the basic
quantity which has already been calculated. But if the user wants, say,
five quantities, he has five subroutine calls; I think one call with 5
optional parameters is clearer.

Ron Shepard

unread,
Apr 7, 2012, 12:08:48 PM4/7/12
to
In article <jlpjto$ls7$1...@online.de>,
hel...@astro.multiCLOTHESvax.de (Phillip Helbig---undress to
reply) wrote:

> In article
> <1c75384d-c73a-436f...@h20g2000yqd.googlegroups.com>,
> Michael <michael.c...@gmail.com> writes:
>
> > What is exactly that you are trying to accomplish? speed, clarity,
> > robustness?
>
> All three.
>
> The idea is a very general routine which calculates many things. It
> isn't a good idea to have IF (PRESENT(X)) in the code since a) the
> calculation is rather quick and b) some things are needed internally
> even if the user doesn't want them returned. I want to avoid a long
> parameter list; the user should just specify what he wants, maybe just
> one parameter from, say, 20. I don't like the idea of global variables.

I think you are making the following assumptions in your code.

1) the programmer calling the subroutine knows which outputs depend
on which inputs.

2) all outputs are computed in any case, and the issue is just which
ones the programmer requests to see.

In these cases, an alternative approach is to define a derived type
that contains all of the outputs. The programmer declares an
instance of this type for his given set of input parameters, and
passes that to the subroutine. the subroutine fills in everything
and returns it to the caller. The programmer then extracts the
parts that he needs as he needs them.

Here is an example of pseudocode.

module work
type output_type
integer :: out1
real :: out2
...
logical :: out20
end type output_type
contains
subroutine sub( in1, in2, output )
integer, intent(in) :: in1, in2
type(output_type), intent(out) :: output
...compute the output components here...
end subroutine sub
end module work

program do_work
use work
integer :: in1, in2
type(output_type) :: output_a, output_b
...set in1 and in2 here...
call sub( in1, in2, output_a )
...set in1 and in2 here...
call sub( in1, in2, output_b )

...at this time output_a has all possible outputs associated
with the first set of inputs, and output_b has all possible
outputs associated with the second set of inputs...
end program do_work

This approach avoids many of the issues related to optional
arguments (such as avoiding any references that are not protected
with present() tests). It also allows the programmer to store as
many outputs as he needs simultaneously without relying on the
module or the subroutine to store them internally. Instead of scalar
output_a and output_b, he might need an array output(1:numout) of
more general size to store the outputs. That is, the subroutines in
the work module above do not need to store any internal state, and
the module does not need to store any global variables, the
programmer of the calling program controls that aspect. This might
allow you to declare sub() as pure, which is an advantage in some
contexts.

This is, BTW, the opposite of the object-oriented approach to this
problem. In an OO approach, the module would hold all of the
outputs, and there would be get/set routines to control the access
of the components to the calling program. Sometimes this is a good
approach, but you can see that it also has some disadvantages in
cases like this.

$.02 -Ron Shepard

Richard Maine

unread,
Apr 7, 2012, 12:09:34 PM4/7/12
to
Phillip Helbig---undress to reply <hel...@astro.multiCLOTHESvax.de>
wrote:

> ...optional output parameters...

First note a terminology quibble. They are arguments - not parameters. A
parameter in Fortran is a named constant.

> If the output parameters don't depend on anything
> except the input parameters (i.e. they are "pure"), then, assuming they
> are SAVEd parameters, one doesn't have to calculate them again. So,
> would pseudocode like the following work?
>
> IF (same input parameters as last time) THEN RETURN

No. Not at all. First, your assumption is never true. Arguments cannot
have the SAVE attribute at all. The initial value (if any) of a dummy
argument never depends on that dummy argument in a previous invocation.
(Well, there used to be some old and nonstandard features sort of
related to that, but nothing close to standard or in any compiler you
are likely to be using). If it is an intent(out) argument, then it
becomes undefined when the procedure starts, possibly then modified by
default initialization if applicable.

If it is intent(inout), then it gets its initial value from that of the
actual argument. If the actual argument is the same as the previous
call, that might have the effect you are looking for. But it would also
make the correctness of the procedure's internal implementation depend
on details of how it was called. Sounds error-prone to me. And confusing
to document, as your "output" argument is no longer actually that. (See
notes below about clarity and correctness relative to optimization).

You could possibly save the results in a local variable, which you then
copy to the dummy argument as appropriate, but note that makes the
routine impure and might be counter-productive because of that.

> Could this be avoided by the following pseudocode?
....
> CONTAINS
>
> internal1 = blabla
> internal2 = blabla

I'm afraid I have no idea what this pseudo-code implies. You can't put
executable code after a CONTAINS - only procedures (or bindings in a
derived type). If your pseudo-contains is something other than that of
Fortran, I can't guess what it is.

> In other words, if variables are module variables rather than local
> variables in the subroutine, does this prevent them from being optimized
> away?
>
> If not, would separate compilation prevent them from being optimized
> away? That is, first compile the module containing the subroutine then
> link it to a separately compiled caller program?

I don't think I understand those questions. You appear to be talking
about "optimizations" that your code implements rather than compiler
optimizations. In that case, I don't quite see why the question. The
code does... well... whatever it does. I don't see how anything would
"prevent" it from doing so. I suppose you might be asking whether SAVE
works for module variables. Yes, it does. In fact, there have been
proposals to make all module variables SAVEd by default. That part isn't
so (at least as of f2003 - I can't speak to f2008), but you can
certainly declare them to be SAVE'd explicitly.

*HOWEVER*...

My initial impression of this whole idea is that it seems like the kind
of micro-optimization that is often counter-productive. Concentrate
first on making code clear (and correct). All of this is highly
counter-productive in that regard. Just calculating the outputs based on
the inputs is clear and simple. Getting the output values from somewhere
that they might or might not have been saved is not nearly so simple,
even before getting into questions about making it work correctly.

There are certainly cases where saving previously computed values can be
a big win. Heck, that's the basis of some data compression schemes
(including some I have done myself). In that case, the saving is more in
file size and communication bandwidth than directly in computation time
(it tends to cost in computation time).

But such schemes pretty much inherently hurt you in clarity, simplicity,
and also in getting them right. There's an old, old adage about
optimization, which lists a lot of things to do before optimization.
Those things to do before optimization include making the code clear and
correct. That adage is as important now as it ever was... and some
people ignore it now, just as they always have. The result of ignoring
it so often is code that is unclear, buggy.... oh, and sometimes not
actually faster anyway.

I would certainly say that the kind of "optimization" you are talking
about should not at all be your usual practice. *IF* you have concluded
that the computation some particular variable is a performance problem
significant enough to merit special treatment, then and only then might
it be worth looking at tricks like this for that particular variable.
And in that case, you should also be looking at other approaches.

--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain

Phillip Helbig---undress to reply

unread,
Apr 7, 2012, 12:51:05 PM4/7/12
to
In article <ron-shepard-45D7...@news60.forteinc.com>, Ron
Shepard <ron-s...@NOSPAM.comcast.net> writes:

> 1) the programmer calling the subroutine knows which outputs depend
> on which inputs.

He should, but if not, this is trapped and an appropriate message
displayed.

> 2) all outputs are computed in any case, and the issue is just which
> ones the programmer requests to see.

Right.

> In these cases, an alternative approach is to define a derived type
> that contains all of the outputs.

> This approach avoids many of the issues related to optional
> arguments (such as avoiding any references that are not protected
> with present() tests).

> This is, BTW, the opposite of the object-oriented approach to this
> problem.

This is a solution to my problem. The only real disadvantage is that it
requires the user to declare some huge unwieldly derived type even if he
uses only a few components. (A minor disadvantage is that if---as I
suspect---my module is compiled separately then linked to a user-written
calling program, then any change in the number of computed outputs would
require modifying and recompiling the calling program instead of just
relinking.)

Phillip Helbig---undress to reply

unread,
Apr 7, 2012, 1:21:03 PM4/7/12
to
In article <1ki69w6.mvgu70sv4bl8N%nos...@see.signature>,
nos...@see.signature (Richard Maine) writes:

> Phillip Helbig---undress to reply <hel...@astro.multiCLOTHESvax.de>
> wrote:
>
> > ...optional output parameters...
>
> First note a terminology quibble. They are arguments - not parameters. A
> parameter in Fortran is a named constant.

Right. As it says in the Klingon Programmer's Guide: Routines don't
have parameters, they have arguments---and they always win them!
(Another gem: Klingon software is not released---it escapes!)

> > IF (same input parameters as last time) THEN RETURN
>
> No. Not at all. First, your assumption is never true. Arguments cannot
> have the SAVE attribute at all.

> If it is an intent(out) argument, then it
> becomes undefined when the procedure starts, possibly then modified by
> default initialization if applicable.

Right.

> If it is intent(inout), then it gets its initial value from that of the
> actual argument. If the actual argument is the same as the previous
> call, that might have the effect you are looking for. But it would also
> make the correctness of the procedure's internal implementation depend
> on details of how it was called. Sounds error-prone to me.

I agree.

> You could possibly save the results in a local variable, which you then
> copy to the dummy argument as appropriate, but note that makes the
> routine impure and might be counter-productive because of that.

That's what I was really thinking of: On any call when input parameters
are new, compute everything. If the same input parameters are specified
in two subsequent calls, just GOTO the end where local variables are
copied to dummy arguments.

> > Could this be avoided by the following pseudocode?
> .....
> > CONTAINS
> >
> > internal1 = blabla
> > internal2 = blabla
>
> I'm afraid I have no idea what this pseudo-code implies. You can't put
> executable code after a CONTAINS - only procedures (or bindings in a
> derived type). If your pseudo-contains is something other than that of
> Fortran, I can't guess what it is.

Sorry, too brief:

CONTAINS
SUBROUTINE CALCULATE STUFF
internal1 = blabla
internal2 = blabla
END SUBROUTINE CALCULATE STUFF

So, only call CALCULATE_STUFF if the input arguments have changed.

> > In other words, if variables are module variables rather than local
> > variables in the subroutine, does this prevent them from being optimized
> > away?
> >
> > If not, would separate compilation prevent them from being optimized
> > away? That is, first compile the module containing the subroutine then
> > link it to a separately compiled caller program?
>
> I don't think I understand those questions.

PROGRAM TEST
REAL :: X
CALL SUB(A=X)
END PROGRAM TEST
MODULE MOD
CONTAINS
SUBROUTINE SUB
REAL :: A, B
A = blabla
B = blabla
END SUBROUTINE SUB

In other words, optimizing compilation of the above might result in B
not being computed at all. I suppose that's not a problem, though.
With separate compilation of the caller and the module, then presumably
this can't happen. If the caller first wants A and then later B, but B
can be calculated more quickly if A is known, then I have to somehow
save the value of A for computing B on the later call. So, A would have
to have the SAVE attribute (whether a local variable or a module
variable).

In my case, calculating the basic quantity is orders of magnitude more
expensive than various related quantities. The user will usually only
want one or two related quantities per call. So, saving the basic
quantity is worth doing since I don't want to recalculate it if the
input parameters (on which it depends) are the same as on the last call.

So, if the input parameters are different, calculate everything. If
not, notice this and define the output parameters based on SAVEd
internal variables from the previous call. Presumably I can just define
them all and don't need any IF (PRESENT(X)); the user sees only those he
requests.

Another approach might be an initialization routine which calculates
everything and has only input parameters. Another routine would have
only optional output parameters based on shared data with the
initialization routine. This would be functionally equivalent, but I
should still check if the input parameters have changed. The only
advantage would be, at the expense of two calls instead of one, making
it less easy for the user to call with the same input parameters. First
idea:

REAL :: X, Y
CALL SUB(INPUT1,INPUT2,A=X)
CALL SUB(INPUT1,INPUT2,B=Y)

Second idea:

REAL :: X, Y
CALL INIT(INPUT1,INPUT2)
CALL VALUES(A=X)
CALL VALUES(B=Y)

Of course, the user could do:

REAL :: X, Y
CALL INIT(INPUT1,INPUT2)
CALL VALUES(A=X)
CALL INIT(INPUT1,INPUT2)
CALL VALUES(B=Y)

but this mistake should be less common (I would still want to catch it,
though).

Another approach would be to make the INPUT arguments optional and only
compute stuff anew if they are present. But the user could still
specify them unchanged every time and I will still have to trap it.

dpb

unread,
Apr 7, 2012, 1:36:13 PM4/7/12
to
I'd recommend thinking of a supervisory routine which calls others to
eliminate/minimize code duplication and possibly multiple interface
routines as optional.

Also, structures at least internally minimize argument lists; perhaps
they're also of benefit to the top level routine as well.

--

dpb

unread,
Apr 7, 2012, 2:08:31 PM4/7/12
to
On 4/7/2012 9:45 AM, Phillip Helbig---undress to reply wrote:
...

>> what is an
>> "optimized variable"?
>
> I meant a variable the calculation of which has been optimized away.

If there's any path during execution by which the variable is/could be
used that can't(+) happen (or if the compiler can't tell).

I don't see how your routines could be functional at all if there is no
path by which any one of the outputs could be computed/used so imo the
idea of the compiler removing code is moot (unless you have actual dead
code which isn't the intent afaict).

Just because under some specific combination of inputs not every
possible code branch in a routine is executed doesn't mean that code can
be optimized out--you seem to be thinking of optimization occurring at
runtime instead like it might in an interpreted language.

(+) Barring a buggy compiler, of course...

--

Ron Shepard

unread,
Apr 7, 2012, 2:30:34 PM4/7/12
to
In article <jlpr9p$rql$1...@online.de>,
hel...@astro.multiCLOTHESvax.de (Phillip Helbig---undress to
reply) wrote:

> In article <ron-shepard-45D7...@news60.forteinc.com>, Ron
> Shepard <ron-s...@NOSPAM.comcast.net> writes:
>
> > 1) the programmer calling the subroutine knows which outputs depend
> > on which inputs.
>
> He should, but if not, this is trapped and an appropriate message
> displayed.
>
> > 2) all outputs are computed in any case, and the issue is just which
> > ones the programmer requests to see.
>
> Right.
>
> > In these cases, an alternative approach is to define a derived type
> > that contains all of the outputs.
>
> > This approach avoids many of the issues related to optional
> > arguments (such as avoiding any references that are not protected
> > with present() tests).
>
> > This is, BTW, the opposite of the object-oriented approach to this
> > problem.
>
> This is a solution to my problem. The only real disadvantage is that it
> requires the user to declare some huge unwieldly derived type even if he
> uses only a few components.

I'm not sure who the "user" is in your sentence. If it is the
programmer writing the calling program, then no, the user does not
have to declare any huge and unwieldly derived types. That is the
point of the example code that I gave. The calling program was very
simple.

> (A minor disadvantage is that if---as I
> suspect---my module is compiled separately then linked to a user-written
> calling program, then any change in the number of computed outputs would
> require modifying and recompiling the calling program instead of just
> relinking.)

Recompiling yes, but modifying no. That is the point of having the
derived type defined in the module. If the derived type is changed
in the module, say a new component is added, then the calling
program only needs to be recompiled and linked. There are no other
changes required, in particular there are no source code changes
that are required. Unless the calling program needs to access that
new component, and then of course the calling program would need to
be changed. There is no way to avoid that, of course.

Also, if you ever decide to remove a component, then you remove it
in the definition in the module, not in numerous definitions
throughout your source code. Then when you recompile your code, you
know at compile time where all the old references are to that
component, and you can quickly change the calling source code
accordingly. You do not have to wait until runtime for these
invalid references to occur. That is one of the nice features about
how these things work in fortran, the compiler does a lot of the
work for you like that.

$.02 -Ron Shepard

glen herrmannsfeldt

unread,
Apr 7, 2012, 3:34:28 PM4/7/12
to
Phillip Helbig---undress to reply <hel...@astro.multiclothesvax.de> wrote:
> I suppose most people use optional output parameters to avoid executing
> code which is not needed or even impossible (depending on optional input
> parameters, say). Another use is to make things easier for the user,
> e.g. a subroutine with many optional output parameters and the user
> specifies only those he is interested in. Of course, this could
> correspond to the first use, i.e. only those specified are calculated.
> However, presumably one could always calculate all output parameters and
> only those the user specified are returned. Right?

> If the above is true, one could have the case where there are two calls
> to the subroutine with the same input parameters but different optional
> output parameters. If the output parameters don't depend on anything
> except the input parameters (i.e. they are "pure"), then, assuming they
> are SAVEd parameters, one doesn't have to calculate them again. So,
> would pseudocode like the following work?

> IF (same input parameters as last time) THEN RETURN

> I'm not interested in whether it works with my compiler (unless my
> compiler violates the standard in this respect) but whether the standard
> implies that one can rely on this behaviour.

No, as others have said, that doesn't work.

You could save copies of the values to be returned, assign them
to the appropriate dummy arguments, and then use your IF.

I beleive, though, that the usual way to do this would be to put
everything in a structure, call the routine with that structure,
in which case it would calculate the appropriate variables, and
leave others with the previous values.

An example of a problem that might work that way: say you have
to solve a system of equations like Ax=B (A,B matrix and vector x.)
One way is to do LU decomposition on A, then compute x from that
and the appropriate B. If you then change B and want a new x,
from the same A, you don't need to recompute the LU decomposition.

(There are many problems with a similar need, and where the compute
intense part doesn't need to be redone.)

If you put A, x, B, and some other variables in a structure,
initialize it appropriately, and then call the routine, then it
can compute what needs to be computed, and return the appropriate
results.

The object-oriented way (which doesn't need to use any OO features
of the language) would be to have SetA, SetB, and GetX routines.
You call SetA which stores a copy of (or pointer to) A, SetB
stores a copy of (it is smaller) B, and then GetX checks to see
if the LU decomposition has been done. If not, allocate space for
the result, compute it, and set a variable indicating that it has
been done. Then compute and return X. If you call SetB and then
GetX again, it won't have to recompute the LU decomposition.
(Storing it separately reduces problems with changing A when you
have a pointer to it.) SetA sets the variable to indicate that
the LUD hasn't been done, when a new A is needed.

Instead of separate routines as described, you could have one big
routine that uses various arguments to indicate what needs to be done.
It is probably better, though to have separate routines.

The SetA and SetB routines, though, should be fairly small and fast,
with all the work being done in GetX.

In a more complicated case, Get routines would call others to do
those parts of the computation, which would only be done when needed.

In most cases, only the one structure and the variable to be set
or got are needed as arguments. The call should be pretty fast when
nothing needs to be computed.

Hope this helps.

-- glen

Rafik Zurob

unread,
Apr 7, 2012, 7:32:50 PM4/7/12
to
Hi Phillip

> PROGRAM TEST
> REAL :: X
> CALL SUB(A=X)
> END PROGRAM TEST
> MODULE MOD
> CONTAINS
> SUBROUTINE SUB
> REAL :: A, B
> A = blabla
> B = blabla
> END SUBROUTINE SUB
>
> In other words, optimizing compilation of the above might result in B
> not being computed at all. I suppose that's not a problem, though.
> With separate compilation of the caller and the module, then presumably
> this can't happen. If the caller first wants A and then later B, but B
> can be calculated more quickly if A is known, then I have to somehow
> save the value of A for computing B on the later call. So, A would have
> to have the SAVE attribute (whether a local variable or a module
> variable).

I assume you meant SUBROUTINE SUB(A, B) above. If so, it doesn't really
matter whether you have module MOD in the same file as program TEST or in a
different file. TEST and MOD are separate compilation units even if they
appear in the same file according to the standard. I don't think any
compiler would remove "B = blabla" from module procedure SUB based on
whether they module and program are in the same file. The reason is that
your program might contain another compilation unit that uses MOD, calls
SUB, and uses B. i.e. SUB is visible to any compilation unit that uses MOD,
not just program TEST, and so the compiler/linker can't get rid of B without
looking at your whole program (during link time), not just when compiling a
single file. I can think of a couple of situations that might get rid of B,
and both won't be affected by whether the module and the program are in the
same file or different files. They are:
1. SUB gets inlined into TEST. B is eliminated from the inlined copy
of SUB. Since we're talking about inlining between compilation units,
you'll need interprocedural analysis, and that doesn't care about which file
SUB is from.
2. SUB does not get inlined, but the optimizer, after looking at your
whole program, determines that argument B is never used and gets rid of it.
This also requires interprocedural analysis.

I don't know which compiler you're using. If you're using XL Fortran, this
is done at -qipa=level=2.

Regards

Rafik
Visit the Fortran Cafe at http://ibm.com/rational/cafe/


Rafik Zurob

unread,
Apr 7, 2012, 8:22:26 PM4/7/12
to
Hi Phillip

> In my case, calculating the basic quantity is orders of magnitude more
> expensive than various related quantities. The user will usually only
> want one or two related quantities per call. So, saving the basic
> quantity is worth doing since I don't want to recalculate it if the
> input parameters (on which it depends) are the same as on the last call.
>
> So, if the input parameters are different, calculate everything. If
> not, notice this and define the output parameters based on SAVEd
> internal variables from the previous call. Presumably I can just define
> them all and don't need any IF (PRESENT(X)); the user sees only those he
> requests.
>

Assuming that you don't want to compute all values at once, but at the same
time you want to reuse the computation of the basic value, I suggest the
following:

module mod
implicit none
type mytype
real, private :: a, b, c, input
! a is the quantity that takes a long time to compute
logical :: is_initialized = .false.
logical computed_a
contains
procedure get_a
procedure get_b
procedure get_c
end type

interface mytype
procedure mytype_constructor
end interface

contains
real function get_a(this)
class(mytype) :: this
if (.not. this%is_initialized) error stop
! compute a...
this%a = this%input * 10.0 ! example
this%computed_a = .true.
get_a = this%a
end function

real function get_b(this)
class(mytype) :: this
real tmp_a
if (.not. this%is_initialized) error stop
if (.not. this%computed_a) tmp_a = this%get_a()
! do the work to compute b based on a
this%b = this%a - 5.0 ! example
get_b = this%b
end function

real function get_c(this)
class(mytype) :: this
real tmp_a
if (.not. this%is_initialized) error stop
if (.not. this%computed_a) tmp_a = this%get_a()
! do the work to compute c based on a
this%c = this%a + 5.0 ! example
get_c = this%c
end function

function mytype_constructor(input)
type(mytype) mytype_constructor
real, intent(in) :: input
mytype_constructor%input = input
mytype_constructor%is_initialized = .true.
mytype_constructor%computed_a = .false.
end function
end module

program test
use mod
type(mytype) x
x = mytype(input=5.0)
! The following computes a and b
print *, x%get_b() ! prints 45.0
! The following reuses the computed value for a, and computes c
print *, x%get_c() ! prints 55.0
x = mytype(7.0)
! The following computes a and c
print *, x%get_c() ! prints 75.0
end program

The above does not use any SAVE'd variables or optional arguments.
Everything is an automatic on the stack and is thread-safe. It also allows
you to have several objects at the same time. e.g.

type(mytype) x, y
x = mytype(input=5.0)
y = mytype(input=7.0)
print *, x%get_b() + y%get_b() ! prints 110.0

If you want, you can also make the user-defined structure constructor
compute a from input right away instead of deferring it until one of the
get_* bindings is called. I didn't do that above because I didn't know if
you had any operations that don't depend on a.

I wrote a blog entry last September about user-defined structure
constructors in the Fortran Cafe at
https://www.ibm.com/developerworks/mydeveloperworks/blogs/b10932b4-0edd-4e61-89f2-6e478ccba9aa/entry/object_oriented_fortran_user_defined_constructors2?lang=en

BTW, the program above compiles and executes successfully with XL Fortran.

Rafik Zurob

unread,
Apr 7, 2012, 8:29:20 PM4/7/12
to
Hi Richard

> I suppose you might be asking whether SAVE
> works for module variables. Yes, it does. In fact, there have been
> proposals to make all module variables SAVEd by default. That part isn't
> so (at least as of f2003 - I can't speak to f2008), but you can
> certainly declare them to be SAVE'd explicitly.

Yes, this is part of F2008.

Regards

Rafik


Barry W Brown

unread,
Apr 7, 2012, 10:38:24 PM4/7/12
to
On Saturday, April 7, 2012 6:54:40 AM UTC-5, Phillip Helbig---undress to reply wrote:
> I suppose most people use optional output parameters to avoid executing
> code which is not needed or even impossible (depending on optional input
> parameters, say). Another use is to make things easier for the user,
> e.g. a subroutine with many optional output parameters and the user
> specifies only those he is interested in. Of course, this could
> correspond to the first use, i.e. only those specified are calculated.
> However, presumably one could always calculate all output parameters and
> only those the user specified are returned. Right?
>
I use this style for certain sets of problems of the form
f(a,b,c,which)= d
in which three of the four arguments a..d must be specified and the other
calculated (hence all arguments must be OPTIONAL) with which denoting the
one to be calculated. For example
cumulative_normal_distribution(z,mean,stddev,which) = alpha
I use this style because it seems easier to use than separate routines for
each case.

I would never bother with using this technique for optimizing code. Optimizing
code (but not the algoritm) is the job of the compiler. In any case most of
my code runs in a fraction of a second and I will get no kudos for making it
faster.

Phillip Helbig---undress to reply

unread,
Apr 8, 2012, 5:58:59 AM4/8/12
to
> I use this style for certain sets of problems of the form
> f(a,b,c,which)= d
> in which three of the four arguments a..d must be specified and the other
> calculated (hence all arguments must be OPTIONAL) with which denoting the
> one to be calculated.

If three are specified, you know which one is missing, so why do you
need to specify it? If all are optional, in most cases you have to use
keywords anyway.

Barry W Brown

unread,
Apr 8, 2012, 10:37:41 AM4/8/12
to
You are right and I posted incorrectly (it was late at night). The actual
calling sequence is
calculation(a,b,c,d,which)
so no parameters HAVE to be specified by name. The answer is returned in
argument(which).

Our code is posted on
biostatistics.mdanderson.org/SoftwareDownload
See cdflib and stplan for many instances of this style.
0 new messages