-
Notifications
You must be signed in to change notification settings - Fork 877
alignment-aware memory allocation #1605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
kazuho
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that we are in a very good direction. Thank you for working on the improvement.
I have laid down my concerns below. Please let me know what you think.
include/h2o/memory.h
Outdated
| void *h2o_mem_alloc_pool(h2o_mem_pool_t *pool, size_t sz); | ||
| void *h2o_mem_alloc_pool_aligned(h2o_mem_pool_t *pool, size_t alignment, size_t size); | ||
|
|
||
| #ifdef H2O_NO_BUILT_IN_ALIGNOF |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please test the name of the compiler using #if here to determine if we can use __alignof__, instead of determining it in CMakeFiles.txt.
The header file will be used from other applications and that might not involve the invocation of CMake.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not determining it in CMakeFiles.txt sounds good.
i searched on the web and seems most compilers support __alignof__.
if we use #if to judge, i'm confused about the judge condition.
is there any platform we need support but missing __alignof__?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a very good question.
I now think that we do not need a check. Let's expect that __alignof__ is always available. We already use things like __builtin_clzll; it is likely that the range of compilers we support will remain the same.
include/h2o/memory.h
Outdated
| #else | ||
| #define H2O_ALIGNOF(type) (__alignof__(type)) | ||
| #endif | ||
| #define H2O_ALIGN(x, a) (((x) + (a)-1) & ~((a)-1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would appreciate it if we could refrain from adding a public interface to calculate alignment. My understanding is that it is only used in memory.c.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, will be fixed later.
lib/common/memory.c
Outdated
| { | ||
| void *ret; | ||
|
|
||
| assert(alignment <= 2 * sizeof(void *) && sz % alignment == 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove this assert?
I do not think that alignment <= 2 * sizeof(void *) is something that is guaranteed on every platform. sz % alignment == 0 is going to be slow, and I do not think we need to test this considering how the function is used; it's essentially trying to detect a compiler bug in using the compiler itself.
Related to this, I think that we should eliminate st_h2o_mem_pool_chunk_t::_dummy. Instead, I would suggest changing the definition of st_h2o_mem_pool_chunk_t to something like below and start h2o_mem_pool_t::chunk_offset from offsetof(union un_h2o_mem_pool_chunk_t, next).
union un_h2o_mem_pool_chunk_t {
struct st_h2o_mem_pool_chunk_t *next;
char bytes[4096];
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed, please check commit 4dd533d
lib/common/memory.c
Outdated
| if (sz == 0) | ||
| sz = 1; | ||
| if (H2O_UNLIKELY(sz == 0)) | ||
| sz = alignment; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that h2o_mem_alloc_pool(int, 0) should return a unique pointer that is aligned for int. Hence keep the code as-is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean leave sz = 1 as-is?
seems we can do this.
even with sz = alignment, h2o_mem_alloc_pool(int, 0) also return a unique pointer that is aligned for int.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the changes.
FTR the issue was that there was a case that left the chunk_offset unchanged leading to the uniqueness not being guaranteed; for example when chunk_offset is a multiple of 4 and h2o_mem_alloc_pool(pool, uint32_t, 0) is called.
lib/common/memory.c
Outdated
| if (pool != NULL) { | ||
| new_entries = h2o_mem_alloc_pool(pool, element_size * vector->capacity); | ||
| new_entries = | ||
| h2o_mem_alloc_pool_aligned(pool, 2 * sizeof(void *), H2O_ALIGN(element_size * vector->capacity, 2 * sizeof(void *))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about passing the alignment requirement as an argument to h2o_vector__expand? Call to the function is wrapped via h2o_vector_reserve in which you can get the name of the typed element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed. please check commit 4dd533d.
kazuho
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the changes.
I think that only cosmetic issues are left. I have left my comments inline, please let me know what you think.
| { | ||
| pool->chunks = NULL; | ||
| pool->chunk_offset = sizeof(pool->chunks->bytes); | ||
| pool->chunk_offset = sizeof(*pool->chunks); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind elaborating why the change is necessary? I see that you have changed all the sizeofs related to the calculation of the buffer size.
My understanding is that pool->chunk_offset has been (and can continue to be) a offset for (st|un)_h2o_mem_pool_chunk_t::bytes, and I do not understand why you need to change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because now un_h2o_mem_pool_chunk_t is a union,
sizeof(*pool->chunks) is equal with sizeof(pool->chunks->bytes) and it seems more simpler.
we can also stay what it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the answer.
I have switched the code back to use sizeof ...bytes. In addition to being more natural (as stated in my comment above) it does not require the knowledge that 1) bytes is the largest member of a 2) union.
lib/common/memory.c
Outdated
| pool->chunk_offset = sizeof(union un_h2o_mem_pool_chunk_t *); | ||
| } | ||
|
|
||
| pool->chunk_offset = H2O_ALIGN(pool->chunk_offset, alignment); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to make this calculation before if (sizeof(*pool->chunks) - pool->chunk_offset < sz)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, we need make chunk_offset align before judge, also when new chunk is allocated.
lib/common/memory.c
Outdated
| void *ret; | ||
|
|
||
| if (sz >= sizeof(pool->chunks->bytes) / 4) { | ||
| if (sz >= sizeof(*pool->chunks) / 4) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this should be sz >= (sizeof(pool->chunks->bytes) - sizeof(pool->chunks->next)) / 4?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes.
we can change this if we want exactly the same as before.
lib/common/memory.c
Outdated
| newp->next = pool->chunks; | ||
| pool->chunks = newp; | ||
| pool->chunk_offset = 0; | ||
| pool->chunk_offset = sizeof(union un_h2o_mem_pool_chunk_t *); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we change the sizeof to sizeof(newp->next)?
Relying on the variable name is more prone to errors than copy-pasting the type of the variable name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, will be fixed.
alignment-aware memory allocation
|
Thank you for your work on the issue! I have merged the pull request after making some cosmetic changes. I think that all the changes are fine, but please let me know if you have any concern. |
issues #1597