Fortran-FOSS-Programmers / Best_Practices

the opinionated *best practices* of Fortran FOSS Programmers group
Creative Commons Attribution Share Alike 4.0 International
64 stars 10 forks source link

Kind parameters #17

Open raullaasner opened 6 years ago

raullaasner commented 6 years ago

What would be the best way of specifying the kind parameter? Should we always use the selected_*_kind function, integer, parameter :: dp = selected_real_kind(15, 307) real(dp) :: x or are there better alternatives? According to this blog post the ISO_FORTRAN_ENV constants for kinds do not always work as intended. Also, is it a good habit to always specify the kind parameter for integers?

szaghi commented 6 years ago

Hi @raullaasner ,

I tend to agree with Dr. Fortran (S. Lionel). I generally use my own kind precision module. I use the intrinsic ISO_FORTRAN_ENV only for rapid tests when PENF is far...

My best regards.

tclune commented 6 years ago

I have agonized over these issues more than I probably should have for all the (lack of) actual problems I’ve encountered.

One thing that is clear to me is that the answer (or at least the reasoning behind the answer) is quite different when one is developing a general purpose infrastructure layer than when one is developing a numerical algorithm. For the latter, one would ideally do the necessary analysis to determine the necessary precision, etc. In my experience, physical scientists rarely do anything more sophisticated than using some default that they learned in their youth. Some at least “know” that 32 bit precision is inadequate for at least some part of their code and thus use 64-bit. (And some disciplines know that 64-bit is inadequate - e.g., those that study the long-term stability of orbits in the solar system.)

I too used to use my own kind precision module, but now that I’m involved with a much larger code 3 million+ lines of code with large pieces that have independent origins, the proliferation of such modules becomes a problem of its own. For infrastructure code my goal is usually to provide interfaces that work with “all” the KIND’s that are used in the numerical components developed by others. In practice the IOS_FORTRAN_ENV constants: REAL32 and REAL64 work well and are very portable. But in theory I ought to support at least each of the following that does not have a negative value:

kind(1.) kind(1.d0) REAL32 REAL64 REAL128 C_FLOAT C_DOUBLE C_LONG_DOUBLE

The trick is to avoid duplicates. For all the current compilers I use:

KIND(1.) == REAL32 == C_FLOAT KIND(1.D0) == REAL64 == C_DOUBLE (And I assume REAL128 == C_LONG_DOUBLE, but have never checked.)

And, yes, I know that some vendors do provide additional precisions, but to cover that I’d have to do some sort of nasty loop over range and precision to see the values that are supported by a given vendor. And all that would still be a preprocessing step that was used to do some sort of generic code generation. (Could possibly use Fortran PDT’s in some cases though.)

Sorry for the rambling post.

On Jan 12, 2018, at 9:38 AM, Stefano Zaghi notifications@github.com<mailto:notifications@github.com> wrote:

Hi @raullaasnerhttps://github.com/raullaasner ,

I tend to agree with Dr. Fortran (S. Lionel). I generally use my own kind precision modulehttps://github.com/szaghi/PENF. I use the intrinsic ISO_FORTRAN_ENV only for rapid tests when PENF is far...

My best regards.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHubhttps://github.com/Fortran-FOSS-Programmers/Best_Practices/issues/17#issuecomment-357254824, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AF7_4J2QSsOsiQwgY4dyvX76qEBkwV-zks5tJ250gaJpZM4RcXsl.

LadaF commented 6 years ago

Hello,

all these are valid and appropriate. Their semantics is different and therefore are appropriate in different situations, which often overlap.

kind(1.d0)

use when you want precision higher than the default, this may change from one compiler setting to another.

selected_real_kind

use when you care about about numerical accuracy irrespective of current compiler options

c_double

use when you want to be compatible with double of the companion C compiler. You don't have to actually interoperate with C.

real64

use when you care about storage size.

Very often they are quite the same for 32 bit and 64 bit. There are some issues between 80-bit and 128-bit, don't forget that storage size of 80-bit reals is also 128!

If you want to be portable and that means not just for you, but for other users, so NOT make too many assumptions about default reals and double precisions. They can be changed using compiler options and people DO use these options (like -fdefault-real or -r8).

In practice you will have to do conversions anyway, when you call a C function and your Fortran reals are specified other than c_double, you should be prepared to do a conversion, do not just assume c_double is the same as real64. If they are, the compiler will optimize it away. If it is a.code just for you, you can be less defensive, because the compiler will object if you pass a wrong kind.-


Regarding integer kinds. That depends on your application. I tend to not specify the kind unless necessary.

My arrays are 3D and indexes in each direction are small, much less than even a max 16-bit integer.

However, if I need to work with the number of elements in the array, or even number of bytes, it could easily overflow, that can be a billion or more. I do not need that often, though.

BTW, this is a deep problem in MPI, it is specified to use int in C and cannot therefore work with very large arrays. Do not repeat that mistake.

Best regards,

Vladimir

Dne 12. 1. 2018 3:32 odpoledne napsal uživatel "Raul Laasner" < notifications@github.com>:

What would be the best way of specifying the kind parameter? Should we always use the selected_*_kind function, integer, parameter :: dp = selected_real_kind(15, 307) real(dp) :: x or are there better alternatives? According to this https://software.intel.com/en-us/blogs/2017/03/27/doctor-fortran-in-it-takes-all-kinds blog post the ISO_FORTRAN_ENV constants for kinds do not always work as intended. Also, is it a good habit to always specify the kind parameter for integers?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Fortran-FOSS-Programmers/Best_Practices/issues/17, or mute the thread https://github.com/notifications/unsubscribe-auth/AAskEfX72_XVb6l-vzeBedPp16p8TjCTks5tJ20WgaJpZM4RcXsl .

nncarlson commented 6 years ago

FWIW, I'm with @tclune on this one, and my thinking has evolved along the same lines over the years.

tclune commented 6 years ago

One additional point that might be worth making.

So-called “half-precision” is becoming an important optimization on hardware accelerators when your code can tolerate it.

I suspect that ISO_FORTRAN_ENV should be extended to include a REAL16 parameter. (Which will then get confused with REAL*16. sigh.)

LadaF commented 6 years ago

Hi,

I do agree one should have generic version for multiple kinds prepared, at least in a library.

I do not agree that one can assume those equalities. I am active on Stackoverflow and can see what less knowledgeable users of various libraries do and they do use various kind promoting options in their compilers.

c_double=real64 will be safe unless a new Unix or other platform emerges (in terms of LP64 etc.), but that won't happen in this or in the next decade. We are lucky we are not in the 60s and CPUs now use IEEE standards.

I also do not agree 32bit is necessarily inadequate. In my applications there is not that much difference introduced and it is much faster. I compile my program in single or double precision just by changing a parameter for the build script, so if I do find real64 is necessary somewhere, I can either compile the whole code as such or find out the location and force it there.

Regards,

Vladimir

Dne 12. 1. 2018 3:59 odpoledne napsal uživatel "tclune" < notifications@github.com>:

I have agonized over these issues more than I probably should have for all the (lack of) actual problems I’ve encountered.

One thing that is clear to me is that the answer (or at least the reasoning behind the answer) is quite different when one is developing a general purpose infrastructure layer than when one is developing a numerical algorithm. For the latter, one would ideally do the necessary analysis to determine the necessary precision, etc. In my experience, physical scientists rarely do anything more sophisticated than using some default that they learned in their youth. Some at least “know” that 32 bit precision is inadequate for at least some part of their code and thus use 64-bit. (And some disciplines know that 64-bit is inadequate - e.g., those that study the long-term stability of orbits in the solar system.)

I too used to use my own kind precision module, but now that I’m involved with a much larger code 3 million+ lines of code with large pieces that have independent origins, the proliferation of such modules becomes a problem of its own. For infrastructure code my goal is usually to provide interfaces that work with “all” the KIND’s that are used in the numerical components developed by others. In practice the IOS_FORTRAN_ENV constants: REAL32 and REAL64 work well and are very portable. But in theory I ought to support at least each of the following that does not have a negative value:

kind(1.) kind(1.d0) REAL32 REAL64 REAL128 C_FLOAT C_DOUBLE C_LONG_DOUBLE

The trick is to avoid duplicates. For all the current compilers I use:

KIND(1.) == REAL32 == C_FLOAT KIND(1.D0) == REAL64 == C_DOUBLE (And I assume REAL128 == C_LONG_DOUBLE, but have never checked.)

And, yes, I know that some vendors do provide additional precisions, but to cover that I’d have to do some sort of nasty loop over range and precision to see the values that are supported by a given vendor. And all that would still be a preprocessing step that was used to do some sort of generic code generation. (Could possibly use Fortran PDT’s in some cases though.)

Sorry for the rambling post.

  • Tom

On Jan 12, 2018, at 9:38 AM, Stefano Zaghi <notifications@github.com< mailto:notifications@github.com>> wrote:

Hi @raullaasnerhttps://github.com/raullaasner ,

I tend to agree with Dr. Fortran (S. Lionel). I generally use my own kind precision modulehttps://github.com/szaghi/PENF. I use the intrinsic ISO_FORTRAN_ENV only for rapid tests when PENF is far...

My best regards.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHubhttps://github.com/ Fortran-FOSS-Programmers/Best_Practices/issues/17#issuecomment-357254824, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AF7_ 4J2QSsOsiQwgY4dyvX76qEBkwV-zks5tJ250gaJpZM4RcXsl.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Fortran-FOSS-Programmers/Best_Practices/issues/17#issuecomment-357260316, or mute the thread https://github.com/notifications/unsubscribe-auth/AAskEakPWDs7qRvUFqebHzcghIvHywJMks5tJ3NRgaJpZM4RcXsl .

raullaasner commented 6 years ago

(And I assume REAL128 == C_LONG_DOUBLE, but have never checked.)

These are the kind of corner cases I had in mind, actually. REAL128 has a different effect with GNU and Intel compilers, whereas using SELECTED_REAL_KIND should always be unambiguous. REAL64 should be safe for now and it refers to double precision in all major Fortran compilers, but interestingly nothing in the standard requires it to be double precision.

tclune commented 6 years ago

On Jan 12, 2018, at 11:12 AM, LadaF notifications@github.com<mailto:notifications@github.com> wrote:

Hi,

I do agree one should have generic version for multiple kinds prepared, at least in a library.

I do not agree that one can assume those equalities.

I’m not exactly assuming these inequalities. I’m just aware that my current implementations could be insufficient on some future architecture. (Very low on my list of concerns.) Considering that most of the client code I support still uses REAL4 and REAL8, it’s not even the biggest concern on this front.

I am active on Stackoverflow and can see what less knowledgeable users of various libraries do and they do use various kind promoting options in their compilers.

c_double=real64 will be safe unless a new Unix or other platform emerges (in terms of LP64 etc.), but that won't happen in this or in the next decade. We are lucky we are not in the 60s and CPUs now use IEEE standards.

Even then, I think that c_double == real64 will remain true. Increasing the address size should not affect this relationship.

I also do not agree 32bit is necessarily inadequate.

I only meant that some scientists do reach that conclusion. Many agree with your findings. Certainly the bulk of the calculations in the code that I currently use are 32-bit FP operations. Some packages are in 64-bit for no known reason and are on our short list for potential performance optimization. (Halving the precision is one of the easiest optimizations available.) In some cases, the scientist has gone a bit further and used 32-bit as a default and then identified a small number of variables that need to be 64-bit, but usually this is purely empirical. I.e., the code got wrong answers and/or crashed when using 32-bit everywhere and they made educated guesses as to where to back off.

In my applications there is not that much difference introduced and it is much faster. I compile my program in single or double precision just by changing a parameter for the build script, so if I do find real64 is necessary somewhere, I can either compile the whole code as such or find out the location and force it there.

I wish our code were in this state, and much of it is. But enough parts cannot be easily changed and pushing this capability competes with other issues. Certainly on codes that I’ve developed myself I’ve generally enabled that functionality. Usually even with quad precision when I can, but library availability often limits that end. Sure I can modify existing FFT and linear algebra libraries.

Cheers,

Regards,

Vladimir

Dne 12. 1. 2018 3:59 odpoledne napsal uživatel "tclune" < notifications@github.commailto:notifications@github.com>:

I have agonized over these issues more than I probably should have for all the (lack of) actual problems I’ve encountered.

One thing that is clear to me is that the answer (or at least the reasoning behind the answer) is quite different when one is developing a general purpose infrastructure layer than when one is developing a numerical algorithm. For the latter, one would ideally do the necessary analysis to determine the necessary precision, etc. In my experience, physical scientists rarely do anything more sophisticated than using some default that they learned in their youth. Some at least “know” that 32 bit precision is inadequate for at least some part of their code and thus use 64-bit. (And some disciplines know that 64-bit is inadequate - e.g., those that study the long-term stability of orbits in the solar system.)

I too used to use my own kind precision module, but now that I’m involved with a much larger code 3 million+ lines of code with large pieces that have independent origins, the proliferation of such modules becomes a problem of its own. For infrastructure code my goal is usually to provide interfaces that work with “all” the KIND’s that are used in the numerical components developed by others. In practice the IOS_FORTRAN_ENV constants: REAL32 and REAL64 work well and are very portable. But in theory I ought to support at least each of the following that does not have a negative value:

kind(1.) kind(1.d0) REAL32 REAL64 REAL128 C_FLOAT C_DOUBLE C_LONG_DOUBLE

The trick is to avoid duplicates. For all the current compilers I use:

KIND(1.) == REAL32 == C_FLOAT KIND(1.D0) == REAL64 == C_DOUBLE (And I assume REAL128 == C_LONG_DOUBLE, but have never checked.)

And, yes, I know that some vendors do provide additional precisions, but to cover that I’d have to do some sort of nasty loop over range and precision to see the values that are supported by a given vendor. And all that would still be a preprocessing step that was used to do some sort of generic code generation. (Could possibly use Fortran PDT’s in some cases though.)

Sorry for the rambling post.

  • Tom

On Jan 12, 2018, at 9:38 AM, Stefano Zaghi notifications@github.com<mailto:notifications@github.com< mailto:notifications@github.com>> wrote:

Hi @raullaasnerhttps://github.com/raullaasner ,

I tend to agree with Dr. Fortran (S. Lionel). I generally use my own kind precision modulehttps://github.com/szaghi/PENF. I use the intrinsic ISO_FORTRAN_ENV only for rapid tests when PENF is far...

My best regards.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHubhttps://github.com/ Fortran-FOSS-Programmers/Best_Practices/issues/17#issuecomment-357254824, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AF7_ 4J2QSsOsiQwgY4dyvX76qEBkwV-zks5tJ250gaJpZM4RcXsl.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Fortran-FOSS-Programmers/Best_Practices/issues/17#issuecomment-357260316, or mute the thread https://github.com/notifications/unsubscribe-auth/AAskEakPWDs7qRvUFqebHzcghIvHywJMks5tJ3NRgaJpZM4RcXsl .

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/Fortran-FOSS-Programmers/Best_Practices/issues/17#issuecomment-357281509, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AF7_4L893MoHhIsrt0_gIpsY6o4ih4awks5tJ4SAgaJpZM4RcXsl.