Monday, August 20, 2007

Fun with FORTRAN intrinsics and portability

When porting code from one compiler to the next, you run into all sorts of fun syntactical issues, gotchas with floating point handling, and how each maintainer semantically interpreted the standard.

When porting code from one runtime library to the next, you run into even more fun! Who says the API of your favorite function has not changed? Perhaps you no longer can reference some functions. What could you do to mitigate these risks?

I stumbled across some code that attempted to mitigate these risks associated with the Compaq FORTRAN non-standard (yet invaluable) intrinsic SLEEP by calling out to the C Runtime Library's sleep routine (compliant under ISO/IEC 9945-1:1990, "POSIX.1"). As the code progressed through the years, the original maintainers noted that on Windows, the sleep routine was renamed to Sleep. A simple change to the interface definition fixed the linking issues:
subroutine SLEEP(seconds)
integer*4 :: seconds
end subroutine
end interface
However, careful users would notice that this change (as an attempt to use the more stable C RTL version of sleep) has an unexpected side effect. MSDN states that the single parameter given to Sleep is actually, "The minimum time interval for which execution is to be suspended, in milliseconds."

So, code that once called SLEEP(1) or SLEEP(5) expecting to regain control in 1s and 5s respectively, now sleeps for 1ms and 5ms respectively. This is well beneath the timeslice/quantum given to a process (6-55ms on Windows), effectively making the call an inefficient Sleep(0) (which in and of itself is an inefficient thread yield!). The correct action is to consult the Intel FORTRAN Libraries reference and note that in the portability library is a SLEEP function that replicates the non-standard intrinsic found for Compaq, and will work across all platforms Intel's FORTRAN compiler is supported. This is not a great solution, but it is also not the worst solution (hacking a layer on top of the Windows Sleep function to multiply the parameter by 1000).

As an aside, it is a bit of a programming error to rely on sleep for hard timing of any interval other than integer multiples of the timeslice/quantum (plus some amount of jitter). It is also a bit of a programming error to ignore changes to API's when moving compilers and libraries and operating systems.

Too bad compilers cannot catch either of these...