Skip to content

Commit daf4f43

Browse files
pks-tgitster
authored andcommitted
reftable/record: decode keys in place
When reading a record from a block, we need to decode the record's key. As reftable keys are prefix-compressed, meaning they reuse a prefix from the preceding record's key, this is a bit more involved than just having to copy the relevant bytes: we need to figure out the prefix and suffix lengths, copy the prefix from the preceding record and finally copy the suffix from the current record. This is done by passing three buffers to `reftable_decode_key()`: one buffer that holds the result, one buffer that holds the last key, and one buffer that points to the current record. The final key is then assembled by calling `strbuf_add()` twice to copy over the prefix and suffix. Performing two memory copies is inefficient though. And we can indeed do better by decoding keys in place. Instead of providing two buffers, the caller may only call a single buffer that is already pre-populated with the last key. Like this, we only have to call `strbuf_setlen()` to trim the record to its prefix and then `strbuf_add()` to add the suffix. This refactoring leads to a noticeable performance bump when iterating over 1 million refs: Benchmark 1: show-ref: single matching ref (revision = HEAD~) Time (mean ± σ): 112.2 ms ± 3.9 ms [User: 109.3 ms, System: 2.8 ms] Range (min … max): 109.2 ms … 149.6 ms 1000 runs Benchmark 2: show-ref: single matching ref (revision = HEAD) Time (mean ± σ): 106.0 ms ± 3.5 ms [User: 103.2 ms, System: 2.7 ms] Range (min … max): 103.2 ms … 133.7 ms 1000 runs Summary show-ref: single matching ref (revision = HEAD) ran 1.06 ± 0.05 times faster than show-ref: single matching ref (revision = HEAD~) Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 6620f91 commit daf4f43

File tree

5 files changed

+28
-30
lines changed

5 files changed

+28
-30
lines changed

reftable/block.c

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,8 @@ static int restart_key_less(size_t idx, void *args)
291291
/* the restart key is verbatim in the block, so this could avoid the
292292
alloc for decoding the key */
293293
struct strbuf rkey = STRBUF_INIT;
294-
struct strbuf last_key = STRBUF_INIT;
295294
uint8_t unused_extra;
296-
int n = reftable_decode_key(&rkey, &unused_extra, last_key, in);
295+
int n = reftable_decode_key(&rkey, &unused_extra, in);
297296
int result;
298297
if (n < 0) {
299298
a->error = 1;
@@ -326,35 +325,34 @@ int block_iter_next(struct block_iter *it, struct reftable_record *rec)
326325
if (it->next_off >= it->br->block_len)
327326
return 1;
328327

329-
n = reftable_decode_key(&it->key, &extra, it->last_key, in);
328+
n = reftable_decode_key(&it->last_key, &extra, in);
330329
if (n < 0)
331330
return -1;
332-
333-
if (!it->key.len)
331+
if (!it->last_key.len)
334332
return REFTABLE_FORMAT_ERROR;
335333

336334
string_view_consume(&in, n);
337-
n = reftable_record_decode(rec, it->key, extra, in, it->br->hash_size);
335+
n = reftable_record_decode(rec, it->last_key, extra, in, it->br->hash_size);
338336
if (n < 0)
339337
return -1;
340338
string_view_consume(&in, n);
341339

342-
strbuf_swap(&it->last_key, &it->key);
343340
it->next_off += start.len - in.len;
344341
return 0;
345342
}
346343

347344
int block_reader_first_key(struct block_reader *br, struct strbuf *key)
348345
{
349-
struct strbuf empty = STRBUF_INIT;
350-
int off = br->header_off + 4;
346+
int off = br->header_off + 4, n;
351347
struct string_view in = {
352348
.buf = br->block.data + off,
353349
.len = br->block_len - off,
354350
};
355-
356351
uint8_t extra = 0;
357-
int n = reftable_decode_key(key, &extra, empty, in);
352+
353+
strbuf_reset(key);
354+
355+
n = reftable_decode_key(key, &extra, in);
358356
if (n < 0)
359357
return n;
360358
if (!key->len)
@@ -371,7 +369,6 @@ int block_iter_seek(struct block_iter *it, struct strbuf *want)
371369
void block_iter_close(struct block_iter *it)
372370
{
373371
strbuf_release(&it->last_key);
374-
strbuf_release(&it->key);
375372
}
376373

377374
int block_reader_seek(struct block_reader *br, struct block_iter *it,
@@ -408,8 +405,8 @@ int block_reader_seek(struct block_reader *br, struct block_iter *it,
408405
if (err < 0)
409406
goto done;
410407

411-
reftable_record_key(&rec, &it->key);
412-
if (err > 0 || strbuf_cmp(&it->key, want) >= 0) {
408+
reftable_record_key(&rec, &it->last_key);
409+
if (err > 0 || strbuf_cmp(&it->last_key, want) >= 0) {
413410
err = 0;
414411
goto done;
415412
}

reftable/block.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,10 @@ struct block_iter {
8484

8585
/* key for last entry we read. */
8686
struct strbuf last_key;
87-
struct strbuf key;
8887
};
8988

9089
#define BLOCK_ITER_INIT { \
9190
.last_key = STRBUF_INIT, \
92-
.key = STRBUF_INIT, \
9391
}
9492

9593
/* initializes a block reader. */

reftable/record.c

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,20 +159,19 @@ int reftable_encode_key(int *restart, struct string_view dest,
159159
return start.len - dest.len;
160160
}
161161

162-
int reftable_decode_key(struct strbuf *key, uint8_t *extra,
163-
struct strbuf last_key, struct string_view in)
162+
int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
163+
struct string_view in)
164164
{
165165
int start_len = in.len;
166166
uint64_t prefix_len = 0;
167167
uint64_t suffix_len = 0;
168-
int n = get_var_int(&prefix_len, &in);
168+
int n;
169+
170+
n = get_var_int(&prefix_len, &in);
169171
if (n < 0)
170172
return -1;
171173
string_view_consume(&in, n);
172174

173-
if (prefix_len > last_key.len)
174-
return -1;
175-
176175
n = get_var_int(&suffix_len, &in);
177176
if (n <= 0)
178177
return -1;
@@ -181,12 +180,12 @@ int reftable_decode_key(struct strbuf *key, uint8_t *extra,
181180
*extra = (uint8_t)(suffix_len & 0x7);
182181
suffix_len >>= 3;
183182

184-
if (in.len < suffix_len)
183+
if (in.len < suffix_len ||
184+
prefix_len > last_key->len)
185185
return -1;
186186

187-
strbuf_reset(key);
188-
strbuf_add(key, last_key.buf, prefix_len);
189-
strbuf_add(key, in.buf, suffix_len);
187+
strbuf_setlen(last_key, prefix_len);
188+
strbuf_add(last_key, in.buf, suffix_len);
190189
string_view_consume(&in, suffix_len);
191190

192191
return start_len - in.len;

reftable/record.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,12 @@ int reftable_encode_key(int *is_restart, struct string_view dest,
8181
struct strbuf prev_key, struct strbuf key,
8282
uint8_t extra);
8383

84-
/* Decode into `key` and `extra` from `in` */
85-
int reftable_decode_key(struct strbuf *key, uint8_t *extra,
86-
struct strbuf last_key, struct string_view in);
84+
/*
85+
* Decode into `last_key` and `extra` from `in`. `last_key` is expected to
86+
* contain the decoded key of the preceding record, if any.
87+
*/
88+
int reftable_decode_key(struct strbuf *last_key, uint8_t *extra,
89+
struct string_view in);
8790

8891
/* reftable_index_record are used internally to speed up lookups. */
8992
struct reftable_index_record {

reftable/record_test.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ static void test_key_roundtrip(void)
295295
EXPECT(!restart);
296296
EXPECT(n > 0);
297297

298-
m = reftable_decode_key(&roundtrip, &rt_extra, last_key, dest);
298+
strbuf_addstr(&roundtrip, "refs/heads/master");
299+
m = reftable_decode_key(&roundtrip, &rt_extra, dest);
299300
EXPECT(n == m);
300301
EXPECT(0 == strbuf_cmp(&key, &roundtrip));
301302
EXPECT(rt_extra == extra);

0 commit comments

Comments
 (0)