Download raw body.
speed up reading of deltas a bit
No feedback on this one yet. Should I just go ahead?
On Fri, May 20, 2022 at 02:04:26PM +0200, Stefan Sperling wrote:
> Testing gotadmin pack with a pack file which contains proper
> deltas, performance bottlenecks show up elsewhere.
> Reading deltified tree objects is actually quite slow :-/
>
> This patch speeds things up a little by not reading deltas twice
> during expansion. The old code first reads in all deltas to find
> the largest delta in the chain, then allocates appropriately-sized
> buffers to process the chain again and actually apply the deltas.
> The delta cache may help a little to avoid overhead from reading
> the same deltas again, but it is better to avoid doing any
> unnecessary work in the first place.
>
> With this patch we do everything in one pass, and grow our buffers
> as needed when a larger delta appears somewhere in the chain.
>
> In got_pack_dump_delta_chain_to_file() the same approach can be
> used to decide whether to apply a delta in memory or to a tempfile.
> We start out with memory buffers if possible and eventually switch
> to files if needed. For simplicity, once we have switched to files
> we won't go back to using memory buffers, even if delta sizes shrink.
>
> More performance improvements will be needed, but this is a start.
>
> ok?
>
> diff refs/heads/main refs/heads/delta-membuf
> blob - 4bac59b80e15c0c64099a61c98b1dd60cd3e01a4
> blob + 2c612a33c5d96977b8bbe4ac08c2fc41cc78381b
> --- lib/pack.c
> +++ lib/pack.c
> @@ -1257,34 +1257,25 @@ got_pack_dump_delta_chain_to_file(size_t *result_size,
> const struct got_error *err = NULL;
> struct got_delta *delta;
> uint8_t *base_buf = NULL, *accum_buf = NULL, *delta_buf;
> - size_t base_bufsz = 0, accum_size = 0, delta_len;
> - uint64_t max_size;
> - int n = 0;
> -
> - *result_size = 0;
> -
> - if (STAILQ_EMPTY(&deltas->entries))
> - return got_error(GOT_ERR_BAD_DELTA_CHAIN);
> -
> + size_t base_bufsz = 0, accum_bufsz = 0, accum_size = 0, delta_len;
> /* We process small enough files entirely in memory for speed. */
> - err = got_pack_get_delta_chain_max_size(&max_size, deltas, pack);
> - if (err)
> - return err;
> - if (max_size < GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
> - accum_buf = malloc(max_size);
> - if (accum_buf == NULL)
> - return got_error_from_errno("malloc");
> - base_file = NULL;
> - accum_file = NULL;
> - } else {
> - if (fseeko(base_file, 0L, SEEK_SET) == -1)
> - return got_error_from_errno("fseeko");
> - if (fseeko(accum_file, 0L, SEEK_SET) == -1)
> - return got_error_from_errno("fseeko");
> - }
> + const size_t max_bufsize = GOT_DELTA_RESULT_SIZE_CACHED_MAX;
> + uint64_t max_size = 0;
> + int n = 0;
>
> + *result_size = 0;
> +
> + if (STAILQ_EMPTY(&deltas->entries))
> + return got_error(GOT_ERR_BAD_DELTA_CHAIN);
> +
> + if (fseeko(base_file, 0L, SEEK_SET) == -1)
> + return got_error_from_errno("fseeko");
> + if (fseeko(accum_file, 0L, SEEK_SET) == -1)
> + return got_error_from_errno("fseeko");
> +
> /* Deltas are ordered in ascending order. */
> STAILQ_FOREACH(delta, &deltas->entries, entry) {
> + uint64_t base_size, result_size = 0;
> int cached = 1;
> if (n == 0) {
> size_t mapoff;
> @@ -1311,7 +1302,9 @@ got_pack_dump_delta_chain_to_file(size_t *result_size,
> goto done;
> }
> }
> - if (base_file) {
> + if (delta->size > max_size)
> + max_size = delta->size;
> + if (max_size > max_bufsize) {
> if (pack->map) {
> mapoff = (size_t)delta_data_offset;
> err = got_inflate_to_file_mmap(
> @@ -1323,6 +1316,12 @@ got_pack_dump_delta_chain_to_file(size_t *result_size,
> &base_bufsz, NULL, NULL, pack->fd,
> base_file);
> } else {
> + accum_buf = malloc(max_size);
> + if (accum_buf == NULL) {
> + err = got_error_from_errno("malloc");
> + goto done;
> + }
> + accum_bufsz = max_size;
> if (pack->map) {
> mapoff = (size_t)delta_data_offset;
> err = got_inflate_to_mem_mmap(&base_buf,
> @@ -1337,7 +1336,7 @@ got_pack_dump_delta_chain_to_file(size_t *result_size,
> if (err)
> goto done;
> n++;
> - if (base_file)
> + if (base_buf == NULL)
> rewind(base_file);
> continue;
> }
> @@ -1359,6 +1358,50 @@ got_pack_dump_delta_chain_to_file(size_t *result_size,
> goto done;
> }
> }
> +
> + err = got_delta_get_sizes(&base_size, &result_size,
> + delta_buf, delta_len);
> + if (err)
> + goto done;
> + if (base_size > max_size)
> + max_size = base_size;
> + if (result_size > max_size)
> + max_size = result_size;
> +
> + if (base_buf && max_size > max_bufsize) {
> + /* Switch from buffers to temporary files. */
> + size_t w = fwrite(base_buf, 1, base_bufsz,
> + base_file);
> + if (w != base_bufsz) {
> + err = got_ferror(outfile, GOT_ERR_IO);
> + goto done;
> + }
> + free(base_buf);
> + base_buf = NULL;
> + free(accum_buf);
> + accum_buf = NULL;
> + }
> +
> + if (base_buf && max_size > base_bufsz) {
> + uint8_t *p = realloc(base_buf, max_size);
> + if (p == NULL) {
> + err = got_error_from_errno("realloc");
> + goto done;
> + }
> + base_buf = p;
> + base_bufsz = max_size;
> + }
> +
> + if (accum_buf && max_size > accum_bufsz) {
> + uint8_t *p = realloc(accum_buf, max_size);
> + if (p == NULL) {
> + err = got_error_from_errno("realloc");
> + goto done;
> + }
> + accum_buf = p;
> + accum_bufsz = max_size;
> + }
> +
> if (base_buf) {
> err = got_delta_apply_in_mem(base_buf, base_bufsz,
> delta_buf, delta_len, accum_buf,
> @@ -1380,25 +1423,11 @@ got_pack_dump_delta_chain_to_file(size_t *result_size,
> /* Accumulated delta becomes the new base. */
> if (base_buf) {
> uint8_t *tmp = accum_buf;
> - /*
> - * Base buffer switches roles with accumulation
> - * buffer. Ensure it can hold the largest
> - * result in the delta chain. The initial
> - * allocation might have been smaller.
> - */
> - if (base_bufsz < max_size) {
> - uint8_t *p;
> - p = reallocarray(base_buf, 1, max_size);
> - if (p == NULL) {
> - err = got_error_from_errno(
> - "reallocarray");
> - goto done;
> - }
> - base_buf = p;
> - base_bufsz = max_size;
> - }
> + size_t tmp_size = accum_bufsz;
> accum_buf = base_buf;
> + accum_bufsz = base_bufsz;
> base_buf = tmp;
> + base_bufsz = tmp_size;
> } else {
> FILE *tmp = accum_file;
> accum_file = base_file;
> @@ -1430,8 +1459,8 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
> const struct got_error *err = NULL;
> struct got_delta *delta;
> uint8_t *base_buf = NULL, *accum_buf = NULL, *delta_buf;
> - size_t base_bufsz = 0, accum_size = 0, delta_len;
> - uint64_t max_size;
> + size_t base_bufsz = 0, accum_bufsz = 0, accum_size = 0, delta_len;
> + uint64_t max_size = 0;
> int n = 0;
>
> *outbuf = NULL;
> @@ -1440,15 +1469,9 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
> if (STAILQ_EMPTY(&deltas->entries))
> return got_error(GOT_ERR_BAD_DELTA_CHAIN);
>
> - err = got_pack_get_delta_chain_max_size(&max_size, deltas, pack);
> - if (err)
> - return err;
> - accum_buf = malloc(max_size);
> - if (accum_buf == NULL)
> - return got_error_from_errno("malloc");
> -
> /* Deltas are ordered in ascending order. */
> STAILQ_FOREACH(delta, &deltas->entries, entry) {
> + uint64_t base_size, result_size = 0;
> int cached = 1;
> if (n == 0) {
> size_t delta_data_offset;
> @@ -1467,6 +1490,10 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
> err = got_error(GOT_ERR_PACK_OFFSET);
> goto done;
> }
> +
> + if (delta->size > max_size)
> + max_size = delta->size;
> +
> if (pack->map) {
> size_t mapoff = (size_t)delta_data_offset;
> err = got_inflate_to_mem_mmap(&base_buf,
> @@ -1505,6 +1532,36 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
> goto done;
> }
> }
> +
> + err = got_delta_get_sizes(&base_size, &result_size,
> + delta_buf, delta_len);
> + if (err)
> + goto done;
> + if (base_size > max_size)
> + max_size = base_size;
> + if (result_size > max_size)
> + max_size = result_size;
> +
> + if (max_size > base_bufsz) {
> + uint8_t *p = realloc(base_buf, max_size);
> + if (p == NULL) {
> + err = got_error_from_errno("realloc");
> + goto done;
> + }
> + base_buf = p;
> + base_bufsz = max_size;
> + }
> +
> + if (max_size > accum_bufsz) {
> + uint8_t *p = realloc(accum_buf, max_size);
> + if (p == NULL) {
> + err = got_error_from_errno("realloc");
> + goto done;
> + }
> + accum_buf = p;
> + accum_bufsz = max_size;
> + }
> +
> err = got_delta_apply_in_mem(base_buf, base_bufsz,
> delta_buf, delta_len, accum_buf,
> &accum_size, max_size);
> @@ -1517,24 +1574,11 @@ got_pack_dump_delta_chain_to_mem(uint8_t **outbuf, siz
> if (n < deltas->nentries) {
> /* Accumulated delta becomes the new base. */
> uint8_t *tmp = accum_buf;
> - /*
> - * Base buffer switches roles with accumulation buffer.
> - * Ensure it can hold the largest result in the delta
> - * chain. Initial allocation might have been smaller.
> - */
> - if (base_bufsz < max_size) {
> - uint8_t *p;
> - p = reallocarray(base_buf, 1, max_size);
> - if (p == NULL) {
> - err = got_error_from_errno(
> - "reallocarray");
> - goto done;
> - }
> - base_buf = p;
> - base_bufsz = max_size;
> - }
> + size_t tmp_size = accum_bufsz;
> accum_buf = base_buf;
> + accum_bufsz = base_bufsz;
> base_buf = tmp;
> + base_bufsz = tmp_size;
> }
> }
>
>
>
speed up reading of deltas a bit