Commit 64dfd1883e

Andrew Kelley <andrew@ziglang.org>
2020-06-21 02:14:33
zig fmt: avoid unnecessary file system access
zig fmt previously would write a temp file, and then either rename it into place if necessary, or unlink it if nothing was changed. Now zig fmt renders into a memory buffer, and only writes the temp file and renames it into place if anything changed. Based on the performance testing I did this actually did not have much of an impact, however it's likely that on other operating systems and other hard drives this could make a big difference.
1 parent 0a9672f
Changed files (1)
src-self-hosted
src-self-hosted/main.zig
@@ -546,6 +546,7 @@ const Fmt = struct {
     any_error: bool,
     color: Color,
     gpa: *Allocator,
+    out_buffer: std.ArrayList(u8),
 
     const SeenMap = std.AutoHashMap(fs.File.INode, void);
 };
@@ -641,7 +642,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
         .seen = Fmt.SeenMap.init(gpa),
         .any_error = false,
         .color = color,
+        .out_buffer = std.ArrayList(u8).init(gpa),
     };
+    defer fmt.seen.deinit();
+    defer fmt.out_buffer.deinit();
 
     for (input_files.span()) |file_path| {
         // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
@@ -767,14 +771,19 @@ fn fmtPathFile(
             fmt.any_error = true;
         }
     } else {
-        const baf = try io.BufferedAtomicFile.create(fmt.gpa, dir, sub_path, .{ .mode = stat.mode });
-        defer baf.destroy();
-
-        const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);
-        if (anything_changed) {
-            std.debug.warn("{}\n", .{file_path});
-            try baf.finish();
-        }
+        // As a heuristic, we make enough capacity for the same as the input source.
+        try fmt.out_buffer.ensureCapacity(source_code.len);
+        fmt.out_buffer.items.len = 0;
+        const anything_changed = try std.zig.render(fmt.gpa, fmt.out_buffer.writer(), tree);
+        if (!anything_changed)
+            return; // Good thing we didn't waste any file system access on this.
+
+        var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
+        defer af.deinit();
+
+        try af.file.writeAll(fmt.out_buffer.items);
+        try af.finish();
+        std.debug.warn("{}\n", .{file_path});
     }
 }