застрял на понимании выравнивания памяти - PullRequest
1 голос
/ 18 мая 2019

Код , выделяющий память с изображением width * height, выглядит следующим образом:

  const size_t alignment = 64;
  size_t space = width * height * bytes_per_pixel + alignment;
  rawdata = new unsigned char[space];
  //typedef unsigned long int uintptr_t
  uintptr_t ptr = reinterpret_cast<uintptr_t>(rawdata); 
  uintptr_t aligned = (ptr - 1u + alignment) & -alignment; 
  data = reinterpret_cast<unsigned char *>(aligned);

Похоже, что 64-байтовое выравнивание было выполнено на rawdata (то есть первоначально выделенной памяти), которая генерировала выровненную память, указанную data. Но что меня озадачило, так это строчка:

uintptr_t aligned = (ptr - 1u + alignment) & -alignment

Кто-нибудь может мне помочь?

1 Ответ

3 голосов
/ 18 мая 2019

Этот расчет обеспечивает соответствие адреса указанному количеству (которое должно быть степенью 2). Это означает, что младшие n биты должны быть равны нулю, когда выравнивание равно 2^n.

Давайте сделаем это в двоичном виде. Предположим, мы получили случайный указатель, выровненный на 16 байтов, в то время как мы хотим, чтобы он был выровнен на 64 байта, и вычислили. (Это предполагает два дополнения, что, кстати, не гарантируется, но является стандартом де-факто):

address = ...1101010000
address - 1 -> ...1101001111
address + 64 -> ...1110001111
-alignment -> ...1111000000
address & -alignment -> ...1110000000

Таким образом, в действительности он находит наименьшее значение, которое делится на выравнивание из-за того, что -alignment имеет все биты ноль ниже точки выравнивания. Он также гарантирует, что он больше, чем исходный указатель, добавив alignment-1, который является отрицанием -alignment в качестве битов, то есть все старшие биты равны нулю, а младшие - единица.

Что если адрес уже выровнен? Затем в результате вычисления получается исходный указатель, поскольку у него младшие биты равны нулю, все младшие биты равны единице, а затем их И.

...