 |
forums.silverfrost.com Welcome to the Silverfrost forums
|
View previous topic :: View next topic |
Author |
Message |
mecej4
Joined: 31 Oct 2006 Posts: 1899
|
Posted: Sat Jul 25, 2015 5:19 pm Post subject: Somewhat misleading compiler error messages |
|
|
The following subprogram has two errors. The compiler (7.20) catches both in due course, but the error messages could be worded better.
Code: | subroutine jac(ndata,t,alf,jac) ! <<=== should be xjac(...)
implicit none
integer, intent(in) :: ndata
double precision, intent(in) :: t(ndata),alf(3)
double precision, intent(out) :: jac(ndata,2)
!
!local variables
!
double precision :: v,tmpv
integer i
!
do i=1,ndata
v=t(i)
jac(i,1)=-v(i)*exp(-alf(1)*t(i)) ! <<=== should be =-v*exp(...
tmpv=exp(-((v-alf(2))/alf(3))**2)
jac(i,2)=2*(t(i)-alf(2))/(alf(3)*alf(3))*tmpv
end do
return
end subroutine
|
When the compiler sees the first line with the error, it says
Code: | 0001) subroutine jac(ndata,t,alf,jac) ! <<=== should be xjac(...)
*** JAC has been used more than once as a dummy argument
|
and aborts the compilation. In fact, "JAC" has been used as the subprogram name and a dummy argument, not "more than once as a dummy argument".
Once we fix the first error (by naming the subprogram as "xjac") and recompile, the second error causes the compiler to say
Quote: |
0014) jac(i,1)=-v(i)*exp(-alf(1)*t(i)) ! <<=== should be =-v*exp(...
*** V has been used in the left hand side of a previous assignment, yet appears to be a function name here
0015) tmpv=exp(-((v-alf(2))/alf(3))**2)
*** V is a routine name, and cannot be used in this context
|
The first message is appropriate. The second is not, because v was declared as a variable before it was used by mistake as a function name. I feel that the compiler should not override the correct explicit declaration and replace it internally by an implied declaration of v as a function. In other words, the compiler should not change its notion of the type of a variable on the basis of a statement that it has already rejected as being incorrect. |
|
Back to top |
|
 |
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 8217 Location: Salford, UK
|
Posted: Mon Jul 27, 2015 8:47 am Post subject: |
|
|
Thank you. I have made a note of this issue. |
|
Back to top |
|
 |
LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2403 Location: Yateley, Hants, UK
|
Posted: Mon Jul 27, 2015 2:12 pm Post subject: |
|
|
Mecej4
I found your post fascinating on several levels, not least because FTN95 usually has excellent diagnostics, and it left me wondering about some of the compilers I have used in the past, that would have said �Error� and little more, perhaps an inscrutable number, or if one was lucky the hint �V� or an indication where the compiler had got to in the offending line. Presumably FTN95 had seen �v(i)� and was prepared to believe that it was a function from then on.
I�m quite happy to accept that this is a cut down and �tweaked� example to show the principle you observed. However, it made me think about compiler optimisation, particularly regarding common subexpressions and loop invariant things. Does, for example, partly replacing common subexpressions foul up the optimiser in FTN95? Do generic functions execute more slowly than type-specific ones? Is the convenience of referring to array elements multiple times slower than adding an assignment, so that a single variable can be used instead?
I might add for good measure the questions how often is the routine executed, and therefore do any time savings really matter, and then finally, can the optimiser do a better job than the programmer? I have to admit that I have avoided /OPT but still find FTN95 acceptably fast, and also that I don�t derive huge confidence from the description of optimisation in FTN95.CHM
As many of my programming habits were formed in the distant past (and this also implies being before optimising compilers), then I would probably be looking to replace �raise to the power� by multiplication, take care of common subexpressions myself, remove loop invariant calculations and so on. Moreover, as my programming habits use COMMON blocks instead of subroutine arguments to keep stack sizes as small as possible (so no SAVE needed, but INTENTs now not possible), the short array ALF might be passed to this routine as A1, A2 and A3 so that I don�t have assignments to local variables to take care of.
Just so that you can see how much of a museum piece I am, I�ve changed the constant 2 and the EXP to type-specific forms, and converted the DO loop to traditional form, as an outdent is as good as indents as far as I�m concerned, although I left the lower case for comparability. Was changing the 2 a sensible thing, as could FTN95 arguably do it a different way? (Say keeping it in a register and doing an add). Would ordering the parts of the assignment improve efficiency?
Code: | common /alphas/ a1, a2, a3
a3sq = a3*a3
do 20 i=1,ndata
ti = t(i)
coef1 = ti - a2
coef2 = coef1 / a3sq
djac(i,1) = -ti * dexp(-a1*ti)
djac(i,2) = 2.0D0 * coef2 * dexp(-coef1*coef2)
20 continue |
Assuming that I have translated the intention of your original Fortran code properly, I find that the addition of a lot of white space and the removal of a forest of brackets helps me enormously, and for that matter, helps everything fit easily within the 72 column card format limit. Certainly doing the common subexpression stuff myself makes the logic a bit more convoluted, but the removal of so many brackets evens the score.
Now you see how we dinosaurs survive in the Lost World of northern Hampshire even if we are extinct elsewhere!
I wonder if instead of A3SQ I should have calculated its inverse, so that inside the DO loop I could have a multiply and not a divide? Even if I coded these two assignments as statement functions, I imagine that I would have precomputed A3SQ.
None of this (of course) implies any criticism, and if a compiler is going to give generally helpful diagnostics it might as well give correct ones instead of misleading ones. Perhaps looking at this from my own particular programming perspective has shown me why I seemed to get so little out of /OPT when I tried it last ... maybe my programming habits defeat some of the optimisations built in to FTN95.
Eddie |
|
Back to top |
|
 |
mecej4
Joined: 31 Oct 2006 Posts: 1899
|
Posted: Mon Jul 27, 2015 10:14 pm Post subject: |
|
|
Eddie, the Silverfrost Forum server has been going out of service quite often today. A few hours ago I replied to your post, but the server disconnected after a few minutes and I could not reconnect. However, I had saved my text, and here it is. If you edited your post in the meantime, my answers may seem a bit odd.
I'll be happy to provide the entire source code, if you wish to do your own tests with hand optimization versus compiler optimization.
__________________________
The code that I showed is a stripped-down version of a routine that calculates the Jacobian matrix of a vector function, see http://www.itl.nist.gov/div898/strd/nls/data/gauss1.shtml . As you can see from that page, every derivative expression (w.r.t. the beta coefficients, which are named 'alf' in the code) will contain a reference to EXP(). The subroutine may get called about a hundred times, depending on the starting values supplied for the beta coefficients. Each run of the subroutine requires 1,250 evaluations of EXP(). Therefore, it is worthwhile to save and reuse the values of the EXP() functions when feasible.
The FTN95 optimizer is rather basic. Given the code below, I find that FTN95 /OPT /P6 produces code which makes 5 calls to EXP during each execution of the loop. In other words, it does not do common sub-expression elimination for this code, with which it could have got by with only 3 calls to EXP per loop run. In summary, we are talking about a 40 percent reduction, from 125,000 to 75,000 calls to EXP(), and all this for one data set.
Code: |
subroutine jacfun(ndata,na,nb,alf,icount,jac)
use globdata, only : t
implicit none
integer na,nb
double precision :: jac(ndata,*),alf(na),tmpv
integer i,ndata,icount
!
do i=1,ndata
jac(i,1)=-t(i)*exp(-alf(1)*t(i))
jac(i,2)=2*(t(i)-alf(2))/(alf(3)*alf(3))*exp(-((t(i)-alf(2))/alf(3))**2)
jac(i,3)=2*((t(i)-alf(2))/alf(3))**2/alf(3)*exp(-((t(i)-alf(2))/alf(3))**2)
jac(i,4)=2*(t(i)-alf(4))/(alf(5)*alf(5))*exp(-((t(i)-alf(4))/alf(5))**2)
jac(i,5)=2*((t(i)-alf(4))/alf(5))**2/alf(5)*exp(-((t(i)-alf(4))/alf(5))**2)
end do
return
end subroutine
|
Whether to do hand optimization or rely on the compiler's capabilities is a complex subject. There is a toss-up between readability, likelihood of making typographical errors in some copies of common expressions, object code efficiency, and the compiler's sophistication. At the Fortran level, there are compilers that one can trust to do a great job with optimization, so one codes for readability. At the machine level, few people can write assembly code that can beat code generated by a modern optimizing compiler.
Last edited by mecej4 on Tue Jul 28, 2015 3:45 am; edited 2 times in total |
|
Back to top |
|
 |
LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2403 Location: Yateley, Hants, UK
|
Posted: Tue Jul 28, 2015 12:02 am Post subject: |
|
|
Mecej4 - the site has been flakey here today too. Many of my questions are at least semi-rhetorical, and don't call for definitive answers. Thanks for your reply: I'm naturally disappointed to hear that FTN95's /OPT is poor.
I'm sure that in principle, machine optimisation ought to beat hand optimisation. I used to test my source codes against every compiler I could lay my hands on, most of which had no optimisation - and hand optimisation always beats that! (And it works with any compiler).
Eddie |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Tue Jul 28, 2015 6:22 am Post subject: |
|
|
This discussion about /opt has reminded me that I did not complete my reporting of the polyhedron benchmark tests.
My test results showed a number of issues, including.
1) I did identify issues about addressing memory in a sequential manner, where poor array subscript order can explode run time. (coding to have good cache usage)
2) I also identified an approach of using /timing or /profile to identify where there is most computation and review that code for better savings, such as should identify the exp() issue above.
3) finally there were a few code examples, such as chemical calculations, where the were large lists of equations, which cleaning up became both a laborious job and also risked introducing code (typing) errors for code that was previously tested.
While fixing issues of type 1 or 2 can be rewarding, it is the type 3) issues where a good /opt is really appreciated.
I have demonstrated there are tools with FTN95 which assist in types 1) and 2), which I have used for many years to develop coding approaches and libraries of routines that run efficiently.
However for type 3, it is often good to type the code, as the algorithm is documented or published and let the compiler optimise. Auditing this code is also an important issue, so changes should be minimal.
(Lately I have seen some bug reports where registers can run out if the line of code has too complex an equation definition, which is another problem with this type of code. Changing compilers can throw up problems that can be difficult to check, especially if there are lots of different rules that are rarely tested.)
My experience with FTN95 /opt has been to only use it in well tested code, as it does not always work the same way and the reasons for this can be numerous, rather than just the fault of the compiler. (eg register vs variable storage, random number generators or trig functions can cause small changes that can change the result of IF tests)
I don't think we can expect significant changes to FTN95 /opt, but can enjoy the error reporting features, such as /undef, /check or even /debug used with SDBG.
John |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Wed Jul 29, 2015 1:56 am Post subject: |
|
|
The operation count for the above code example could be improved as:
Code: | ! original loop
do i=1,ndata
jac(i,1) = -t(i) * exp(-alf(1)*t(i))
jac(i,2) = 2* (t(i)-alf(2))/(alf(3)*alf(3)) * exp(-((t(i)-alf(2))/alf(3))**2)
jac(i,3) = 2*((t(i)-alf(2))/ alf(3))**2/alf(3) * exp(-((t(i)-alf(2))/alf(3))**2)
jac(i,4) = 2* (t(i)-alf(4))/(alf(5)*alf(5)) * exp(-((t(i)-alf(4))/alf(5))**2)
jac(i,5) = 2*((t(i)-alf(4))/ alf(5))**2/alf(5) * exp(-((t(i)-alf(4))/alf(5))**2)
end do
|
Could be improved to Code: | do i=1,ndata
talf2 = t(i)-alf(2)
talf4 = t(i)-alf(4)
jac(i,1) = -t(i) * exp (-alf(1)*t(i))
jac(i,2) = 2 * (talf2)/(alf(3)*alf(3)) * exp (-((talf2)/alf(3))**2)
jac(i,3) = 2 *((talf2)/ alf(3))**2/alf(3) * exp (-((talf2)/alf(3))**2)
jac(i,4) = 2 * (talf4)/(alf(5)*alf(5)) * exp (-((talf4)/alf(5))**2)
jac(i,5) = 2 *((talf4)/ alf(5))**2/alf(5) * exp (-((talf4)/alf(5))**2)
end do
|
and further improved to Code: | do i=1,ndata
!
e_alf1 = exp (-alf(1)*t(i))
!
talf2 = t(i)-alf(2)
talf23 = ( talf2 / alf(3) )**2
e_alf2 = exp (-talf23)
!
talf4 = t(i)-alf(4)
talf45 = ( talf4 / alf(5) )**2
e_alf4 = exp (-talf45)
!
jac(i,1) = -t(i) * e_alf1
jac(i,2) = 2 * talf2 / alf(3)**2 * e_alf2
jac(i,3) = 2 * talf23 / alf(3) * e_alf2
jac(i,4) = 2 * talf4 / alf(5)**2 * e_alf4
jac(i,5) = 2 * talf45 / alf(5) * e_alf4
end do
|
or further ...
It is worth considering how this example demonstrates mecej4's comment Quote: | There is a toss-up between readability, likelihood of making typographical errors in some copies of common expressions, object code efficiency, and the compiler's sophistication. |
John |
|
Back to top |
|
 |
LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2403 Location: Yateley, Hants, UK
|
Posted: Wed Jul 29, 2015 11:02 am Post subject: |
|
|
John,
You beat me to it, as my yesterday was spent gazing into a set of pits looking for subtle features in the soil and hence the history of landsliding on the site instead of pondering niceties of Fortran.
I would certainly have gone further and replaced raising to a power 2 by multiplications AND some of the array elements to single variables. I note that you have inserted extra white space, which I find extremely valuable in making things readable. My style, which may not work for anyone else, is exhibited in the code I posted.
I think I've said before that I bow before Mecej4's superior knowledge of Fortran standards, but as far as the forest of brackets in the first and second examples is concerned, with no added white space, I find coding like this verging on the unreadable. In code I wrote myself I would be looking for the common factors to eliminate long before I wrote a line of Fortran, and indeed I find that task much easier with conventional mathematical notation than from one Fortran layout to another. However, you did it perhaps the way I would have by first dividing it up with those white spaces.
In fact, learning that alfa is really beta left me puzzled why the name alfa was chosen in the first place. I would consider beta to be a better variable name as it doesn't have the alternate spelling that alpha-alfa has!
Eddie |
|
Back to top |
|
 |
mecej4
Joined: 31 Oct 2006 Posts: 1899
|
Posted: Wed Jul 29, 2015 12:23 pm Post subject: Re: |
|
|
LitusSaxonicum wrote: | In fact, learning that alfa is really beta left me puzzled why the name alfa was chosen in the first place. I would consider beta to be a better variable name ...
|
The NIST page with the problem definition uses β for the model parameters, and this is standard notation in statistics. The documentation of Ch.9.3 of the subroutine library, JPL-MATH77, splits the parameter set into [α, β], the first part containing those parameters that appear nonlinearly in the residuals, and the second part the rest.
The code fragment was not written for easy verification, nor for efficiency or for presentation to HMQE. If "s�chsische" can be written "Saxonicum", it should be tolerable for me to take the last syllable of αβ -> "al.pha.bet" -> "bet".
As to the forest of parentheses, I am used to the convenience of viewing code with an editor that can automatically highlight matching parentheses, braces and brackets. Plato can show matching parentheses if you press Ctrl-] . Consequently, before I put the lens on a complicated Fortran expression I copy-paste the code into my editor to make the job easier. |
|
Back to top |
|
 |
JohnCampbell
Joined: 16 Feb 2006 Posts: 2615 Location: Sydney
|
Posted: Wed Jul 29, 2015 1:19 pm Post subject: |
|
|
Eddie,
I did have an "or further ..." Code: | do i=1,ndata
!
talf2 = t(i)-alf(2)
talf23 = ( talf2 / alf(3) )**2
exp_alf2 = exp (-talf23) / alf(3)
talf4 = t(i)-alf(4)
talf45 = ( talf4 / alf(5) )**2
exp_alf4 = exp (-talf45) / alf(5)
!
jac(i,1) = -t(i) * exp(-alf(1)*t(i))
jac(i,2) = 2 * talf2 / alf(3) * exp_alf2
jac(i,3) = 2 * talf23 * exp_alf2
jac(i,4) = 2 * talf4 / alf(5) * exp_alf4
jac(i,5) = 2 * talf45 * exp_alf4
end do |
Although I have not tested these changes, I would expect that ifort -O3 would do most of this automatically and lately I have been surprised what FTN95 /opt can do sometimes, identifying repeated code.
I do think that this latest effort I have posted has lost a lot of the clarity that the original code had, and if the compiler can do the few identifying repeated code snippets, then a modern optimising compiler is probably the better solution.
The problem with optimising compilers is that is very difficult to debug, as the optimiser will change your bugs and any bugs that are in the compiler itself become very difficult to identify. Most forums report these from time to time.
If you change the compiler options, the bugs can change dramatically also.
We need to be aware of what optimising compilers don't do,
John
I'm a bit lost with these α, β coefficients, as mine are mass and stiffness damping coefficients. |
|
Back to top |
|
 |
LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2403 Location: Yateley, Hants, UK
|
Posted: Wed Jul 29, 2015 1:52 pm Post subject: |
|
|
Hi Mecej4,
Thanks for the explanation. In my case, Saxonicum is Latin, dating probably to before the Germans had a written language. Wikipedia tells me that Saxony is landlocked and if correct, it cannot therefore have a Litus.
Did you get my point though about the different spellings of alpha being a temptation to misspell the variable name? I try to avoid such, despite the tendency to Americanise 'ph'. To spell Alph as Alf would make Coleridge's poem turn from Romanticism to Comedy.
Now I see how you cope with the forest of parentheses. Maybe I should try a different editor or even Plato. I remember the shock of moving from Algol-60 on the Elliot 4120 where square brackets were used for array notation to Fortran with its single kind of brackets and with its simple equals = where Algol denoted assignment with :=. But even so, I recommend white space as an aid to readability, otherwiseallwordswouldbestrungtogetherandthiswouldmakeitmoredifficulttoread. Your editor may well help you to write it, but I'm reading it without the benefit of colour coding. (And, for all I know, colour coding of source code isn't part of any Fortran standard. I hope no-one out there is thinking that is a good idea for Fortran 2020!)
Perhaps one day Fortran will have different brackets - there are 3 pairs on my keyboard () [] and {} (and maybe 4 if I included <>). This would be an advance akin to the introduction of double quote marks, which finally allowed me to dispose of 1H' (the last stand of the Hollerith) and replace it with "'" or '"' (I avoided '''' for much the same reasons as I eschew deeply nested brackets).
For John, I must say that the manually simplified code is much clearer to my eye than the original, which only goes to show that one man's meat is another man's poison, beauty is in the eye of the beholder, and that there are horses for courses.
Eddie |
|
Back to top |
|
 |
mecej4
Joined: 31 Oct 2006 Posts: 1899
|
Posted: Wed Jul 29, 2015 3:47 pm Post subject: |
|
|
Quote: | John: I'm a bit lost with these α, β coefficients, as mine are mass and stiffness damping coefficients. |
You can see the model equation at http://www.itl.nist.gov/div898/strd/nls/data/gauss2.shtml .
Eddie: Let us note that punctuation became popular after printing became widespread. There are many old manuscripts and stone inscriptions in European languages which show no punctuation. Example: https://en.wikipedia.org/wiki/Scriptio_continua . Even today, many languages have no bifurcation of letters into capital and lower-case (this word itself comes from the letter-cases used at the printer's workplace), and there are many other languages in which there is no native need/use for punctuation. Fortunately, neither English nor Fortran are tonal languages, or else...
You may find the graphs at the end of http://homepage.ntlworld.com/vivian.c/Punctuation/PunctFigs.htm interesting. Help save the semicolon, which seems headed for extinction! Enter "Fortran" at https://books.google.com/ngrams/ , and try to explain the peaks in the graph and the present downtrend. |
|
Back to top |
|
 |
LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2403 Location: Yateley, Hants, UK
|
Posted: Thu Jul 30, 2015 2:11 pm Post subject: |
|
|
The 19th century Fortran citations are all, as far as I know, the effect of hyphenation at the end of a line, thus giving 'for tran-slation' or 'for tran-sport' a citation of 'fortran', which shows that the hyphen ranks higher than a space in Google-land. Punctuation was de rigeur in English when Fortran was invented, and perhaps I'll leave it at that, noting that most computer languages have their roots in English except perhaps Algol's 'ENTIER'.
The Ngram's spikes must indicate something, but what? A different picture might be drawn if the counts were scaled by number of volumes sold, as a successful book deters other authors from even starting to write.
Eddie |
|
Back to top |
|
 |
mecej4
Joined: 31 Oct 2006 Posts: 1899
|
Posted: Thu Jul 30, 2015 4:23 pm Post subject: Re: |
|
|
LitusSaxonicum wrote: | The 19th century Fortran citations are all, as far as I know, the effect of hyphenation at the end of a line, thus giving 'for tran-slation' or 'for tran-sport' a citation of 'fortran',...
Eddie |
Good point, Eddie. I had never run into n-grams before yesterday, and this flaw has been noted by others and Google seems to have shown interest in improving the measure: http://blog.dictionary.com/hyphen/ .
Quote: | a successful book deters other authors from even starting to write |
Sometimes, however, that may cause several ghostwriters to surface, writing under the now famous name: for example, "Leslie Charteris". |
|
Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|