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...@@ -17,26 +17,27 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
17 const Self = @This();17 const Self = @This();
1818
19 pub fn read(self: *Self, dest: []u8) Error!usize {19 pub fn read(self: *Self, dest: []u8) Error!usize {
20 var dest_index: usize = 0;20 // First try reading from the already buffered data onto the destination.
2121 const current = self.buf[self.start..self.end];
22 while (dest_index < dest.len) {22 if (current.len != 0) {
23 const written = @min(dest.len - dest_index, self.end - self.start);23 const to_transfer = @min(current.len, dest.len);
24 @memcpy(dest[dest_index..][0..written], self.buf[self.start..][0..written]);24 @memcpy(dest[0..to_transfer], current[0..to_transfer]);
25 if (written == 0) {25 self.start += to_transfer;
26 // buf empty, fill it26 return to_transfer;
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;
38 }27 }
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;
40 }41 }
4142
42 pub fn reader(self: *Self) Reader {43 pub fn reader(self: *Self) Reader {
...@@ -134,12 +135,13 @@ test "Block" {...@@ -134,12 +135,13 @@ test "Block" {
134 var test_buf_reader: BufferedReader(4, BlockReader) = .{135 var test_buf_reader: BufferedReader(4, BlockReader) = .{
135 .unbuffered_reader = BlockReader.init(block, 2),136 .unbuffered_reader = BlockReader.init(block, 2),
136 };137 };
138 const reader = test_buf_reader.reader();
137 var out_buf: [4]u8 = undefined;139 var out_buf: [4]u8 = undefined;
138 _ = try test_buf_reader.read(&out_buf);140 _ = try reader.readAll(&out_buf);
139 try testing.expectEqualSlices(u8, &out_buf, block);141 try testing.expectEqualSlices(u8, &out_buf, block);
140 _ = try test_buf_reader.read(&out_buf);142 _ = try reader.readAll(&out_buf);
141 try testing.expectEqualSlices(u8, &out_buf, block);143 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);
143 }145 }
144146
145 // len out < block147 // len out < block
...@@ -147,14 +149,15 @@ test "Block" {...@@ -147,14 +149,15 @@ test "Block" {
147 var test_buf_reader: BufferedReader(4, BlockReader) = .{149 var test_buf_reader: BufferedReader(4, BlockReader) = .{
148 .unbuffered_reader = BlockReader.init(block, 2),150 .unbuffered_reader = BlockReader.init(block, 2),
149 };151 };
152 const reader = test_buf_reader.reader();
150 var out_buf: [3]u8 = undefined;153 var out_buf: [3]u8 = undefined;
151 _ = try test_buf_reader.read(&out_buf);154 _ = try reader.readAll(&out_buf);
152 try testing.expectEqualSlices(u8, &out_buf, "012");155 try testing.expectEqualSlices(u8, &out_buf, "012");
153 _ = try test_buf_reader.read(&out_buf);156 _ = try reader.readAll(&out_buf);
154 try testing.expectEqualSlices(u8, &out_buf, "301");157 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);
156 try testing.expectEqualSlices(u8, out_buf[0..n], "23");159 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);
158 }161 }
159162
160 // len out > block163 // len out > block
...@@ -162,12 +165,13 @@ test "Block" {...@@ -162,12 +165,13 @@ test "Block" {
162 var test_buf_reader: BufferedReader(4, BlockReader) = .{165 var test_buf_reader: BufferedReader(4, BlockReader) = .{
163 .unbuffered_reader = BlockReader.init(block, 2),166 .unbuffered_reader = BlockReader.init(block, 2),
164 };167 };
168 const reader = test_buf_reader.reader();
165 var out_buf: [5]u8 = undefined;169 var out_buf: [5]u8 = undefined;
166 _ = try test_buf_reader.read(&out_buf);170 _ = try reader.readAll(&out_buf);
167 try testing.expectEqualSlices(u8, &out_buf, "01230");171 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);
169 try testing.expectEqualSlices(u8, out_buf[0..n], "123");173 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);
171 }175 }
172176
173 // len out == 0177 // len out == 0
...@@ -175,8 +179,9 @@ test "Block" {...@@ -175,8 +179,9 @@ test "Block" {
175 var test_buf_reader: BufferedReader(4, BlockReader) = .{179 var test_buf_reader: BufferedReader(4, BlockReader) = .{
176 .unbuffered_reader = BlockReader.init(block, 2),180 .unbuffered_reader = BlockReader.init(block, 2),
177 };181 };
182 const reader = test_buf_reader.reader();
178 var out_buf: [0]u8 = undefined;183 var out_buf: [0]u8 = undefined;
179 _ = try test_buf_reader.read(&out_buf);184 _ = try reader.readAll(&out_buf);
180 try testing.expectEqualSlices(u8, &out_buf, "");185 try testing.expectEqualSlices(u8, &out_buf, "");
181 }186 }
182187
...@@ -185,11 +190,12 @@ test "Block" {...@@ -185,11 +190,12 @@ test "Block" {
185 var test_buf_reader: BufferedReader(5, BlockReader) = .{190 var test_buf_reader: BufferedReader(5, BlockReader) = .{
186 .unbuffered_reader = BlockReader.init(block, 2),191 .unbuffered_reader = BlockReader.init(block, 2),
187 };192 };
193 const reader = test_buf_reader.reader();
188 var out_buf: [4]u8 = undefined;194 var out_buf: [4]u8 = undefined;
189 _ = try test_buf_reader.read(&out_buf);195 _ = try reader.readAll(&out_buf);
190 try testing.expectEqualSlices(u8, &out_buf, block);196 try testing.expectEqualSlices(u8, &out_buf, block);
191 _ = try test_buf_reader.read(&out_buf);197 _ = try reader.readAll(&out_buf);
192 try testing.expectEqualSlices(u8, &out_buf, block);198 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);
194 }200 }
195}201}