authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-20 20:14:33-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-20 20:14:33-04:00
log64dfd1883eb2b9ab175af9b07c84bd4b53b7e904
tree468d8404da72474318a8abb166238b0b0b572436
parent0a9672fb86b84658f8780f57e769be45e41f3034

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 files changed, 17 insertions(+), 8 deletions(-)

src-self-hosted/main.zig+17-8
...@@ -546,6 +546,7 @@ const Fmt = struct {...@@ -546,6 +546,7 @@ const Fmt = struct {
546 any_error: bool,546 any_error: bool,
547 color: Color,547 color: Color,
548 gpa: *Allocator,548 gpa: *Allocator,
549 out_buffer: std.ArrayList(u8),
549550
550 const SeenMap = std.AutoHashMap(fs.File.INode, void);551 const SeenMap = std.AutoHashMap(fs.File.INode, void);
551};552};
...@@ -641,7 +642,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -641,7 +642,10 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
641 .seen = Fmt.SeenMap.init(gpa),642 .seen = Fmt.SeenMap.init(gpa),
642 .any_error = false,643 .any_error = false,
643 .color = color,644 .color = color,
645 .out_buffer = std.ArrayList(u8).init(gpa),
644 };646 };
647 defer fmt.seen.deinit();
648 defer fmt.out_buffer.deinit();
645649
646 for (input_files.span()) |file_path| {650 for (input_files.span()) |file_path| {
647 // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.651 // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
...@@ -767,14 +771,19 @@ fn fmtPathFile(...@@ -767,14 +771,19 @@ fn fmtPathFile(
767 fmt.any_error = true;771 fmt.any_error = true;
768 }772 }
769 } else {773 } else {
770 const baf = try io.BufferedAtomicFile.create(fmt.gpa, dir, sub_path, .{ .mode = stat.mode });774 // As a heuristic, we make enough capacity for the same as the input source.
771 defer baf.destroy();775 try fmt.out_buffer.ensureCapacity(source_code.len);
772776 fmt.out_buffer.items.len = 0;
773 const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);777 const anything_changed = try std.zig.render(fmt.gpa, fmt.out_buffer.writer(), tree);
774 if (anything_changed) {778 if (!anything_changed)
775 std.debug.warn("{}\n", .{file_path});779 return; // Good thing we didn't waste any file system access on this.
776 try baf.finish();780
777 }781 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
782 defer af.deinit();
783
784 try af.file.writeAll(fmt.out_buffer.items);
785 try af.finish();
786 std.debug.warn("{}\n", .{file_path});
778 }787 }
779}788}
780789