git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 06/10] reftable/reader: iterate to next block in place

From
Patrick Steinhardt <ps@pks.im>
Date
Apr 8, 2024, 12:16 UTC
Message-ID
<685f0a40bc5ac6a8c3eaab78bac2050c0cad8de4.1712578376.git.ps@pks.im>
In-Reply-To
<cover.1712578376.git.ps@pks.im>

The table iterator has to iterate towards the next block once it has yielded all records of the current block. This is done by creating a new table iterator, initializing it to the next block, releasing the old iterator and then copying over the data.

Refactor the code to instead advance the table iterator in place. This is simpler and unlocks some optimizations in subsequent patches. Also, it allows us to avoid some allocations.

The following measurements show a single matching ref out of 1 million refs. Before this change:

  HEAP SUMMARY:
      in use at exit: 13,603 bytes in 125 blocks
    total heap usage: 7,235 allocs, 7,110 frees, 301,481 bytes allocated
After:
  HEAP SUMMARY:
      in use at exit: 13,603 bytes in 125 blocks
    total heap usage: 315 allocs, 190 frees, 107,027 bytes allocated
Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 reftable/block.c  |  2 ++
 reftable/reader.c | 47 ++++++++++++++++++++++++++---------------------
 2 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/reftable/block.c b/reftable/block.c
index 2d8d0668b3..0c4e71eae3 100644
--- a/reftable/block.c
+++ b/reftable/block.c
@@ -188,6 +188,8 @@ int block_reader_init(struct block_reader *br, struct reftable_block *block,
 	uint8_t *restart_bytes = NULL;
 	uint8_t *uncompressed = NULL;
 
+	reftable_block_done(&br->block);
+
 	if (!reftable_is_block_type(typ)) {
 		err =  REFTABLE_FORMAT_ERROR;
 		goto done;
diff --git a/reftable/reader.c b/reftable/reader.c
index b77b639751..dd4de294a1 100644
--- a/reftable/reader.c
+++ b/reftable/reader.c
@@ -312,26 +312,20 @@ static void table_iter_close(struct table_iter *ti)
 	block_iter_close(&ti->bi);
 }
 
-static int table_iter_next_block(struct table_iter *dest,
-				 struct table_iter *src)
+static int table_iter_next_block(struct table_iter *ti)
 {
-	uint64_t next_block_off = src->block_off + src->br.full_block_size;
+	uint64_t next_block_off = ti->block_off + ti->br.full_block_size;
 	int err;
 
-	dest->r = src->r;
-	dest->typ = src->typ;
-	dest->block_off = next_block_off;
-
-	err = reader_init_block_reader(src->r, &dest->br, next_block_off, src->typ);
+	err = reader_init_block_reader(ti->r, &ti->br, next_block_off, ti->typ);
 	if (err > 0)
-		dest->is_finished = 1;
-	if (err) {
-		table_iter_block_done(dest);
+		ti->is_finished = 1;
+	if (err)
 		return err;
-	}
 
-	dest->is_finished = 0;
-	block_iter_seek_start(&dest->bi, &dest->br);
+	ti->block_off = next_block_off;
+	ti->is_finished = 0;
+	block_iter_seek_start(&ti->bi, &ti->br);
 
 	return 0;
 }
@@ -342,7 +336,6 @@ static int table_iter_next(struct table_iter *ti, struct reftable_record *rec)
 		return REFTABLE_API_ERROR;
 
 	while (1) {
-		struct table_iter next = TABLE_ITER_INIT;
 		int err;
 
 		if (ti->is_finished)
@@ -362,14 +355,11 @@ static int table_iter_next(struct table_iter *ti, struct reftable_record *rec)
 		 * table and retry. If there are no more blocks then the
 		 * iterator is drained.
 		 */
-		err = table_iter_next_block(&next, ti);
+		err = table_iter_next_block(ti);
 		if (err) {
 			ti->is_finished = 1;
 			return err;
 		}
-
-		table_iter_close(ti);
-		*ti = next;
 	}
 }
 
@@ -453,9 +443,24 @@ static int reader_seek_linear(struct table_iter *ti,
 	 * have no other way to do this.
 	 */
 	while (1) {
-		struct table_iter next = TABLE_ITER_INIT;
+		struct table_iter next = *ti;
+
+		/*
+		 * We must be careful to not modify underlying data of `ti`
+		 * because we may find that `next` does not contain our desired
+		 * block, but that `ti` does. In that case, we would discard
+		 * `next` and continue with `ti`.
+		 *
+		 * This also means that we cannot reuse allocated memory for
+		 * `next` here. While it would be great if we could, it should
+		 * in practice not be too bad given that we should only ever
+		 * end up doing linear seeks with at most three blocks. As soon
+		 * as we have more than three blocks we would have an index, so
+		 * we would not do a linear search there anymore.
+		 */
+		memset(&next.br.block, 0, sizeof(next.br.block));
 
-		err = table_iter_next_block(&next, ti);
+		err = table_iter_next_block(&next);
 		if (err < 0)
 			goto done;
 		if (err > 0)
-- 
2.44.GIT
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 22 of 31 in “reftable: optimize table and block iterators”
  1. 0/9 reftable: optimize table and block iteratorsPatrick Steinhardt, Mar 27, 2024
  2. 1/9 reftable/block: rename `block_reader_start()`Patrick Steinhardt, Mar 27, 2024
  3. 2/9 reftable/block: merge `block_iter_seek()` and `block_reader_seek()`Patrick Steinhardt, Mar 27, 2024
  4. 3/9 reftable/block: better grouping of functionsPatrick Steinhardt, Mar 27, 2024
  5. 4/9 reftable/block: introduce `block_reader_release()`Patrick Steinhardt, Mar 27, 2024
  6. Karthik NayakApr 3, 2024
  7. Patrick SteinhardtApr 8, 2024
  8. 5/9 reftable/block: move ownership of block reader into `struct table_iter`Patrick Steinhardt, Mar 27, 2024
  9. Justin ToblerApr 3, 2024
  10. Patrick SteinhardtApr 3, 2024
  11. 6/9 reftable/reader: iterate to next block in placePatrick Steinhardt, Mar 27, 2024
  12. 7/9 reftable/block: reuse uncompressed blocksPatrick Steinhardt, Mar 27, 2024
  13. 8/9 reftable/block: open-code call to `uncompress2()`Patrick Steinhardt, Mar 27, 2024
  14. 9/9 reftable/block: reuse `zstream` state on inflationPatrick Steinhardt, Mar 27, 2024
  15. Karthik NayakApr 3, 2024
  16. 00/10 reftable: optimize table and block iteratorsPatrick Steinhardt, Apr 8, 2024
  17. 01/10 reftable/block: rename `block_reader_start()`Patrick Steinhardt, Apr 8, 2024
  18. 02/10 reftable/block: merge `block_iter_seek()` and `block_reader_seek()`Patrick Steinhardt, Apr 8, 2024
  19. 03/10 reftable/block: better grouping of functionsPatrick Steinhardt, Apr 8, 2024
  20. 04/10 reftable/block: introduce `block_reader_release()`Patrick Steinhardt, Apr 8, 2024
  21. 05/10 reftable/block: move ownership of block reader into `struct table_iter`Patrick Steinhardt, Apr 8, 2024
  22. 06/10 reftable/reader: iterate to next block in placePatrick Steinhardt, Apr 8, 2024
  23. 07/10 reftable/block: reuse uncompressed blocksPatrick Steinhardt, Apr 8, 2024
  24. 08/10 reftable/block: open-code call to `uncompress2()`Patrick Steinhardt, Apr 8, 2024
  25. 09/10 reftable/block: reuse `zstream` state on inflationPatrick Steinhardt, Apr 8, 2024
  26. Karthik NayakApr 10, 2024
  27. 10/10 reftable/block: avoid copying block iterators on seekPatrick Steinhardt, Apr 8, 2024
  28. Justin ToblerApr 9, 2024
  29. Patrick SteinhardtApr 9, 2024
  30. Justin ToblerApr 9, 2024
  31. Karthik NayakApr 10, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.