Setting part of an array to zero efficiently

92 Views Asked by At

I am trying to reset an array of simple objects to zero, as shown below:

using Node = std::array<std::uint16_t, 256>;
using Array = std::array<Node, 4096>;

void reset_1(Array& a) {
  std::fill(std::begin(a), std::end(a), Node{});
}
    
void reset_2(Array& a) {
  a = {};
}

void reset_3(Array& a) {
  a.fill(Node{});
}

All these functions get the job done, but looking at the compiler explorer (using both Clang 10.0.1 and gcc 10.2), reset_2 does it in one memset, while the other two do it in multiple chunks (same result using std::fill_n).

In my real case, I actually want to reset all nodes, except for the first one. This would lead me to write something like:

void reset_not_zero_1(Array& a) {
  std::fill(std::begin(a) + 1, std::end(a), Node{});
}
    
void reset_not_zero_2(Array& a) {
  std::memset(a.data() + 1, 0, sizeof(a) - sizeof(Node));
}

My question is: am I invoking some sort of undefined behavior with reset_not_zero_2? The array is trivially copyable, but maybe I am missing something.

2

There are 2 best solutions below

0
On

My question is: am I invoking some sort of undefined behavior with reset_not_zero_2?

No. Seems fine to me.

4
On

From cppreference

Converts the value ch to unsigned char and copies it into each of the first count characters of the object pointed to by dest. If the object is a potentially-overlapping subobject or is not TriviallyCopyable (e.g., scalar, C-compatible struct, or an array of trivially copyable type), the behavior is undefined. If count is greater than the size of the object pointed to by dest, the behavior is undefined.

So far so good.

assume another Node size is 14 but its alignas is 16

using Array = std::array<Node, 4096>;

size of 'a' would then be 16*4096 = 65536 as Node would force a padding of 2.

std::memset(a.data() + 1, 0, sizeof(a) - sizeof(Node));

This would then be 65536-14 out of 65536-16 bytes, which is not OK, asa it overwrites 2 bytes of the next data.

If on the other hand

sizeof(a) == a.size()*sizeof(a::value_type) 

Then it should be OK.

Also if alignas force a larger sizeof it should also be OK.