На 32-разрядных процессорах, является ли тип integer более эффективным, чем тип short?

StackOverflow https://stackoverflow.com/questions/163254

Вопрос

В 32-битном процессоре целое число составляет 4 байта, а короткое целое число составляет 2 байта. Если я пишу приложение на C / C ++, которое использует много числовых значений, которые всегда будут помещаться в заданный диапазон короткого целого числа, будет ли эффективнее использовать 4-байтовые целые или 2-байтовые целые числа?

Я слышал, что 4-байтовые целые числа более эффективны, так как это соответствует пропускной способности шины от памяти до процессора. Однако, если я сложу вместе два коротких целых числа, будет ли ЦП упаковывать оба значения за один параллельный проход (таким образом, охватывая 4-байтовую полосу пропускания шины)?

Это было полезно?

Решение

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

Он не будет выполнять сразу две 16-битные операции, но если вы пишете код самостоятельно и уверены, что он не переполнится, вы можете сделать это самостоятельно.

Изменить . Я должен добавить, что это также в некоторой степени зависит от вашего определения "эффективного". Хотя он сможет выполнять 32-разрядные операции быстрее, вы, конечно, будете использовать вдвое больше памяти.

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

Другие советы

Если у вас большой массив чисел, выбирайте наименьший подходящий размер. Будет более эффективно работать с массивом 16-битных шорт, чем 32-битными, так как вы получите удвоенную плотность кэша. Стоимость любого расширения знака, которое ЦП должен делать для работы с 16-битными значениями в 32-битных регистрах, тривиально ничтожна по сравнению со стоимостью пропуска кэша.

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

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

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

32-битный ЦП - это ЦП, который обычно работает с 32-битными значениями внутри, но это не означает, что он медленнее при выполнении той же операции с 8/16-битным значением. Например, x86, все еще обратно совместимый до 8086, может работать с частями регистра. Это означает, что даже если регистр имеет ширину 32 бита, он может работать только с первыми 16 или первыми 8 битами этого регистра, и замедления не будет вообще. Эта концепция была даже принята x86_64, где регистры 64-битные, но они все еще могут работать только на первых 32, 16 или 8-битных.

Кроме того, процессоры x86 всегда загружают целую строку кэша из памяти, если она еще не находится в кэше, и строка кэша в любом случае больше 4 байтов (для 32-битных процессоров, а не 8 или 16 байтов), и, следовательно, загрузка 2 байтов из памяти одинаково быстро, как загрузка 4 байта из памяти. Если обрабатывать много значений из памяти, 16-битные значения могут на самом деле быть намного быстрее, чем 32-битные, так как объемы памяти меньше. Если строка кэша имеет длину 8 байт, в каждой строке кэша имеется четыре 16-битных значения, но только два 32-битных значения, поэтому при использовании 16-битных целых у вас один доступ к памяти на каждые четыре значения, а при использовании 32-битных целых у вас по одному каждые два , что приводит к удвоению числа передач для обработки большого массива int.

Другие процессоры, например, PPC, не могут обрабатывать только часть регистра, они всегда обрабатывают полный регистр. Тем не менее, эти процессоры обычно имеют специальные операции загрузки, которые позволяют им, например, загрузить 16-битное значение из памяти, расширить его до 32-битного и записать его в регистр. Позже у них есть специальная операция сохранения, которая берет значение из регистра и сохраняет в памяти только последние 16 бит; обе операции требуют только одного цикла ЦП, как и 32-битная загрузка / сохранение, поэтому разницы в скорости тоже нет. А поскольку PPC может выполнять только арифметические операции над регистрами (в отличие от x86, который также может работать непосредственно с памятью), эта процедура загрузки / сохранения выполняется в любом случае независимо от того, используете ли вы 32-битные или 16-битные.

Единственный недостаток, если вы объединяете несколько операций на 32-битном процессоре, который может работать только на полных регистрах, - это то, что 32-битный результат последней операции может быть "сокращен". до 16 бит до выполнения следующей операции, в противном случае результат может быть неверным. Однако такое сокращение - это всего лишь один цикл ЦП (простая операция И), и компиляторы очень хорошо понимают, когда такое сокращение действительно необходимо и когда его исключение не повлияет на конечный результат. таким образом, такое сокращение не выполняется после каждой инструкции, оно выполняется, только если это действительно неизбежно. Некоторые процессоры предлагают различные "улучшенные" инструкции, которые делают такое сокращение ненужным, и я видел много кода в своей жизни, где я ожидал такого сокращения, но, глядя на сгенерированный код сборки, компилятор нашел способ полностью его избежать.

Так что если вы ожидаете здесь общего правила, мне придется вас разочаровать. Никто не может с уверенностью сказать, что 16-битные операции одинаково быстры для 32-битных операций, и никто не может с уверенностью сказать, что 32-битные операции всегда будут быстрее. Это зависит также от того, что именно ваш код делает с этими числами и как он это делает. Я видел тесты, в которых 32-битные операции выполнялись быстрее на определенных 32-битных процессорах, чем тот же код с 16-битными операциями, однако я также уже видел обратное. Даже переключение с одного компилятора на другой или обновление версии компилятора может уже все перевернуть. Я могу только сказать следующее: Кто бы ни утверждал, что работа с шортами значительно медленнее, чем работа с целыми числами, пожалуйста, предоставьте пример исходного кода для этого утверждения и назовите CPU и компилятор, который он использовал для тестирования, так как я никогда не испытывал ничего подобного в о последних 10 лет. Могут быть ситуации, когда работа

Это зависит. Если вы привязаны к процессору, 32-разрядные операции на 32-разрядном процессоре будут выполняться быстрее, чем 16-разрядные. Если вы ограничены в памяти (особенно если у вас слишком много пропусков кэша L2), используйте самые маленькие данные, в которые вы можете втиснуться.

Вы можете узнать, какой из них вы используете профилировщиком, который будет измерять как пропуски ЦП, так и L2, например VTune Intel . Вы будете запускать ваше приложение 2 раза с одной и той же нагрузкой, и оно объединит 2 прогона в одно представление горячих точек в вашем приложении, и вы сможете увидеть для каждой строки кода, сколько циклов было потрачено на этой строке. Если в дорогой строке кода вы видите 0 пропусков кэша, значит, вы привязаны к процессору. Если вы видите тонны промахов, вы ограничены в памяти.

Не слушай совет, попробуй его.

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

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

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

Когда вы говорите «32 бита», я предполагаю, что вы имеете в виду x86. 16-битная арифметика довольно медленная: префикс размера операнда делает декодирование действительно медленным. Поэтому не делайте ваши временные переменные короткими int или int16_t.

Однако x86 может эффективно загружать 16- и 8-битные целые числа в 32- или 64-битные регистры. (movzx / movsx: расширение нуля и знака). Поэтому не стесняйтесь использовать short int для массивов и структурных полей, но убедитесь, что вы используете int или long для своих временных переменных.

  

Однако, если я сложу вместе два коротких целых числа, будет ли ЦП упаковывать оба значения за один проход параллельно (таким образом, охватывая 4-байтовую полосу пропускания шины)?

Это чепуха. инструкции загрузки / сохранения взаимодействуют с кешем L1, и ограничивающим фактором является количество операций; ширина не имеет значения. например на core2: 1 загрузка и 1 хранилище за цикл, независимо от ширины. Кэш L1 имеет 128 или 256-битный путь к кэшу L2.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top