4

In some class of mine I have a std::vector<uint8_t> member.

1) What guarantees do I have regarding memory alignment of std::vector<uint8_t>::data()? I guess it is 1-byte aligned, i.e. not aligned. Right?

2) How can I make sure that the first element of that vector is 4-byte aligned?

I'm particularly interested in a solution for the ARM architecture (compiling with GCC 4.6), since I'm trying to fix a bus error which I get only on that platform. But it would be nice to have a platform independend solution, because I'm compiling the same code also for x86 with MSVC++ under Windows and with GCC under Linux.

Robert Hegner
  • 9,014
  • 7
  • 62
  • 98
  • 1
    I guess this is a case for a custom allocator. I never wrote one, and one says it is complicated. Alternative dirty solution: allocate `+3` (or `+N-1` where `N` is your alignment) elements, find out the misalignment, and offset all accesses. (Wrap this in a class `aligned_vector`). – leemes Feb 09 '15 at 08:01
  • 1
    Also see http://stackoverflow.com/questions/12942548/making-stdvector-allocate-aligned-memory and http://stackoverflow.com/questions/8456236/how-is-a-vectors-data-aligned . However, I'd say you should rather find the actual source of the bus error and fix that. – nos Feb 09 '15 at 08:14
  • `std::aligned_storage` might be a more appropriate class. – MSalters Feb 09 '15 at 08:21
  • @leemes I'm not sure if your dirty solution works. What if an automatic reallocation happens on inserting new elements? The calculated offset could be wrong after that... Looks like a custom allocator is the solution, as you said. Thanks to your links I became aware of Boost.Align which offers an aligned allocator. I'll try that first. – Robert Hegner Feb 09 '15 at 08:29
  • @RobertHegner Oh, you're right, I didn't think about reallocation. Thanks for pointing out. – leemes Feb 09 '15 at 08:30
  • Hang on, `uint8_t` shouldn't need any alignment - if you're really getting bus errors due to alignment faults, you should find out what's causing word/halfword accesses to unsanitised byte data, because it's wrong. – Notlikethat Feb 09 '15 at 09:53
  • @Notlikethat You're right, as long as I access the memory only using `uint8_t` there is no problem at all. But in my application I'm using the vector to hold serialized data (protocol buffer) which basically can be anything. So for example in some places I cast the pointer to `float*` in order to serialize/deserialize `float` values. This has never been a problem on x86. But now on ARM it turns out that I need to be careful with alignment. So what I do now is to make sure that the vector is 4-byte aligned and to add padding in my communication protocol where necessary. – Robert Hegner Feb 09 '15 at 10:05
  • @Robert Casting pointers in that way puts you in gool ol' undefined behaviour territory (x86 is the wretched hive of "undefined behaviour that appears to work as expected"). The 'correct' way to handle that is to `mempcy()` the `uint8_t[]` buffer into a properly-allocated `float[]` buffer, but obviously the 'pre-aligned vector' approach is nicer for performance. It might be worth implementing 8-byte alignment as per most `malloc()` implementations just in case you ever need to extend to `double` or other 64-bit types. – Notlikethat Feb 09 '15 at 10:33
  • @Notlikethat I try to avoid that additional copy of data, that's why I came up with this solution. But thanks for your advise. I will change to 8-byte alignment. – Robert Hegner Feb 09 '15 at 10:43
  • Guaranteeing he alignment of the buffer won't help. Protocol blocks doesn't guarantee alignment of the data within the buffer. Your code will only work if 1) the processor is little endian and 2) it supports mis-aligned data accesses. Both are true for Intel, but false for most other processors (although I don't know about ARM). Practically speaking, you have to extract fixed length sizes using shifts and or'ing. (For `float` and `double`, that only works if the processor uses IEEE format. But that's the case for most modern processors.) – James Kanze Feb 09 '15 at 11:03
  • 1
    And I don't quite see how the cited duplicate addresses his (implicit) question. I'll admit that it's partially his fault, since his last paragraph implies that his issue is elsewhere. (In practice, the default allocator for `std::vector` will ensure sufficient alignment, since it is required to use `::operator new`, and `::operator new` is required to return a pointer sufficiently aligned for all data types.) – James Kanze Feb 09 '15 at 11:07
  • @JamesKanze All platforms involved (x86, ARM) are little-endian and use IEEE float format. Of course, aligning my `uint8_t` buffer as a whole does not automatically mean that all data members within the buffer (which can be of type `float` or whatever) are properly aligned. But if I have some guarantees regarding the alignment of the whole buffer, I can design my message types in such a way that the relevant fields *are* properly aligned. This is what I meant by: "So what I do now is to make sure that the vector is 4-byte aligned and to add padding in my communication protocol where necessary" – Robert Hegner Feb 09 '15 at 12:44
  • 1
    @RobertHegner I thought that I had seen a reference to protocol blocks; protocol blocks doesn't allow such aligning. If you're sure of the byte order, then you can `memcpy` the data out (but in my experience, such certainties aren't future safe). As for guaranteeing alignment from a correctly aligned starting point, the usual arithmetic tricks (`(offset + 3) & ~3`) can be used on the offset. Or with some tricky casting, directly on a pointer. – James Kanze Feb 09 '15 at 13:03
  • @JamesKanze No I don't use protocol blocks, it's all hand made... In your previous comment you basically said that since the default allocator uses `new`, alignment is "sufficient". But what does it mean? 8-bytes? 16-bytes? And does this automatically give me guarantees about the alignment of the first element of the vector? (couldn't it be that the vector itself is 16-bytes aligned, but has some internal header in front of the first element which destroy the alignment for the first element?). So it all comes down to my original question about alignment guarantees I have for the first element. – Robert Hegner Feb 09 '15 at 13:23
  • 1
    Of course, if you're mostly going to be reinterpreting the data anyway, the _really_ easy way to get an n-byte buffer guaranteed to start on the correct alignment for a `float` is `new std::vector((n+sizeof(float)-1)/sizeof(float))`. Might complicate other areas a little, though. – Notlikethat Feb 09 '15 at 21:41
  • @RobertHegner Concerning `::operator new`: "The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function)." As for `std::vector` not starting at the starting address given by the allocator, I don't think that there's any formal guarantee, but I can't imagine an implementation doing otherwise. – James Kanze Feb 10 '15 at 12:33
  • @RobertHegner But the whole concept of `reinterpret_cast`ing a pointer into the vector and then accessing the bytes as a `float` or whatever is so fragile and implementation dependent anyway, you might as well go with implementation dependent code. – James Kanze Feb 10 '15 at 12:37

0 Answers0