authorgravatar for lucascarvalhosantos91@gmail.comLucas Santos <lucascarvalhosantos91@gmail.com> 2024-09-23 21:20:27-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-09-23 17:20:27-07:00
logb19d0fb0fd3e4ff023968e77f01848ffb9dce360
tree3b591d1f39f59a13377c6a17d5b9a891fd8b0add
parentd1901c744c9bf2862f99be837325e0a50365c38b
signaturebadge-check Signed by PGP key B5690EEEBB952194

Improve efficiency of buffered_reader. (#21256)

The previous implementation of buffered_reader always reads from the unbuffered reader into the internal buffer, and then dumps the data onto the destination. This is inefficient, as sometimes it's possible to read directly into the destination. The previous strategy generates more memory copies and unbuffered reads than necessary.

1 files changed, 39 insertions(+), 33 deletions(-)

lib/std/io/buffered_reader.zig+39-33
......@@ -17,26 +17,27 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
1717 const Self = @This();
1818
1919 pub fn read(self: *Self, dest: []u8) Error!usize {
20 var dest_index: usize = 0;
21
22 while (dest_index < dest.len) {
23 const written = @min(dest.len - dest_index, self.end - self.start);
24 @memcpy(dest[dest_index..][0..written], self.buf[self.start..][0..written]);
25 if (written == 0) {
26 // buf empty, fill it
27 const n = try self.unbuffered_reader.read(self.buf[0..]);
28 if (n == 0) {
29 // reading from the unbuffered stream returned nothing
30 // so we have nothing left to read.
31 return dest_index;
32 }
33 self.start = 0;
34 self.end = n;
35 }
36 self.start += written;
37 dest_index += written;
20 // First try reading from the already buffered data onto the destination.
21 const current = self.buf[self.start..self.end];
22 if (current.len != 0) {
23 const to_transfer = @min(current.len, dest.len);
24 @memcpy(dest[0..to_transfer], current[0..to_transfer]);
25 self.start += to_transfer;
26 return to_transfer;
3827 }
39 return dest.len;
28
29 // If dest is large, read from the unbuffered reader directly into the destination.
30 if (dest.len >= buffer_size) {
31 return self.unbuffered_reader.read(dest);
32 }
33
34 // If dest is small, read from the unbuffered reader into our own internal buffer,
35 // and then transfer to destination.
36 self.end = try self.unbuffered_reader.read(&self.buf);
37 const to_transfer = @min(self.end, dest.len);
38 @memcpy(dest[0..to_transfer], self.buf[0..to_transfer]);
39 self.start = to_transfer;
40 return to_transfer;
4041 }
4142
4243 pub fn reader(self: *Self) Reader {
......@@ -134,12 +135,13 @@ test "Block" {
134135 var test_buf_reader: BufferedReader(4, BlockReader) = .{
135136 .unbuffered_reader = BlockReader.init(block, 2),
136137 };
138 const reader = test_buf_reader.reader();
137139 var out_buf: [4]u8 = undefined;
138 _ = try test_buf_reader.read(&out_buf);
140 _ = try reader.readAll(&out_buf);
139141 try testing.expectEqualSlices(u8, &out_buf, block);
140 _ = try test_buf_reader.read(&out_buf);
142 _ = try reader.readAll(&out_buf);
141143 try testing.expectEqualSlices(u8, &out_buf, block);
142 try testing.expectEqual(try test_buf_reader.read(&out_buf), 0);
144 try testing.expectEqual(try reader.readAll(&out_buf), 0);
143145 }
144146
145147 // len out < block
......@@ -147,14 +149,15 @@ test "Block" {
147149 var test_buf_reader: BufferedReader(4, BlockReader) = .{
148150 .unbuffered_reader = BlockReader.init(block, 2),
149151 };
152 const reader = test_buf_reader.reader();
150153 var out_buf: [3]u8 = undefined;
151 _ = try test_buf_reader.read(&out_buf);
154 _ = try reader.readAll(&out_buf);
152155 try testing.expectEqualSlices(u8, &out_buf, "012");
153 _ = try test_buf_reader.read(&out_buf);
156 _ = try reader.readAll(&out_buf);
154157 try testing.expectEqualSlices(u8, &out_buf, "301");
155 const n = try test_buf_reader.read(&out_buf);
158 const n = try reader.readAll(&out_buf);
156159 try testing.expectEqualSlices(u8, out_buf[0..n], "23");
157 try testing.expectEqual(try test_buf_reader.read(&out_buf), 0);
160 try testing.expectEqual(try reader.readAll(&out_buf), 0);
158161 }
159162
160163 // len out > block
......@@ -162,12 +165,13 @@ test "Block" {
162165 var test_buf_reader: BufferedReader(4, BlockReader) = .{
163166 .unbuffered_reader = BlockReader.init(block, 2),
164167 };
168 const reader = test_buf_reader.reader();
165169 var out_buf: [5]u8 = undefined;
166 _ = try test_buf_reader.read(&out_buf);
170 _ = try reader.readAll(&out_buf);
167171 try testing.expectEqualSlices(u8, &out_buf, "01230");
168 const n = try test_buf_reader.read(&out_buf);
172 const n = try reader.readAll(&out_buf);
169173 try testing.expectEqualSlices(u8, out_buf[0..n], "123");
170 try testing.expectEqual(try test_buf_reader.read(&out_buf), 0);
174 try testing.expectEqual(try reader.readAll(&out_buf), 0);
171175 }
172176
173177 // len out == 0
......@@ -175,8 +179,9 @@ test "Block" {
175179 var test_buf_reader: BufferedReader(4, BlockReader) = .{
176180 .unbuffered_reader = BlockReader.init(block, 2),
177181 };
182 const reader = test_buf_reader.reader();
178183 var out_buf: [0]u8 = undefined;
179 _ = try test_buf_reader.read(&out_buf);
184 _ = try reader.readAll(&out_buf);
180185 try testing.expectEqualSlices(u8, &out_buf, "");
181186 }
182187
......@@ -185,11 +190,12 @@ test "Block" {
185190 var test_buf_reader: BufferedReader(5, BlockReader) = .{
186191 .unbuffered_reader = BlockReader.init(block, 2),
187192 };
193 const reader = test_buf_reader.reader();
188194 var out_buf: [4]u8 = undefined;
189 _ = try test_buf_reader.read(&out_buf);
195 _ = try reader.readAll(&out_buf);
190196 try testing.expectEqualSlices(u8, &out_buf, block);
191 _ = try test_buf_reader.read(&out_buf);
197 _ = try reader.readAll(&out_buf);
192198 try testing.expectEqualSlices(u8, &out_buf, block);
193 try testing.expectEqual(try test_buf_reader.read(&out_buf), 0);
199 try testing.expectEqual(try reader.readAll(&out_buf), 0);
194200 }
195201}