Opened 7 years ago

Closed 6 years ago

Last modified 6 years ago

#11312 closed Bugs (fixed)

smart_ptr::detail::as_allocator::allocate() adds alignment padding when it's not needed

Reported by: David Aue <david@…> Owned by: Glen Fernandes
Milestone: Boost 1.64.0 Component: smart_ptr
Version: Boost 1.57.0 Severity: Problem
Keywords: Cc:

Description

smart_ptr::detail::as_allocator::allocate() {

enum {

M = boost::alignment_of<type>::value

}; std::size_t n1 = count * sizeof(value_type); std::size_t n2 = data.size * sizeof(type); std::size_t n3 = n2 + M; CA ca(allocator()); void* p1 = ca.allocate(n1 + n3);

Allocation is for two items of size n1 and n2. Alignment size is added to n2 to create n3 to there is room to align n2 properly.

if n1 % M == 0 then alignment is already fine and the extra M bytes is wasted.

Change History (15)

comment:1 by Peter Dimov, 7 years ago

I don't see why. We can only depend on the alignment of p1 as being suitable for an object of type value_type. n1 % M == 0 doesn't seem to give us anything.

comment:2 by Peter Dimov, 7 years ago

Owner: changed from Peter Dimov to Glen Fernandes

comment:3 by David Aue <david@…>, 7 years ago

pointer allocate(size_type count, const_void_pointer = 0) {
    enum {
        M = boost::alignment_of<type>::value
    };
    std::size_t n1 = count * sizeof(value_type);
    std::size_t n2 = data.size * sizeof(type);
    std::size_t n3 = n2 + M;
    CA ca(allocator());
    void* p1 = ca.allocate(n1 + n3);
    void* p2 = static_cast<char*>(p1) + n1;
    (void)boost::alignment::align(M, n2, p2, n3);
    *data.result = static_cast<type*>(p2);
    return static_cast<value_type*>(p1);
}


I call:
boost::allocate_shared<int[]>(allocator, 4);

Inside allocate() I see this:

n1 == 20 for ms_init_tag
n2 == 16 for the 4 ints
M == 4
n3 == 20

ca.allocate is called with n1+n3 for a total of 40 bytes.

p1 == some_address
p2 == some_address + n1
boost::alignment::align called to fix p2 for 4 byte alignment
p2 is unchanged by align

So we allocated 40 bytes and we are using the first 20 for ms_init and the next 16 for the int[4] and then there are 4 unused bytes that are not affecting our 4 byte alignment requirement.

comment:4 by Glen Fernandes, 7 years ago

One optimization I could do is instead of adding alignof(type) bytes:

  • If alignof(value_type) >= align(type) then do not add any additional padding.
  • Else add alignof(type) - 1 bytes for padding.

comment:5 by David Aue <david@…>, 7 years ago

That sounds perfect.

in reply to:  5 comment:6 by Glen Fernandes, 7 years ago

In fact, one step further:

  • If alignof(value_type) >= align(type) then do not add any additional padding.
  • Else add alignof(type) - alignof(value_type) bytes for padding.

comment:7 by Peter Dimov, 7 years ago

CA is allocator<char> and there is no guarantee for p1 to be aligned at anything, regardless of what the alignment of value_type is.

Although I suppose that the code already assumes that p1 is aligned at alignof(value_type) anyway. But this assumption doesn't seem quite correct.

comment:8 by Glen Fernandes, 7 years ago

Good point. The above math was under the assumption that for any value_type the allocate() function for any given allocator would yield memory that is at least alignof(std::max_align_t) bytes aligned.

It doesn't seem right to make this assumption, and nothing in [allocator.requirements] provides such a guarantee anyway, so I'll update the implementation accordingly.

comment:9 by Peter Dimov, 7 years ago

We should probably use ::rebind<type_with_alignment<alignof(value_type)>::type> to allocate, and then the optimization would hold.

comment:10 by Glen Fernandes, 7 years ago

But type_with_alignment<alignof(value_type)>::type wouldn't have size 1, right? It would have size at least alignof(value_type) bytes and allocate() takes number of objects.

comment:11 by Peter Dimov, 7 years ago

Yes, of course. The number of objects would need to be adjusted accordingly.

comment:12 by Glen Fernandes, 6 years ago

Version: Boost 1.57.0Boost 1.63.0

comment:13 by Glen Fernandes, 6 years ago

Milestone: To Be DeterminedBoost 1.64.0
Resolution: fixed
Status: newclosed

This should be fixed in develop now after I rewrote all of allocate_shared for arrays. It will probably release with 1.65 unless the tests have enough time to cycle for 1.64 and there are no issues.

comment:14 by Glen Fernandes, 6 years ago

Milestone: Boost 1.64.0Boost 1.65.0
Version: Boost 1.63.0Boost 1.57.0

Will release in 1.64.

Last edited 6 years ago by Glen Fernandes (previous) (diff)

comment:15 by Glen Fernandes, 6 years ago

Milestone: Boost 1.65.0Boost 1.64.0
Note: See TracTickets for help on using tickets.