Что более эффективно: бит, байт или int?

Скажем, что у вас должна быть структура, похожая на следующую:

struct Person {
  int  gender;         // betwwen 0-1
  int  age;            // between 0-200
  int  birthmonth;     // between 0-11
  int  birthday;       // between 1-31
  int  birthdayofweek; // between 0-6
}

Что касается производительности, которая будет лучшим типом данных для хранения каждого из полей? (например, битполе, int, char и т.д.)

Он будет использоваться на процессоре x86 и полностью сохранен в ОЗУ. Необходимо будет хранить достаточно большое количество (50 000+), поэтому необходимо будет учитывать кеширование процессора и т.д.

Изменить: Хорошо, позвольте мне перефразировать вопрос. Если использование памяти не имеет значения, и весь набор данных не будет вписываться в кеш, независимо от того, какие типы данных используются, обычно лучше использовать более мелкие типы данных для размещения большего количества данных в кэше ЦП или лучше использовать более крупные datatypes, чтобы позволить процессору выполнять более быстрые операции? Я прошу об этом только для справки, поэтому читаемость кода и тому подобное не следует рассматривать.

Ответ 1

  • Не беспокойтесь об этом; использовать семантически правильные и наиболее читаемые
  • Нет ответа, который является правильным все время. Это зависит от платформы и компилятора. Если вам действительно все равно, тогда вам нужно протестировать.

В общем, я останусь придерживаться ints... кроме пола, который, вероятно, должен быть перечислением.

Ответ 2

int_fast#_t от < stdint.h > или < boost/cstdint.hpp > .

Тем не менее, вы откажетесь от простоты и согласованности (например, эти типы могут быть типами символов, которые являются целыми типами в C/С++, и это может привести к неожиданным разрешениям функций), а не просто использовать int.

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

Он будет использоваться на процессоре x86 и полностью сохранен в ОЗУ. Необходимо будет хранить достаточно большое количество (50 000+), поэтому необходимо будет учитывать кеширование процессора и т.д.

Вам все равно придется беспокоиться о кеше (после того, как вы на таком уровне оптимизации), даже если все данные не будут кэшироваться. Например, вы получаете доступ к каждому элементу в последовательности? непредсказуемо? или только одно поле из каждого элемента в последовательности? Сравните struct { int a, b; } data[N]; с int data_a[N], data_b[N];. (Представьте, что вам нужно все "a" сразу, но можете игнорировать другой, какой путь более кэш-памяти?) Опять же, это не похоже на основную область, на которой вы должны сосредоточиться.

Ответ 3

Количество используемых бит: Пол; 1/2 (2, если вы хотите включить интерсексуальность:)) возраст; 8 (0-255) Месяц рождения; 4 (16) день рождения; 5 (32) birthdayofweek; 3 (8)

Биты вообще: менее 22.

Зная, что он работает на x86, мы имеем тип данных int с 32 битами. Поэтому создайте свои собственные процедуры, которые могут принимать int

read (gender, int * pValue); write (gender, int * pValue);

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

Это очень быстро и имеет чрезвычайно низкий объем памяти.

Ответ 4

это зависит. У вас заканчивается память? Тогда эффективность памяти становится первостепенной. Это слишком долго? Тогда процессорное время или, по крайней мере, воспринимаемое время отклика пользователя становится первостепенным.

Ответ 5

Это зависит. Многие процессоры могут напрямую обращаться к словам, но требуют дополнительных инструкций для доступа к октетам или битам. В зависимости от компилятора и процессора, int может быть таким же, как адресное слово. Но скорость действительно будет иметь значение? Читаемость и ремонтопригодность, вероятно, будут более важными.

Ответ 6

В целом

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

  • Адресационные типы (байты, char, short, int и x86-64 "long int" ) могут быть загружены из памяти за одну операцию, и поэтому они имеют наименьшую нагрузку на процессор за один -операция.

  • Но битовые поля или флаги, упакованные в один или несколько битов, могут привести к более быстрой программе, потому что:

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

Скорости процессора ускоряются быстрее, чем диски и скорости сети в течение десятилетий, и теперь отдельные операции с процессором редко бывают проблемой, особенно в случае с C/С++. Вы уже используете самый быстрый генератор кода в арсенале.

Сценарий, в котором вы упомянули

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

Беспокойство о важных вещах

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

Беспокойство о раздувании памяти имеет значение, и это имеет большое значение, если есть вероятность, что ваше приложение не поместится в памяти. Виртуальное хранилище оказалось очень важным для защиты и управления памятью на уровне ядра ОС, но одна вещь, которую ему никогда не удавалось сделать, это позволить программам расти больше, чем доступная оперативная память, не увязывая все.

Ответ 7

Int - самый быстрый.

Если вы используете его в массиве, вы потеряете больше памяти, поэтому в этом случае вам может понадобиться байт.

Ответ 8

Что сказал Крис. Если это гипотетическая программа, которую вы разрабатываете, попытка выбрать int против uint8 на этом этапе не поможет вам в кратчайшие сроки. Сосредоточьте свои усилия в другом месте.

Если, когда дело доходит до него, у вас есть гигантская сложная система, на которой вы сделали несколько раундов оптимизаций, и вам интересно, что это за эффект, переключение int на uint8 возможно (должно быть в любом случае) довольно легко в любом случае. На этом этапе вы можете сделать статистически достоверное сравнение в реальном использовании - не раньше.

Ответ 9

enum Gender { Female, Male };

struct Date {...};

class Person
{
    Gender gender;
    Date date_of_birth;
    //no age field - can be computed from date_of_birth
};

Ответ 10

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

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

Невозможно точно сказать, какой фактор будет доминировать. В большинстве случаев наиболее эффективно использовать ints и bytes по мере необходимости. Не беспокойтесь о битполях.

Но в некоторых случаях это не так. Итак, измерение, контрольный показатель, профиль. Узнайте, как каждая реализация выполняется в вашем коде.

Ответ 11

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

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

В общей модели проблема чиста и сначала простую очевидную вещь, затем профиль, чтобы узнать, есть ли у вас проблемы с памятью. Если посмотреть на поставляемый корпус, стоит отметить пару моментов:

  • Возраст поля - это де-нормализация модели и является функцией рождения, дня рождения, дня рождения, который может быть представлен как одна длинная дата рождения. Итак, просто взглянув на модель, вы можете легко обрезать 8 байтов.
  • 4 (байты на int) * 5 (количество полей) * 50 000 (количество экземпляров) = ~ 1 МБ. У моего ноутбука 4MB L2-кеш, у вас вряд ли будут проблемы с памятью.

Ответ 12

Это зависит только от одной вещи:

What is the ratio of blindly copying the data and actually reading the data?

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

Если вы получаете доступ к нему очень редко и редко копируете его (как часть перераспределения контейнера) или сериализуете его на диск, используйте большие целые числа, которые совместимы с ЦП.

Ответ 13

Размер слова процессора - это наименьшая единица, которую компьютер может читать/записывать в/из памяти. Если вы читаете или записываете бит, char, short или int CPU, накладные расходы северного моста одинаковы во всех случаях, учитывая разумное поведение системы/компилятора. Очевидно, что преимущество кеш-кэша в меньших размерах поля. Один из них для x86 - это обеспечение правильного выравнивания по кратным размерам слов. Другие архитектуры, такие как sparc, гораздо менее прощающие и на самом деле сбой, если доступ к памяти не привязан.

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

Ответ 14

struct Person 
{
  uint8_t gender;         // betwwen 0-1
  uint8_t age;            // between 0-200
  uint8_t birthmonth;     // between 0-11
  uint8_t birthday;       // between 1-31
  uint8_t birthdayofweek; // between 0-6
}

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

Можно утверждать, что в любом случае ЦПУ должен будет получить 32-битный блок в памяти, а затем нужно извлечь байт, что потребует больше времени. Однако это произвольный выбор процессора, и он не должен влиять на ваши структуры данных. Представьте себе, что если завтра процессоры начнут работать с 64-битными блоками, ваша "оптимизация" немедленно станет бесполезной, поскольку в любом случае будет извлечение. Тем не менее, он почти уверен, что оптимизация кэша памяти всегда будет актуальной. Вот почему мне нравится предоставлять приложение столь же легкое, насколько это возможно, по памяти, даже если речь идет о производительности.

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

Ответ 15

Вещь - я не вижу больших полей Person. Поэтому скорость здесь не проблема.

Какая проблема - это последовательность. У вас есть структура с публичными членами, что можно легко установить неверные данные, будучи вне диапазона или выключен индексом.

Я имею в виду, вы здесь не согласны, не так ли?

int  birthmonth;     // between 0-11
int  birthday;       // between 1-31

Почему месяцы начинаются с нуля, но дней нет?

И даже если бы у вас было это последовательное, возможно, один парень, который использует вашу структуру, идет с "месяцами начинается с нуля", один парень с "месяцами начинается с одного". Это вызывает ошибку. Почему возраст ограничен? Конечно, мы не найдем 200-летнего парня, но почему бы не сказать max типа?

Скорее используйте такие вещи, как класс enum, специальный тип, который ограничивает ваш диапазон как template<int from, int to> class RangedInt или уже существующий тип, например Date (тем более, что такой тип может проверять, сколько дней в феврале было в этом году и так далее).

Некоторые люди прокомментировали гендерную проблематику здесь, но в любом случае нуль - это не пол. Вы имеете в виду мужчин и женщин, но пока у нас есть какая-то интуиция, когда вы говорите, что этот день находится в диапазоне 0..31, как бы мы догадались, если ноль - мужчина или женщина? Не должно регулироваться комментариями. (И если вы на самом деле хотите обратиться к материалам гендерной теории, я бы сказал, что правильный тип string.)

Кроме того, в большинстве случаев вы не измените ничего о человеке после его создания. Изменения, которые происходят в реальной жизни (например, получение другого имени через брак), происходят на уровне базы данных или тому подобное, а не на уровне класса. Поэтому я сделал класс, в котором вы можете установить только конструктор. Хотя, конечно, это очевидно зависит от использования. Но если вы можете сделать это неизменным, сделайте это неизменным.

Изменить: И легко сделать возраст несовместимым с датой рождения. Это нехорошо. Удалите избыточные данные, сделайте это методом или тому подобным. Хотя год отсутствует? Я должен сказать, странный дизайн.