Эффективность структур данных в C99 (возможно, зависит от порядка байтов) - PullRequest
0 голосов
/ 25 декабря 2010

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

Однако во время работы алгоритма мне часто требуется доступ к различным частям слова. Теперь я могу сделать это двумя способами:

uint32_t w = 0x11223344;
uint8_t a = (w & 0xff000000) >> 24;
uint8_t b = (w & 0x00ff0000) >> 16;
uint8_t b = (w & 0x0000ff00) >>  8;
uint8_t d = (w & 0x000000ff);

Однако часть меня думает, что это не особенно эффективно. Я подумал, что лучше использовать профсоюзное представление так:

typedef union
{
    struct
    {
        uint8_t d;
        uint8_t c;
        uint8_t b;
        uint8_t a;
    };
    uint32_t n;
} word32;

Используя этот метод, я могу назначить word32 w = 0x11223344;, тогда я могу получить доступ к различным части, которые мне нужны (w.a=11 в порядке байтов).

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

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

Конечно, я мог бы проверить результаты сборки, но мои знания компиляторов не являются блестящими. Я бы подумал, что объединение будет более эффективным, поскольку оно по сути преобразуется в смещения памяти, например так:

mov eax, [r9+8]

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

Если это имеет значение, я использую C99, в частности, мой компилятор - clang (llvm).

Заранее спасибо.

Ответы [ 4 ]

2 голосов
/ 26 декабря 2010

Если вам нужен AES, почему бы не использовать существующую реализацию? Это может быть особенно полезно на современных процессорах Intel с аппаратной поддержкой AES.

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

2 голосов
/ 26 декабря 2010

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

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

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

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

1 голос
/ 26 декабря 2010

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

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

1 голос
/ 25 декабря 2010

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

Другой вариант (также зависящий от порядка байтов) - приведениеслово для байтового массива и прямой доступ к байтам.То есть что-то вроде следующего:

uint8_t b = ((uint8_t*)w)[n] 

Я не уверен, что вы увидите разницу на реальном современном 32/64-битном процессоре.

РЕДАКТИРОВАТЬ : Похоже, что в обоих случаях clang выдает одинаковый код.

...