diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 342aaa5a2f8..c4c35adffeb 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,9 @@ +2019-07-21 Thomas König + + PR libfortran/91030 + * gfortran.texi (GFORTRAN_FORMATTED_BUFFER_SIZE): Document + (GFORTRAN_UNFORMATTED_BUFFER_SIZE): Likewise. + 2019-07-16 Harald Anlauf PR fortran/90903 diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi index 4f654508abc..16be9e05b43 100644 --- a/gcc/fortran/gfortran.texi +++ b/gcc/fortran/gfortran.texi @@ -611,6 +611,8 @@ Malformed environment variables are silently ignored. * GFORTRAN_LIST_SEPARATOR:: Separator for list output * GFORTRAN_CONVERT_UNIT:: Set endianness for unformatted I/O * GFORTRAN_ERROR_BACKTRACE:: Show backtrace on run-time errors +* GFORTRAN_FORMATTED_BUFFER_SIZE:: Buffer size for formatted files. +* GFORTRAN_UNFORMATTED_BUFFER_SIZE:: Buffer size for unformatted files. @end menu @node TMPDIR @@ -782,6 +784,20 @@ the backtracing, set the variable to @samp{n}, @samp{N}, @samp{0}. Default is to print a backtrace unless the @option{-fno-backtrace} compile option was used. +@node GFORTRAN_FORMATTED_BUFFER_SIZE +@section @env{GFORTRAN_FORMATTED_BUFFER_SIZE}---Set buffer size for formatted I/O + +The @env{GFORTRAN_FORMATTED_BUFFER_SIZE} environment variable +specifies buffer size in bytes to be used for formatted output. +The default value is 8192. + +@node GFORTRAN_UNFORMATTED_BUFFER_SIZE +@section @env{GFORTRAN_UNFORMATTED_BUFFER_SIZE}---Set buffer size for unformatted I/O + +The @env{GFORTRAN_UNFORMATTED_BUFFER_SIZE} environment variable +specifies buffer size in bytes to be used for unformatted output. +The default value is 131072. + @c ===================================================================== @c PART II: LANGUAGE REFERENCE @c ===================================================================== diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog index 71fe27bf9fc..b4e3fe73282 100644 --- a/libgfortran/ChangeLog +++ b/libgfortran/ChangeLog @@ -1,3 +1,21 @@ +2019-07-21 Thomas König + + PR libfortran/91030 + * io/unix.c (BUFFER_SIZE): Delete. + (BUFFER_FORMATTED_SIZE_DEFAULT): New variable. + (BUFFER_UNFORMATTED_SIZE_DEFAULT): New variable. + (unix_stream): Add buffer_size. + (buf_read): Use s->buffer_size instead of BUFFER_SIZE. + (buf_write): Likewise. + (buf_init): Add argument unformatted. Handle block sizes + for unformatted vs. formatted, using defaults if provided. + (fd_to_stream): Add argument unformatted in call to buf_init. + * libgfortran.h (options_t): Add buffer_size_formatted and + buffer_size_unformatted. + * runtime/environ.c (variable_table): Add + GFORTRAN_UNFORMATTED_BUFFER_SIZE and + GFORTRAN_FORMATTED_BUFFER_SIZE. + 2019-06-25 Kwok Cheung Yeung Andrew Stubbs diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c index c2fc674cc83..42792976c4b 100644 --- a/libgfortran/io/unix.c +++ b/libgfortran/io/unix.c @@ -193,7 +193,8 @@ fallback_access (const char *path, int mode) /* Unix and internal stream I/O module */ -static const int BUFFER_SIZE = 8192; +static const int FORMATTED_BUFFER_SIZE_DEFAULT = 8192; +static const int UNFORMATTED_BUFFER_SIZE_DEFAULT = 128*1024; typedef struct { @@ -205,6 +206,7 @@ typedef struct gfc_offset file_length; /* Length of the file. */ char *buffer; /* Pointer to the buffer. */ + ssize_t buffer_size; /* Length of the buffer. */ int fd; /* The POSIX file descriptor. */ int active; /* Length of valid bytes in the buffer */ @@ -592,9 +594,9 @@ buf_read (unix_stream *s, void *buf, ssize_t nbyte) && raw_seek (s, new_logical, SEEK_SET) < 0) return -1; s->buffer_offset = s->physical_offset = new_logical; - if (to_read <= BUFFER_SIZE/2) + if (to_read <= s->buffer_size/2) { - did_read = raw_read (s, s->buffer, BUFFER_SIZE); + did_read = raw_read (s, s->buffer, s->buffer_size); if (likely (did_read >= 0)) { s->physical_offset += did_read; @@ -632,11 +634,11 @@ buf_write (unix_stream *s, const void *buf, ssize_t nbyte) s->buffer_offset = s->logical_offset; /* Does the data fit into the buffer? As a special case, if the - buffer is empty and the request is bigger than BUFFER_SIZE/2, + buffer is empty and the request is bigger than s->buffer_size/2, write directly. This avoids the case where the buffer would have to be flushed at every write. */ - if (!(s->ndirty == 0 && nbyte > BUFFER_SIZE/2) - && s->logical_offset + nbyte <= s->buffer_offset + BUFFER_SIZE + if (!(s->ndirty == 0 && nbyte > s->buffer_size/2) + && s->logical_offset + nbyte <= s->buffer_offset + s->buffer_size && s->buffer_offset <= s->logical_offset && s->buffer_offset + s->ndirty >= s->logical_offset) { @@ -651,7 +653,7 @@ buf_write (unix_stream *s, const void *buf, ssize_t nbyte) the request is bigger than the buffer size, write directly bypassing the buffer. */ buf_flush (s); - if (nbyte <= BUFFER_SIZE/2) + if (nbyte <= s->buffer_size/2) { memcpy (s->buffer, buf, nbyte); s->buffer_offset = s->logical_offset; @@ -688,7 +690,7 @@ buf_write (unix_stream *s, const void *buf, ssize_t nbyte) static int buf_markeor (unix_stream *s) { - if (s->unbuffered || s->ndirty >= BUFFER_SIZE / 2) + if (s->unbuffered || s->ndirty >= s->buffer_size / 2) return buf_flush (s); return 0; } @@ -765,11 +767,32 @@ static const struct stream_vtable buf_vtable = { }; static int -buf_init (unix_stream *s) +buf_init (unix_stream *s, bool unformatted) { s->st.vptr = &buf_vtable; - s->buffer = xmalloc (BUFFER_SIZE); + /* Try to guess a good value for the buffer size. For formatted + I/O, we use so many CPU cycles converting the data that there is + more sense in converving memory and especially cache. For + unformatted, a bigger block can have a large impact in some + environments. */ + + if (unformatted) + { + if (options.unformatted_buffer_size > 0) + s->buffer_size = options.unformatted_buffer_size; + else + s->buffer_size = UNFORMATTED_BUFFER_SIZE_DEFAULT; + } + else + { + if (options.formatted_buffer_size > 0) + s->buffer_size = options.formatted_buffer_size; + else + s->buffer_size = FORMATTED_BUFFER_SIZE_DEFAULT; + } + + s->buffer = xmalloc (s->buffer_size); return 0; } @@ -1120,13 +1143,13 @@ fd_to_stream (int fd, bool unformatted) (s->fd == STDIN_FILENO || s->fd == STDOUT_FILENO || s->fd == STDERR_FILENO))) - buf_init (s); + buf_init (s, unformatted); else { if (unformatted) { s->unbuffered = true; - buf_init (s); + buf_init (s, unformatted); } else raw_init (s); diff --git a/libgfortran/libgfortran.h b/libgfortran/libgfortran.h index 433b204abda..c0db96f02a8 100644 --- a/libgfortran/libgfortran.h +++ b/libgfortran/libgfortran.h @@ -540,6 +540,7 @@ typedef struct int all_unbuffered, unbuffered_preconnected; int fpe, backtrace; + int unformatted_buffer_size, formatted_buffer_size; } options_t; diff --git a/libgfortran/runtime/environ.c b/libgfortran/runtime/environ.c index e3e7d2499d7..5817d199799 100644 --- a/libgfortran/runtime/environ.c +++ b/libgfortran/runtime/environ.c @@ -198,6 +198,14 @@ static variable variable_table[] = { /* Print out a backtrace if possible on runtime error */ { "GFORTRAN_ERROR_BACKTRACE", -1, &options.backtrace, init_boolean }, + /* Buffer size for unformatted files. */ + { "GFORTRAN_UNFORMATTED_BUFFER_SIZE", 0, &options.unformatted_buffer_size, + init_integer }, + + /* Buffer size for formatted files. */ + { "GFORTRAN_FORMATTED_BUFFER_SIZE", 0, &options.formatted_buffer_size, + init_integer }, + { NULL, 0, NULL, NULL } };