Почему мы всегда объявляем константы степенями 2? - PullRequest
10 голосов
/ 08 августа 2009

Большинство констант, которые я вижу в коде других, являются степенями 2, то есть

#define SIZE 256

или

public static final int SIZE = 2048;

Есть ли какая-то особая причина, почему мы делаем это вместо того, чтобы

#define SIZE 257

Ответы [ 9 ]

29 голосов
/ 08 августа 2009

Полномочия 2 удобны, потому что они хорошо соответствуют основным аппаратным ограничениям.

Например:

  1. Размер страницы
  2. Пределы адресного пространства
  3. Содержит выравнивание (выравнивание DWORD или QWORD, обе степени 2)

Для флагов степени 2 всегда имеют один установленный бит. Так что такие вещи, как MY_FLAG_1 | MY_FLAG_2 | MY_FLAG_3 | ... работают только со степенью двойки. Аналогично для тестирования флагов с &.

Это также что-то вроде соглашения, чтобы выбрать ближайшую большую степень два для размеров буфера и тому подобное.

18 голосов
/ 08 августа 2009

Одна веская причина - битовая маскировка . Например, если вы используете константы для представления атрибутов объекта (или чего-либо еще), вы можете сохранить множество атрибутов в одно целое число посредством битовой маскировки и позже идентифицировать отдельные атрибуты. Отлично подходит для сохранения многих «флагов» в базе данных.

4 голосов
/ 08 августа 2009

Память, как правило, выделяется в виде нескольких размеров страниц из операционной системы, и во многих случаях полезно расположить вещи так, чтобы они точно соответствовали страницам (чтобы не тратить память). Это зависит от конкретной процедуры распределения, действительно ли это помогает; например если существует неявный заголовок, то размер, равный степени два, может действительно повредить.

2 голосов
/ 08 августа 2009

Когда дело доходит до размеров массива, я подозреваю, что есть две причины, почему предпочтение отдается двум степеням. Один из них, о чем свидетельствуют несколько ответов, заключается в том, что программисты, которые не знают, что происходит «под капотом», как представляется, имеют общее представление о том, что может быть более эффективно использовать степень двойки. Другой (в основном исторический сейчас) связан с циклическими буферами.

Циклические буферы, имеющие степень двойки, могут обрабатываться легче и быстрее, используя маски для индексов чтения или записи (или указатели), вместо использования обычно более медленной операции по модулю или использования условий, которые требуют ветвлений. Это было важно на старых машинах, но все еще может быть важно для передачи больших объемов данных - например, обработка графики

Например, в C число байтов, доступных для чтения в циклическом буфере, может быть получено с помощью:

pending = (SIZE + wr - rd) & (SIZE - 1);

Если не использовать степень двойки, эквивалент будет:

pending = (SIZE + wr - rd) % (SIZE - 1);

На машинах, которые не реализуют инструкцию деления / модуля, этот маленький "%" может занять несколько сотен циклов, поэтому вам потребуется что-то вроде:

if (wr >= rd)
    pending = wr - rd;
else
    pending = (SIZE + wr) - rd;

Который загромождает код и вызывает ветвление, которое может остановить конвейер команд.

Запись в буфер, который был что-то вроде

buf[wr++] = x;
if (wr == SIZE)
    rd = 0;

становится (в целом) более эффективным:

buf[wr++] = x;
wr &= SIZE-1;

Конечно, если вы использовали 8-битную переменную без знака для индексации массива из 256 записей, вам даже не нужно было выполнять маскирование.

2 голосов
/ 08 августа 2009

Наши переменные размеры - степени 2 (1, 2, 4 или 8 байтов). Компьютер комфортно работает на этих границах. В старые времена мы аккуратно дополняли наши структуры, чтобы ускорить работу нашего кода, а иногда и упростить арифметику указателей.

Если у вас есть выбор между размером 256 и 257, мы выбираем 256. Одной из причин будет отладка. Когда вы просматриваете память или файл, ваш отладчик или программа просмотра шестнадцатеричных файлов будут показывать данные в строках, равных двум.

Вот тот, который показывает 16 байтов на строку, в группах по 4.

alt text
(источник: wikimedia.org )

Для флагов мы делаем их степенью двойки, чтобы мы могли обрабатывать их все отдельно в одной переменной, а не во многих переменных или массиве.

Таким образом, все они могут быть или могут быть и есть из одной и той же переменной.

bits |= 8;       //00000100
bits |= 32;      //00010000

//bits is now 40   00010100

bits &= 32;      //00010000

//bits is now 32   00010000

Многие программисты пишут числа в шестнадцатеричном, а не в десятичном виде, чтобы было легче увидеть, что происходит с отдельными битами.

1 голос
/ 08 августа 2009

Добавление большего количества битов в микросхему памяти обычно выполняется путем четырехкратного увеличения числа «ячеек» в микросхеме. В два раза шире, в два раза длиннее, в четыре раза больше памяти (за исключением меньших расстояний между «ячейками»).

Также проще умножить, используя один сдвиг, а не общий алгоритм умножения для добавления последовательных сдвигов в зависимости от того, установлен конкретный бит или нет. OpenGL был известен тем, что требовал мощности двух размеров для текстур для доступа к определенным строкам сканирования.

1 голос
/ 08 августа 2009

В двоичных компьютерах удобно использовать основанные на стандартах двоичные кратные , такие как Мебибайт (или Kibi, Gibi, Tebi ...). Эти числа 2 также хорошо выглядят в нотациях Octal или Hex .

1 голос
/ 08 августа 2009

Не так уж много причин. Из-за выравнивания переменных в структурированном и в стеке массив из трех байтов, вероятно, займет 4 или 8 байтов в памяти. Я думаю, что это просто приятно.

Выделение целого числа страниц из кучи может работать не так эффективно, как хотелось бы, из-за накладных расходов внутренней структуры кучи. Выделение 4096 байт (1 страница для 32-битного компьютера с Windows) может привести к выделению 4104 или более.

Если константы являются флагами, то история совсем другая. Обычно более эффективно иметь битовые флаги, чем флаги в некоторой базе, которая не является степенью 2.

0 голосов
/ 08 августа 2009

Поскольку память компьютера работает с бинарной системой 0/1.

...