Intel® Fortran Compiler Classic and Intel® Fortran Compiler Developer Guide and Reference

ID 767251
Date 11/07/2023
Public

A newer version of this document is available. Customers should click here to go to the newest version.

Document Table of Contents

Rules for Unformatted Sequential READ Statements

Unformatted, sequential READ statements transfer binary data (without translation) between the current record and the entities specified in the I/O list. The value transferred from the file is called a field. Only one record is read.

Objects of intrinsic or derived types can be transferred.

For data transfer, the file must be positioned so that the record read is an unformatted record or an end-of-file record.

The unformatted, sequential READ statement reads a single record. Each field value in the record must be of the same type as the corresponding entity in the input list, unless the field value is real or complex.

If the field value is real or complex, one complex field value can correspond to two real list entities, or two real values can correspond to one complex list entity. The corresponding values and entities must have the same kind parameter.

If the number of I/O list items is less than or equal to the number of fields in an input record, the READ statement ignores the excess fields. If the number of I/O list items is greater than the number of fields in an input record, an error occurs.

If a READ statement contains no I/O list, it skips over one full record, positioning the file to read the following record on the next execution of a READ statement.

If the file is connected for formatted, list-directed, or namelist I/O, unformatted data transfer is prohibited.

You have previously been able to buffer the output (WRITEs) of variable length, unformatted, sequential files, by specifying certain values for an OPEN statement, environment variable, or compiler option. You can now do the same buffering for input (READs) of records. To enable buffering for the input of records, you can specify any of the following:

  • BUFFERED=YES in the file's OPEN statement

  • Value YES (Y or y), or TRUE (T or t), or a number > 0 for the environment variable FORT_BUFFERED

  • Setting buffered_io for the assume option

When any of the above are specified, the Fortran Runtime Library buffers all input records from variable length, unformatted, sequential files, regardless of the size of the records in the file. In addition, if the environment variable FORT_ BUFFERING_THRESHOLD has a positive value n, the following occurs:

  • I/O list items with a size <= n are buffered and are moved one at a time from the runtime buffer to the I/O list item

  • I/O list items with a size > n are not buffered and are moved one at a time from the file to the I/O list item

Determining the Size of I/O Buffers

If both the block size and the buffer count have been specified with positive values, their product determines the size in bytes of the buffer for that I/O unit. If neither is specified, the default size of the buffer is 8KB (8192 bytes). This is the initial size of the I/O buffer; the buffer may be expanded to hold a larger record.

If block size is not specified, the following occurs:

  • The block size defaults to 128KB

  • If the buffer count is not specified, it defaults to 1

  • The initial default buffer size is 8KB

  • If buffer count is specified, then the initial default buffer size is (8KB * buffercount)

If a block size is specified, the following occurs:

  • The block size is the specified value, rounded up to 512-byte boundary

  • If the buffer count is not specified, it defaults to 1

  • The initial default buffer size is the block size, rounded up to 512-byte boundary

  • If buffer count is specified, then the initial default buffer size is (block size * buffer count)

Optimizing for Time or Space

When reading variable length, unformatted sequential records, the runtime system may optimize for time or for space.

This optimization decision is made during runtime on a record-by-record basis using specifications made by the program and the length of a given record. That is, one record may be optimized for time while another record from the same file may be optimized for space.

The default behavior when reading records of this type whose length exceeds the specified block size, is to not buffer the input records. You can override this default behavior by requesting that the input be buffered.

The following table shows the relationship between a file's specified block size and the length of its variable length records. Note that the length of the individual record is the key:

 

record length >= block size

record length < block size

buffering unspecified

Optimizes for space

Optimizes for time

OPEN (BUFFERED='YES')

Optimizes for time

Optimizes for time

OPEN (BUFFERED='NO')

Optimizes for space

Optimizes for time

If an input record's length is less than or equal to the specified block size, then by default, the runtime system always optimizes for time rather than for space and the input is buffered.

If an input record’s length is greater than the specified block size, then the following occurs:

  • By default, the runtime system always optimizes for space rather than time and the input is not buffered.

  • If you request buffering of input, then the runtime system optimizes for time and the input is buffered.

  • If you request no buffering of input, then the runtime system optimizes for space rather than time and the input is not buffered.

  • If you request dynamic buffering of input, the runtime system optimizes based on the size of the I/O list item and some items are buffered and some are not.

Optimizing for time:

Traditionally, optimizing for time comes at the expense of using more memory.

When the runtime system optimizes for time, it buffers input. It reads as much data as possible during one disk access into the runtime's internal buffer, extending it if necessary to hold the file's largest record. Fields within the record are then moved to the user space in response to READs from the file. Typically, minimizing file accesses is faster.

However, there are circumstances when optimizing for space can actually be faster than optimizing for time.

For example, consider you are reading records whose length exceeds the block size and the data is being read into a contiguous array. Reading this huge array directly into a user's space is going to be faster than reading it first into the runtime system's internal buffer, then moving the data to the user's space. In this case, it is better to optimize for space; that is, you should not buffer the input record.

On the other hand, if the READ is being done into non-contiguous elements of an array, the traditional method of optimizing for time becomes a huge win. Data being read into non-contiguous array elements must be moved, or read, into the user's space one element at a time. In this case, you always want to optimize for time; that is, you should buffer the input data.

If you are reading large, variable length, unformatted records, you should try both buffered and unbuffered I/O to determine which delivers the better performance.

Optimizing for space:

Traditionally, optimizing for space comes at the expense of time.

When the runtime system optimizes for space, it wants to avoid creating a huge internal buffer in order to hold a "very large" record. The size of a "very large" record is clearly subjective, but the rule of thumb here is whether or not a record's size is greater than the specified block size.

If this is the case, the runtime system will read one field at a time from the record, directly into the I/O list items. The optimal record for this optimization is one whose record length exceeds the default block size of 128 KB (or a user-specified block size) and contains "very large" fields.

Note that because fields are read one at a time from the file to the user space, very large records that contain very small fields may see a serious performance issue. In these cases, it may be better to buffer the input. If you are reading large, variable length, unformatted records, you should try both buffered and unbuffered I/O to determine which delivers the better performance.

Optimizing unformatted sequential input based on field size - dynamic buffering:

Dynamic buffering is a hybrid solution that chooses the time/space trade-off on a per field basis. The decision is based on a "field size threshold" supplied by the user, deciding, for every field in every record in the file, regardless of the record length, whether or not to buffer a field from the file to the I/O list item. The runtime system can do this because it knows the size of a field that is being requested before actually attempting to read the field.

When a READ statement is first executed, a read from the file is issued to fill the buffer, regardless of its size. This is necessary so that the runtime system can extract the record size from the first length control field. (Each unformatted sequential record has 4-byte leading and trailing record lengths to facilitate reading both forwards and backwards in the file.) From that point on, dynamic buffering decides whether or not to buffer a field or to read it directly from the file to the I/O list item. If the buffer holds the beginning portion of a large field, it will be moved to the start of the I/O list item and the remainder will be read directly from the file.

The following table shows the various buffering options for unformatted, sequential input:

Buffering Option

How do I get this?

What happens?

Non-buffered

You get this kind of buffering by default, or by specifying:

  • OPEN (BUFFERED=NO)

    - or -

  • FORT_BUFFERED=NO

    - or -

  • assume nobuffered_io

When non-buffered is in effect:

  • The runtime system does not re-allocate its buffer to accommodate large records.

  • Records with lengths > the block size:

    • Are not buffered.

    • All fields are moved one at a time from the file to the I/O list item.

  • Records with lengths <= the block size:

    • Are buffered.

    • All fields are moved one at a time from the buffer to the I/O list item.

Buffered

You get this kind of buffering by specifying:

  • OPEN (BUFFERED=YES)

    - or -

  • FORT_BUFFERED=YES

    - or -

  • assume buffered_io

When buffered is in effect:

  • The runtime system re-allocates its buffer to accommodate the largest record read.

  • All input records are buffered.

  • All fields are moved one at a time from the buffer to the I/O list item

Dynamic buffering

You get this kind of buffering by specifying:

  • FORT_BUFFERING_THRESHOLD=n

    - and one of -

  • OPEN (BUFFERED=YES)

    - or -

  • FORT_BUFFERED=YES

    - or -

  • assume buffered_io

When dynamic buffering is in effect:

  • The runtime system does not re-allocate its buffer to accommodate large records.

  • All fields with a size <= n are buffered and are moved one at a time from the buffer to the I/O list item.

  • Fields with a size > n are not buffered and are moved one at a time from the file to the I/O list item.

Examples

The following example shows an unformatted, sequential READ statement:


  READ (UNIT=6, IOSTAT=IO_STATUS) A, B, C