Suppress File Writing in Legacy Code

Suppress File Writing in Legacy Code

I have a very large legacy simuilation model written Fortran 90 and compiled with the Intel Compiler. The program opens about 30 file units for writing output information and I want to make this an optional feature of the code. Is there a way through either an Intel specific command or Fortran standard that can supress writing the output files.

The brute force way would be to make some sort of flag that would be read in at run time and the place a series of IF statements before every WRITE. I would like to do this a bit more elegantly, but have not been able to figure out how (thought I could set the unit numbers to negatives, but that just caused the code to print to the cmd prompt).

The reason for all the extraneous output is for debugging the simulation model, but it impacts adversly the overall speed of the code.

Thanks for all help.

16 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

There is no way you can do this, as far as I know. You could open the files as a "null device" (*), so that they do not take up any space on disk
and the operating system might even decide that it is not worth to transfer the bytes that your program writes, but that does not take away the
processing required for the formatting of the numbers.

The write statements are not concentrated in a few dedicated routines, I suppose?

Regards,

Arjen

(*) On Windows the file name would be "nul" (or at least that was the case with older versions of Windows and I suppose that still works
that way). On Linux that would be "/dev/null"

Consider using the name of the null device ("NUL:" in Windows) as the FILE= argument when OPENing the files that you want to suppress. You could collect all the OPEN statements in one subroutine. Based on an environmental variable, directives in a small configuration file or user input, you would choose either real file names or the name of the null device in that subroutine.

The main problem with collecting all your OPENs into one routine MYOPEN and then collecting all your WRITEs and all your FORMATs into a single routine MYWRITE and replacing

WRITE(UNIT,FORMATNUMBER) (Argument list)

with

iret=MYWRITE(UNIT, FORMATNUMBER, Argumentlist)

is how to package each unique argument list. At first glance you would have to create a unique user-defined type for each argument list.
Sounds a lot of work when putting your WRITEs inside IF...Then..Else...Endif blocks is more straightforward and more flexible (easy to change argument list and format at will) even though a drag at first.

If this is formatted output, you can write all output to a string variable and pass it to a single subroutine which checks a global or passed argument to determine if it should write it. If the unit numbers vary, pass that as an argument. The subroutine can check open status of each unit if they differ and can perform opening as needed.

Good suggestion!
Just use

WRITE(CHARACTERBUFFER,formatnumber) (OUTPUT LIST whatever...)

then

iret=MYWRITE(CHARACTERBUFFER,UNITNUMBER).

Your problem will be sizing your character buffer beforehand long enough to handle your longest burst of output.
You are still processing the WRITEs though.

Anthony Richards (#3):Quote:

The main problem with collecting all your OPENs into one routine MYOPEN and then collecting all your WRITEs and all your FORMATs into a single routine...
My suggestion in #2 was only about collecting the OPEN statements. Once unit numbers have been associated with bit-buckets, where the WRITE statements are located, what the I/O lists contain and the types of the members of those lists do not matter.

How about
IF(YourUnit .gt. 0) WRITE(YourUNIT,FORMATNUMBER) (Argument list)

This would require your "YourUnit" numbers to be variables.
Also, depending on what is in "FORMATNUMBER" you may also be able to use the Fortran PreProcessor

#define WRITE(u,f) IF(u .gt. 0) write(u, f)

The above may require using free format as use in fixed format may cause line to exceed line length.

Jim Dempsey

www.quickthreadprogramming.com

Thanks for all the help, for my situation there are a series of global variables that are used as unit numbers and a single routine that opens them all. I think it be easiest to open up to NULL and write to that.

Can someone explain how this is done, I never knew Windows had a bit bucket like linux does.

Also, I did not follow the preprocressor directive that was mentioned. I can not set the unit numbers to 0 because the code already uses this by default for deciding which options are in use. All the unit numbers are stored in an integer array where each element represents a particular input or output file which are features that the code may use. It does use fix formats for character strings for most of the written output.

Thanks again for all your comments.

Does windows have something like a the linux tmpfs that is already mounted (no additional software needed)? One possibility I thought of would be to open the files to a directory in ram and have it constantly rewind so it does not use much storage. The main interest is both to save on the conversion to character, but to prevent excessive hard disk I/O. The code has a lot of loops that write an enormous ammount of extra data to ascii files.

Does windows have something like a the linux tmpfs that is already mounted (no additional software needed)? One possibility I thought of would be to open the files to a directory in ram and have it constantly rewind so it does not use much storage. The main interest is both to save on the conversion to character, but to prevent excessive hard disk I/O. The code has a lot of loops that write an enormous ammount of extra data to ascii files.

My preferred approach is as Jim has suggested. It has a number of benefits.
Have global variables for the file unit numbers and set them to -ve if the file is not open or you do not want to use that output option at that time.
To change the code, you need to
1) include or USE the module with the unit numbers,
2) simply change the WRITE (... to
if (LU_23 > 0) WRITE (LU_23,...
.
This is a minimal change to the original code and opens up the functionality of selectively manageing all output.
Searching for all WRITE statements is fairly easy for a global change, while the main code change problem comes when the file unit numbers are transferred via subroutine arguments.
.
Again this change can be a precurser to an improved management of program output, with the advantage of minimising output size and run time.
Avoiding the generation of the output text can be a significant saving.
I have needed this approach, where outputs from my simulation codes have exploded from a few mb of output to many gb. This was an easy change to bring the output back under control, while leaving a lot of checking code in place, but selectively turned off.
.
John

One hitch with the "all negative numbers" approach is that it then prevents you from using a rather handy feature in F2008 - the NEWUNIT specifier in the open statement, which must return a negative unit number.  I find fixed unit numbers an anathema (why should I have to care what specific number is in use and then need to resolve any clashes between libraries - I just want to reference a particular connection!), so something like NEWUNIT appeals to me.

If you tested explicitly against -1 then I think you are ok - from what I can tell that can never be a valid unit number.  i.e. IF (LU_23 /= -1) WRITE (...

I'd be trying to cut things off in the source code like this - if you are relying on a nul device type of solution then you've still got a lot of IO overhead being incurred, even if physical disk heads aren't involved.  But failing that, as Arjen said the filename for the Windows bitbucket is simply "NUL". 

OPEN(UNIT=10,FILE='NUL',ACTION='WRITE')
  WRITE (10,"('No one will read this!')")
  CLOSE(10)

If you are dong a global edit and extending coding line lengths, you have to be using free-format if you are to avoid going beyond column 72 problems.

If 0 in your units table indicates something special in addition to not being open, and if -n is not appropriate (re NEWUNIT), then you can define a unit number that is to be reserved to indicate file not available - avoid WRITE. Perhaps you can use 9999 (assuming your unit numbers are held in something other than integer(1)).

INTEGER, PARAMETER :: AvoidIO = 9999
...
IF(YourUnit != AvoidIO) WRITE(YourUnit,....)
Also address OPEN, SEEK, INQUIRE, CLOSE, ...

Note, should you have a section of code containing writes

IF(YourUnit != AvoidIO) THEN
WRITE(YourUnit,....) ...
WRITE(YourUnit,....) ...
WRITE(YourUnit,....) ...
...
ENDIF

The relative small amount of work spent now will be returned later by shorter run times in not formatting data sent to NUL

Jim Dempsey

www.quickthreadprogramming.com

I like the idea of writing to the null device. It goes well with a test for skipping writing as well:

program p
   implicit none
   integer unit1, unit2
! Or 'NLA0:' if you prefer
   character(20), parameter :: nulfilename = 'NUL'
   character(20) filename
   open(newunit=unit1,file='f.txt',status='replace')
   open(newunit=unit2,file=nulfilename)
   write(unit1,'(a)') 'Should appear in f.txt'
   write(unit2,'(a)') 'Should not survive'
   inquire(unit=unit1,name=filename)
   write(*,'(a)') 'Name of unit1 file = '//trim(filename)
   inquire(unit=unit2,name=filename)
   write(*,'(a)') 'Name of unit2 file = '//trim(filename)
   if(filename /= nulfilename) then
      write(unit2,'(a)') "This shouldn't have happened"
   else
      write(*,'(a)') 'Writing to unit2 skipped'
   end if
end program p

Leave a Comment

Please sign in to add a comment. Not a member? Join today