diff rust/rhg/src/commands/status.rs @ 52726:65839176cea9

rhg: buffer the output of `rhg status` Before this commit, `hg status` was issuing multiple `write` syscalls per line printed, separately writing out the path and the status fragments. This change makes hg status on large number of files significantly faster, going from 1.8s to 1.2s in one case. This requires adding the color information to `StdoutBuffer`, and moving the formatting functions from ui to it. I made `StdoutBuffer` generic over the underlying writer, without insisting on BufWriter, because I anticipated the need to use it with both full-buffered and line-buffered writers. That didn't end up being necessary, but I think the code is still better this way.
author Arseniy Alekseyev <aalekseyev@janestreet.com>
date Fri, 31 Jan 2025 15:04:13 +0000
parents bde718849153
children 7405f8a67611
line wrap: on
line diff
--- a/rust/rhg/src/commands/status.rs	Thu Jan 30 09:23:16 2025 +0100
+++ b/rust/rhg/src/commands/status.rs	Fri Jan 31 15:04:13 2025 +0000
@@ -687,8 +687,7 @@
         mut paths: Vec<StatusPath<'_>>,
     ) -> Result<(), CommandError> {
         paths.sort_unstable();
-        // TODO: get the stdout lock once for the whole loop
-        // instead of in each write
+        let mut stdout = self.ui.stdout_buffer();
         for StatusPath { path, copy_source } in paths {
             let relative_path;
             let relative_source;
@@ -702,25 +701,27 @@
             } else {
                 (path.as_bytes(), copy_source.as_ref().map(|s| s.as_bytes()))
             };
-            // TODO: Add a way to use `write_bytes!` instead of `format_bytes!`
-            // in order to stream to stdout instead of allocating an
-            // itermediate `Vec<u8>`.
+            // TODO: Add a way to use `write_bytes!` instead of
+            // `format_bytes!` in order to stream to stdout
+            // instead of allocating an itermediate
+            // `Vec<u8>`.
             if !self.no_status {
-                self.ui.write_stdout_labelled(status_prefix, label)?
+                stdout.write_stdout_labelled(status_prefix, label)?
             }
             let linebreak = if self.print0 { b"\x00" } else { b"\n" };
-            self.ui.write_stdout_labelled(
+            stdout.write_stdout_labelled(
                 &format_bytes!(b"{}{}", path, linebreak),
                 label,
             )?;
             if let Some(source) = copy_source {
                 let label = "status.copied";
-                self.ui.write_stdout_labelled(
+                stdout.write_stdout_labelled(
                     &format_bytes!(b"  {}{}", source, linebreak),
                     label,
                 )?
             }
         }
+        stdout.flush()?;
         Ok(())
     }