#1252 closed Bugs (fixed)
[pool] severe overhead with unaligned sizes.
Reported by: | Owned by: | Chris Newbold | |
---|---|---|---|
Milestone: | To Be Determined | Component: | pool |
Version: | Boost 1.34.1 | Severity: | Problem |
Keywords: | pool | Cc: | Chris Newbold, kevin.ic@…, boostpool@…, jonathan.jones@… |
Description
Pool will set the size of allocated blocks to the least common multiple of the requested size and LCM(sizeof(void*), sizeof(size_t)).
Request a block size of 1500 on a 32-bit system and you get 1500 byte blocks. Request 1501 bytes and you get 6004 byte blocks - and the extra space isn't ever used. It is obviously even more pronounced on a 64-bit system.
I believe the intent of the author was to align block sizes, not to use LCM. I propose changing pool.hpp:183 from
return details::pool::lcm<size_type>(requested_size, min_size);
to
return requested_size + (min_size - 1) & ~(min_size - 1);
Change History (17)
comment:1 by , 15 years ago
Component: | None → pool |
---|---|
Owner: | set to |
comment:2 by , 15 years ago
comment:3 by , 15 years ago
No - this fix did not make in into 1.35. I need to write a test case to make sure that this does, indeed, fix the problem. Unless you would like to write one? ;-)
comment:4 by , 14 years ago
Milestone: | Boost 1.36.0 → Boost 1.37.0 |
---|---|
Status: | new → assigned |
Reading the design documents at http://www.boost.org/doc/libs/1_36_0/libs/pool/doc/implementation/alignment.html, I'm not convinced that you are correct.
Please read that page and tell me what you think.
follow-up: 7 comment:5 by , 14 years ago
Cc: | added |
---|
Here are my thoughts...
I believe that the design outlined in the linked documents is sound, including the use of LCM to find the chunk size which will satisfy the alignment requirements of the free list pointer and block size.
The problem with the original design is using requested_size in the LCM computation. Just because I asked for chunks of X bytes does not mean that each chunk must be aligned on an X byte boundary.
Predicate 2 of the "Guaranteeing Alignment" document quotes language from 5.3.4/10 of the C++ standard indicating that memory obtained in a 'new' expression must be aligned on a boundary that is a multiple of that required by the type with the most stringent alignment requirement. On most platforms, this is going to be double, or long long, or maybe long double.
The point being that the "worst-case" scenario is that the programmer tries to lay down a structure at the start of a chunk obtained from the pool and that structure has an element of such a type.
This is exactly what malloc() or operator new() does. I do not think that the pool needs to do anything more.
The proposed change in this ticket, however, is not correct. It does not account for the possibility that some type may have a more stringent alignment requirement than the free-list pointer and/or block size value.
comment:6 by , 14 years ago
As an aside, a couple of months back I stepped up and volunteered to take owner maintainership of Boost.Pool since it appeared to have been orphaned. If you'd like to wash your hands of this, just assign the ticket to me. :)
comment:7 by , 14 years ago
Sorry, I just got around to seeing this. I agree with cnewbold.
I think the alignment should be align(max(min_size, min(requested_size, sizeof(max_type))), 2).
I'm not familiar with other platforms but on x86/x64, sizeof(max_type) is now 32 bytes (for the new AVX instructions from Intel).
comment:8 by , 14 years ago
What is max_type?
After some further thought, I think that a clean solution would be to leverage alignment_of from Boost.Typetraits.
Change:
return details::pool::lcm<size_type>(requested_size, min_size);
to:
return details::pool::lcm<size_type>(alignment_of(requested_size), min_size);
comment:9 by , 14 years ago
Marshall--
I'm currently in the pool code fixing several other tickets and, if you like and if you think that my suggested fix above is correct, I'd be happy to apply it and write some test cases. Let me know... Thanks.
comment:10 by , 14 years ago
Owner: | changed from | to
---|---|
Status: | assigned → new |
Works for me - especially the tests. Pool seems to be lacking in tests. ;-)
comment:11 by , 14 years ago
Milestone: | Boost 1.37.0 → To Be Determined |
---|---|
Status: | new → assigned |
Not going to make 1.37; the changes are somewhat delicate and I'd like to have some bake time before releasing.
comment:12 by , 13 years ago
Cc: | added |
---|
Would it be more efficient to simply ignore the alignment requirements for the pointers, and just copy in and out of the memory using aligned addresses before using them?
So we can always allocate in multiples of requested_size (large enough to cover min_size). Then, to access embedded data such as the next pointer, we can do:
void* get_nextof(void const* ptr) { void* p; memcpy(&p, ptr, sizeof(void*)); return p; }
Seems like the copying penalty would be worth it for a more reasonable overhead.
comment:13 by , 12 years ago
Cc: | added |
---|
comment:15 by , 12 years ago
comment:16 by , 11 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
comment:17 by , 10 years ago
Cc: | added |
---|
I see the milestone for this is set to 1.35.0; does that mean that a fix for this problem has been (or will be) included in the upcoming release?