Skip to content

Commit 37c730b

Browse files
update logic
1 parent b5fe351 commit 37c730b

4 files changed

Lines changed: 85 additions & 8 deletions

File tree

include/aws/s3/private/s3_default_buffer_pool.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ struct aws_s3_default_buffer_pool {
9191
/* size at which allocations should go to secondary */
9292
size_t primary_size_cutoff;
9393

94+
/* size below which allocations should go to secondary */
95+
size_t primary_size_min_cutoff;
96+
9497
/* NOTE: See aws_s3_buffer_pool_usage_stats for descriptions of most fields */
9598

9699
size_t mem_limit;

source/s3_default_buffer_pool.c

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,20 @@
4545

4646
struct aws_s3_default_buffer_pool;
4747

48+
enum aws_s3_default_buffer_pool_reserved_from {
49+
AWS_S3_BUFFER_POOL_RESERVED_FROM_PRIMARY,
50+
AWS_S3_BUFFER_POOL_RESERVED_FROM_SECONDARY,
51+
AWS_S3_BUFFER_POOL_RESERVED_FROM_SPECIAL,
52+
};
53+
4854
struct aws_s3_default_buffer_ticket {
4955
size_t size;
5056
uint8_t *ptr;
5157
size_t chunks_used;
5258
bool forced;
5359
struct aws_s3_buffer_pool *pool;
5460
bool is_special_block; /* True if this ticket is from a special-sized block */
61+
enum aws_s3_default_buffer_pool_reserved_from reserved_from /* which area ticket was reserved from */
5562
};
5663

5764
/* Default size for blocks array. Note: this is just for meta info, blocks
@@ -82,6 +89,12 @@ static const size_t s_max_chunk_size_for_buffer_reuse = MB_TO_BYTES(64);
8289
* we still consider 1GiB available for normal buffer usage. */
8390
static const size_t s_max_impact_of_forced_buffers_on_memory_limit_as_percentage = 80;
8491

92+
/*
93+
* primary min cut off. below this number buffers are not allocated from primary and instead use
94+
* secondary.
95+
*/
96+
static const size_t s_primary_min_cutoff = KB_TO_BYTES(128);
97+
8598
/*
8699
* Sets n bits at position starting with LSB.
87100
* Note: n must be at most 8, but in practice will always be at most 4.
@@ -232,6 +245,18 @@ struct aws_s3_buffer_pool *aws_s3_default_buffer_pool_new(
232245
chunk_size = 0;
233246
}
234247

248+
/*
249+
* This is fairly arbitrary. System allocator tends to be a bit better on tiny allocations.
250+
* And we want to avoid wasting mem in that case.
251+
* Note this is not the answer for wasting mem, but it does improve some corner cases.
252+
*/
253+
size_t min_primary_size = 0;
254+
if (4 * chunk_size >= s_primary_min_cutoff) {
255+
min_primary_size = s_primary_min_cutoff;
256+
} else {
257+
min_primary_size = 0;
258+
}
259+
235260
size_t page_size = aws_system_info_page_size();
236261
/* TODO: if people override their allocator, this will not wrap their allocator at all. eg: mem-tracing allocator
237262
* will be ignored. */
@@ -248,6 +273,7 @@ struct aws_s3_buffer_pool *aws_s3_default_buffer_pool_new(
248273
* Tries to balance between how many allocations use buffer and buffer space
249274
* being wasted. */
250275
buffer_pool->primary_size_cutoff = chunk_size * 4;
276+
buffer_pool->primary_size_min_cutoff = min_primary_size;
251277
buffer_pool->mem_limit = adjusted_mem_lim;
252278

253279
int mutex_error = aws_mutex_init(&buffer_pool->mutex);
@@ -395,16 +421,18 @@ static void s_aws_ticket_wrapper_destroy(void *data) {
395421

396422
if (ticket->ptr == NULL) {
397423
/* Ticket was never used, make sure to clean up reserved count. */
398-
if (ticket->is_special_block) {
424+
if (ticket->reserved_from == AWS_S3_BUFFER_POOL_RESERVED_FROM_SPECIAL) {
399425
buffer_pool->special_blocks_reserved -= ticket->size;
400-
} else if (ticket->size <= buffer_pool->primary_size_cutoff) {
426+
} else if (ticket->reserved_from == AWS_S3_BUFFER_POOL_RESERVED_FROM_PRIMARY) {
401427
buffer_pool->primary_reserved -= ticket->size;
402-
} else {
428+
} else if (ticket->reserved_from == AWS_S3_BUFFER_POOL_RESERVED_FROM_SECONDARY) {
403429
buffer_pool->secondary_reserved -= ticket->size;
430+
} else {
431+
AWS_ASSERT(false); /* someone forgot to update this after adding new member. */
404432
}
405433
} else {
406434
/* Handle special block returns */
407-
if (ticket->is_special_block) {
435+
if (ticket->reserved_from == AWS_S3_BUFFER_POOL_RESERVED_FROM_SPECIAL) {
408436
struct aws_hash_element *elem = NULL;
409437
aws_hash_table_find(&buffer_pool->special_blocks, (void *)ticket->size, &elem);
410438
/* TODO: the lifetime between the ticket and the special block, let's assume the block will always be
@@ -427,7 +455,7 @@ static void s_aws_ticket_wrapper_destroy(void *data) {
427455
/* Make sure we do find where the ticket buffer from the list. */
428456
AWS_FATAL_ASSERT(found);
429457
buffer_pool->special_blocks_used -= ticket->size;
430-
} else if (ticket->size <= buffer_pool->primary_size_cutoff) {
458+
} else if (ticket->reserved_from == AWS_S3_BUFFER_POOL_RESERVED_FROM_PRIMARY) {
431459

432460
size_t chunks_used = ticket->size / buffer_pool->chunk_size;
433461
if (ticket->size % buffer_pool->chunk_size != 0) {
@@ -455,13 +483,15 @@ static void s_aws_ticket_wrapper_destroy(void *data) {
455483
if (ticket->forced) {
456484
buffer_pool->forced_used -= ticket->size;
457485
}
458-
} else {
486+
} else if (ticket->reserved_from == AWS_S3_BUFFER_POOL_RESERVED_FROM_SECONDARY) {
459487
aws_mem_release(buffer_pool->base_allocator, ticket->ptr);
460488
buffer_pool->secondary_used -= ticket->size;
461489

462490
if (ticket->forced) {
463491
buffer_pool->forced_used -= ticket->size;
464492
}
493+
} else {
494+
AWS_ASSERT(false); /* someone forgot to update this after adding new member. */
465495
}
466496
}
467497
aws_mem_release(buffer_pool->base_allocator, ticket);
@@ -609,10 +639,18 @@ struct aws_s3_default_buffer_ticket *s_try_reserve_synced(
609639
/* Check if this is a special-sized allocation */
610640
if (from_special) {
611641
ticket->is_special_block = true;
642+
ticket->reserved_from = AWS_S3_BUFFER_POOL_RESERVED_FROM_SPECIAL;
612643
buffer_pool->special_blocks_reserved += meta.size;
613-
} else if (meta.size <= buffer_pool->primary_size_cutoff) {
614-
buffer_pool->primary_reserved += meta.size;
644+
} else if (meta.size <= buffer_pool->primary_size_cutoff) { /* TODO: this needs to be smarter. what if block size more than mem_lim*/
645+
if (meta.size <= buffer_pool->primary_size_min_cutoff) {
646+
ticket->reserved_from = AWS_S3_BUFFER_POOL_RESERVED_FROM_SECONDARY;
647+
buffer_pool->secondary_reserved += meta.size;
648+
} else {
649+
ticket->reserved_from = AWS_S3_BUFFER_POOL_RESERVED_FROM_PRIMARY;
650+
buffer_pool->primary_reserved += meta.size;
651+
}
615652
} else {
653+
ticket->reserved_from = AWS_S3_BUFFER_POOL_RESERVED_FROM_SECONDARY;
616654
buffer_pool->secondary_reserved += meta.size;
617655
}
618656
}

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ add_test_case(test_s3_buffer_pool_special_size_reuse)
447447
add_test_case(test_s3_buffer_pool_special_size_with_limits)
448448
add_test_case(test_s3_buffer_pool_special_size_mixed)
449449
add_test_case(test_s3_buffer_pool_special_size_trim)
450+
add_test_case(test_s3_buffer_pool_reserve_tiny_chunks)
450451

451452
add_net_test_case(client_update_upload_part_timeout)
452453
add_net_test_case(client_meta_request_override_part_size)

tests/s3_default_buffer_pool_tests.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,3 +635,38 @@ static int s_test_s3_buffer_pool_forced_buffer_wont_stop_reservations(struct aws
635635
AWS_TEST_CASE(
636636
test_s3_buffer_pool_forced_buffer_wont_stop_reservations,
637637
s_test_s3_buffer_pool_forced_buffer_wont_stop_reservations)
638+
639+
640+
/* Test small requests reserve expected amount of mem without rounding up to part size. */
641+
static int s_test_s3_buffer_pool_reserve_tiny_chunks(struct aws_allocator *allocator, void *ctx) {
642+
(void)ctx;
643+
const size_t chunk_size = MB_TO_BYTES(8);
644+
const size_t small_size = MB_TO_BYTES(1);
645+
const size_t mem_limit = GB_TO_BYTES(1);
646+
struct aws_s3_buffer_pool *buffer_pool = aws_s3_default_buffer_pool_new(
647+
allocator, (struct aws_s3_buffer_pool_config){.part_size = chunk_size, .memory_limit = mem_limit});
648+
649+
struct aws_s3_buffer_ticket *ticket = NULL;
650+
struct aws_future_s3_buffer_ticket *future =
651+
aws_s3_default_buffer_pool_reserve(buffer_pool, (struct aws_s3_buffer_pool_reserve_meta){.size = small_size});
652+
ASSERT_TRUE(aws_future_s3_buffer_ticket_is_done(future));
653+
ASSERT_INT_EQUALS(aws_future_s3_buffer_ticket_get_error(future), AWS_OP_SUCCESS);
654+
normal_ticket = aws_future_s3_buffer_ticket_get_result_by_move(future);
655+
struct aws_byte_buf buf = aws_s3_buffer_ticket_claim(ticket);
656+
ASSERT_UINT_EQUALS(small, buf.capacity);
657+
658+
struct aws_s3_default_buffer_pool_usage_stats stats = aws_s3_default_buffer_pool_get_usage(buffer_pool);
659+
ASSERT_INT_EQUALS(small_size, stats.secondary_used);
660+
661+
aws_s3_buffer_ticket_release(ticket);
662+
aws_future_s3_buffer_ticket_release(future);
663+
664+
ASSERT_INT_EQUALS(0, stats.secondary_used);
665+
666+
/* Cleanup */
667+
aws_s3_default_buffer_pool_destroy(buffer_pool);
668+
return 0;
669+
}
670+
AWS_TEST_CASE(
671+
test_s3_buffer_pool_reserve_tiny_chunks,
672+
s_test_s3_buffer_pool_reserve_tiny_chunks)

0 commit comments

Comments
 (0)