Skip to content

Commit

Permalink
Avoid excessive reallocation in row compressors (timescale#6638)
Browse files Browse the repository at this point in the history
Memory operations can add up to tens of percents of the total
compression CPU load. To reduce the need for them, reserve for the
expected array sizes when initializing the compressor.
  • Loading branch information
akuzm authored Feb 14, 2024
1 parent 8743016 commit e44e0ad
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/adts/bit_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ typedef struct BitArray BitArray;
typedef struct BitArrayIterator BitArrayIterator;

/* Main Interface */
static void bit_array_init(BitArray *array);
static void bit_array_init(BitArray *array, int expected_bits);

/* Append num_bits to the array */
static void bit_array_append(BitArray *array, uint8 num_bits, uint64 bits);
Expand Down
4 changes: 2 additions & 2 deletions src/adts/bit_array_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ static inline void bit_array_wrap_internal(BitArray *array, uint32 num_buckets,
************************/

static inline void
bit_array_init(BitArray *array)
bit_array_init(BitArray *array, int expected_bits)
{
*array = (BitArray){
.bits_used_in_last_bucket = 0,
};
uint64_vec_init(&array->buckets, CurrentMemoryContext, 0);
uint64_vec_init(&array->buckets, CurrentMemoryContext, expected_bits / 64);
}

/* This initializes the bit array by wrapping buckets. Note, that the bit array will
Expand Down
7 changes: 5 additions & 2 deletions src/adts/vec.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,11 @@ VEC_RESERVE(VEC_TYPE *vec, uint32 additional)
if (num_new_elements == 0 || vec->num_elements + num_new_elements <= vec->max_elements)
return;

if (num_new_elements < vec->num_elements / 2)
num_new_elements = vec->num_elements / 2;
if (num_new_elements < vec->num_elements)
{
/* Follow the usual doubling progression of allocation sizes. */
num_new_elements = vec->num_elements;
}

num_elements = vec->num_elements + num_new_elements;
Assert(num_elements > vec->num_elements);
Expand Down
2 changes: 1 addition & 1 deletion test/src/adt_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ bit_array_test(void)
BitArray bits;
BitArrayIterator iter;
int i;
bit_array_init(&bits);
bit_array_init(&bits, 0);

for (i = 0; i < 65; i++)
bit_array_append(&bits, i, i);
Expand Down
14 changes: 12 additions & 2 deletions tsl/src/compression/gorilla.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,19 @@ gorilla_compressor_alloc(void)
GorillaCompressor *compressor = palloc(sizeof(*compressor));
simple8brle_compressor_init(&compressor->tag0s);
simple8brle_compressor_init(&compressor->tag1s);
bit_array_init(&compressor->leading_zeros);
/*
* The number of leading zeros takes about 5 bits to encode, and changes
* maybe every 100 rows, so use this as a conservative estimate.
*/
bit_array_init(&compressor->leading_zeros,
/* expected_bits = */ (GLOBAL_MAX_ROWS_PER_COMPRESSION * 5) / 100);
simple8brle_compressor_init(&compressor->bits_used_per_xor);
bit_array_init(&compressor->xors);
/*
* We typically see about 12 bits or 4 decimal digits per row for the "xors"
* part in gorilla compression.
*/
bit_array_init(&compressor->xors,
/* expected_bits = */ GLOBAL_MAX_ROWS_PER_COMPRESSION * 12);
simple8brle_compressor_init(&compressor->nulls);
compressor->has_nulls = false;
compressor->prev_leading_zeroes = 0;
Expand Down
16 changes: 14 additions & 2 deletions tsl/src/compression/simple8b_rle.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,20 @@ simple8brle_compressor_init(Simple8bRleCompressor *compressor)
.num_elements = 0,
.num_uncompressed_elements = 0,
};
uint64_vec_init(&compressor->compressed_data, CurrentMemoryContext, 0);
bit_array_init(&compressor->selectors);
/*
* It is good to have some estimate of the resulting size of compressed
* data, because it helps to allocate memory in advance to avoid frequent
* reallocations. Here we use a completely arbitrary but pretty realistic
* ratio of 10.
*/
const int expected_compression_ratio = 10;
uint64_vec_init(&compressor->compressed_data,
CurrentMemoryContext,
GLOBAL_MAX_ROWS_PER_COMPRESSION / expected_compression_ratio);
bit_array_init(&compressor->selectors,
/* expected_bits = */ (GLOBAL_MAX_ROWS_PER_COMPRESSION *
SIMPLE8B_BITS_PER_SELECTOR) /
expected_compression_ratio);
}

static void
Expand Down

0 comments on commit e44e0ad

Please sign in to comment.