2013-04-19 3 views
4

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

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

Теперь, если я упакую structs один 1-байтовый выравнивание с #pragma pack(1), я могу читать структуры от потока ввода-вывода непосредственно на указатели на структуру. Это удобно. Если я не упакую структуры, я могу либо fread полей по одному, либо fread блоков за один раз и reinterpret_cast структурных полей один за другим, что, вероятно, будет быстро устаревать.

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

Так что вопрос в руке, я могу ...

  • Проведение десятков тысяч крошечных звонков fread?
  • Прочитайте фрагменты и скопируйте их по соответствующим байтам?
  • Пакет структур и читайте прямо на них?
+3

Единственный правильный ответ: сравнить его. Я бы подумал, что ваш код, скорее всего, связан с IO, и влияние производительности упаковки вашей структуры или нет, не поддается измерению, но без контрольного показателя это просто догадки. –

+0

Он также очень зависит от вашей платформы. На некоторых платформах даже не принимаются нерастянутые чтения/записи, поэтому у вас даже нет возможности упаковывать ваши структуры. Если ваш код должен быть кросс-платформенным, вам не следует путаться с упаковкой вообще. @MarkB также упомянул о предисловии, которое важно помнить тоже. – syam

+0

Поскольку код не знает о платформе, я предполагаю, что упаковка полностью со стола. Угадайте, что оставляет чтение кусков и разрезает его. – user2285060

ответ

4

В конечном счете, разница в производительности между решением A и решением B может быть определена только путем бенчмаркинга. Просьба в Интернете даст вам переменные результаты, которые могут или не могут отражать реальность в вашем случае.

Что происходит, когда вы «неправильно оцениваете» данные, так это то, что процессору необходимо выполнить несколько операций чтения [и то же самое применимо для записи] для одной части данных. Точно, сколько дополнительного времени зависит от процессора - некоторые процессоры не делают это автоматически, поэтому система времени ожидания будет ловить «плохое чтение» и выполнять чтение на каком-то уровне эмуляции [или, в некоторых процессорах, просто убить процесс для «непривязанного доступа к памяти»]. Очевидно, что захват ловушки и выполнение нескольких операций чтения, а возврат к вызывающему коду - довольно значительное влияние на производительность - он может легко выполнять сотни циклов дольше, чем выровненная операция чтения.

В случае x86 он «работает так же, как вы ожидали», но с штрафом обычно 1 дополнительный такт [при условии, что данные уже находятся в кеше L1]. Один тактовый цикл не очень много в современном процессоре, но если цикл имеет длину 10000000000000 итераций и считывает неровные данные n раз, вы добавили к времени выполнения n * 10000000000000 тактовых циклов, что может быть значительным.

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

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

Также имейте в виду, что # pragma pack является компилятором, и достичь макросов нелегко, что позволяет выбрать между решением «Microsoft» и «gcc», например. Изменить: похоже, что последние версии gcc поддерживают эту опцию, но не ВСЕ компиляторы.

+0

Спасибо. Ваши комментарии к процессору x86 полезны; Я не искал бетонов, просто идея о том, к чему я иду. Кроме того, '#pragma pack' является кросс-совместимым с MSVC и GCC в соответствии с [this] (http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html). GCC добавил его для дополнительной поддержки перекрестной поддержки MS. – user2285060

+0

@ user2285060: Хорошо, я отредактировал раздел о совместимости компилятора «gcc» и «MS», а также выяснил случай «захвата». Я видел это на ARM, например, когда неравномерный доступ эмулируется и занимает несколько сотен циклов вместо одного цикла. –

+0

Несколько * сто * циклов ... святое дерьмо. – user2285060

1

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

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

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

+0

Самый быстрый с точки зрения чтения их, я предполагаю, но может ли это отрицательно повлиять на число хруст позже? – Angew

+1

Какова конечная информация о содержимом файла отличается от архитектуры чтения? –

+0

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

2

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

Предполагая, что вы всегда знаете количество байтов (возможно, из указателя типа структуры в файле), я бы предложил использовать фабричный шаблон, где конструктор созданного объекта знает, как вытащить байты из атрибута буфера памяти по атрибуту (если файл достаточно мал, вы можете просто прочитать всю вещь в буфер, а затем создать цикл/factory-create/deserialize-into-object-through-constructor.Таким образом, вы можете управлять контентом и разрешать требуемое выравнивание структуры компилятора.

+0

Конечность всегда можно зафиксировать на отдельном шаге после чтения. –

+0

@MarkRansom: Да, я согласен с этим, но вам по-прежнему нужен «шаг», чтобы исправить это. В этот момент вы можете просто иметь общий «конвертировать из сохраненного формата в формат времени исполнения», который выполняет «распаковку и порядок байтов по мере необходимости». –

Смежные вопросы