PR30724, cygwin ld performance regression since 014a602b86
authorAlan Modra <amodra@gmail.com>
Sun, 6 Aug 2023 22:58:55 +0000 (08:28 +0930)
committerAlan Modra <amodra@gmail.com>
Tue, 8 Aug 2023 23:17:35 +0000 (08:47 +0930)
According to the reporter of this bug the newlib fseek implementation
is likely slowed down by locking and fflush, only attempting to
optimise seeks when the file is opened read-only.  Thus when writing
the output we get a dramatic slowdown due to commit 014a602b86.

PR 30724
* bfd.c (enum bfd_last_io): New.
(struct bfd): Add last_io field.
* bfd-in2.h: Regenerate.
* bfd-io.c (bfd_bread, bfd_bwrite): Force seek if last_io is
opposite direction.
(bfd_seek): Reinstate optimisation for seek to same position.

bfd/bfd-in2.h
bfd/bfd.c
bfd/bfdio.c

index 5f49807d7f20e7ec2ab5b8db8b9b7ec7ee91997a..cc62ab196176b2115ab9d46d327d8790ebc29a09 100644 (file)
@@ -1916,6 +1916,14 @@ enum bfd_direction
     both_direction = 3
   };
 
+enum bfd_last_io
+  {
+    bfd_io_seek = 0,
+    bfd_io_read = 1,
+    bfd_io_write = 2,
+    bfd_io_force = 3
+  };
+
 enum bfd_plugin_format
   {
     bfd_plugin_unknown = 0,
@@ -2068,6 +2076,20 @@ struct bfd
   /* The direction with which the BFD was opened.  */
   ENUM_BITFIELD (bfd_direction) direction : 2;
 
+  /* POSIX.1-2017 (IEEE Std 1003.1) says of fopen : "When a file is
+     opened with update mode ('+' as the second or third character in
+     the mode argument), both input and output may be performed on
+     the associated stream.  However, the application shall ensure
+     that output is not directly followed by input without an
+     intervening call to fflush() or to a file positioning function
+     (fseek(), fsetpos(), or rewind()), and input is not directly
+     followed by output without an intervening call to a file
+     positioning function, unless the input operation encounters
+     end-of-file."
+     This field tracks the last IO operation, so that bfd can insert
+     a seek when IO direction changes.  */
+  ENUM_BITFIELD (bfd_last_io) last_io : 2;
+
   /* Is the file descriptor being cached?  That is, can it be closed as
      needed, and re-opened when accessed later?  */
   unsigned int cacheable : 1;
index e43a388ac721fbdb01a6344c84f4c7f240e5a5dd..88943a042d661373f6fc6d4472974fdc32c854c2 100644 (file)
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -53,6 +53,14 @@ EXTERNAL
 .    both_direction = 3
 .  };
 .
+.enum bfd_last_io
+.  {
+.    bfd_io_seek = 0,
+.    bfd_io_read = 1,
+.    bfd_io_write = 2,
+.    bfd_io_force = 3
+.  };
+.
 .enum bfd_plugin_format
 .  {
 .    bfd_plugin_unknown = 0,
@@ -208,6 +216,20 @@ CODE_FRAGMENT
 .  {* The direction with which the BFD was opened.  *}
 .  ENUM_BITFIELD (bfd_direction) direction : 2;
 .
+.  {* POSIX.1-2017 (IEEE Std 1003.1) says of fopen : "When a file is
+.     opened with update mode ('+' as the second or third character in
+.     the mode argument), both input and output may be performed on
+.     the associated stream.  However, the application shall ensure
+.     that output is not directly followed by input without an
+.     intervening call to fflush() or to a file positioning function
+.     (fseek(), fsetpos(), or rewind()), and input is not directly
+.     followed by output without an intervening call to a file
+.     positioning function, unless the input operation encounters
+.     end-of-file."
+.     This field tracks the last IO operation, so that bfd can insert
+.     a seek when IO direction changes.  *}
+.  ENUM_BITFIELD (bfd_last_io) last_io : 2;
+.
 .  {* Is the file descriptor being cached?  That is, can it be closed as
 .     needed, and re-opened when accessed later?  *}
 .  unsigned int cacheable : 1;
index 22c39a7b0ccfb0bd9ef29cea58ce1d699dec9d45..e0d47b3ee1c3017f1bf45e3f5187251d8cff0500 100644 (file)
@@ -279,6 +279,14 @@ bfd_bread (void *ptr, bfd_size_type size, bfd *abfd)
       return -1;
     }
 
+  if (abfd->last_io == bfd_io_write)
+    {
+      abfd->last_io = bfd_io_force;
+      if (bfd_seek (abfd, 0, SEEK_CUR) != 0)
+       return -1;
+    }
+  abfd->last_io = bfd_io_read;
+
   nread = abfd->iovec->bread (abfd, ptr, size);
   if (nread != -1)
     abfd->where += nread;
@@ -313,6 +321,14 @@ bfd_bwrite (const void *ptr, bfd_size_type size, bfd *abfd)
       return -1;
     }
 
+  if (abfd->last_io == bfd_io_read)
+    {
+      abfd->last_io = bfd_io_force;
+      if (bfd_seek (abfd, 0, SEEK_CUR) != 0)
+       return -1;
+    }
+  abfd->last_io = bfd_io_write;
+
   nwrote = abfd->iovec->bwrite (abfd, ptr, size);
   if (nwrote != -1)
     abfd->where += nwrote;
@@ -456,6 +472,13 @@ bfd_seek (bfd *abfd, file_ptr position, int direction)
   if (direction != SEEK_CUR)
     position += offset;
 
+  if (((direction == SEEK_CUR && position == 0)
+       || (direction == SEEK_SET && (ufile_ptr) position == abfd->where))
+      && abfd->last_io != bfd_io_force)
+    return 0;
+
+  abfd->last_io = bfd_io_seek;
+
   result = abfd->iovec->bseek (abfd, position, direction);
   if (result != 0)
     {