Эквивалент C sizeof для макросов

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

Что-то типа:

typedef struct {
    uint32_t alpha;
    uint32_t two;
    uint32_t iii;
} Entry;

/*...*/

uint8_t * bytes = (uint8_t *) entry;
#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
#if (sizeof(Entry) == 12)
    PROCESS_ENTRY( 0);PROCESS_ENTRY( 1);PROCESS_ENTRY( 2);
    PROCESS_ENTRY( 3);PROCESS_ENTRY( 4);PROCESS_ENTRY( 5);
    PROCESS_ENTRY( 6);PROCESS_ENTRY( 7);PROCESS_ENTRY( 8);
    PROCESS_ENTRY( 9);PROCESS_ENTRY(10);PROCESS_ENTRY(11);
#else
#   warning Using non-optimized code
    size_t i;
    for (i = 0; i < sizeof(Entry); i++)
    {
        PROCESS_ENTRY(i);
    }
#endif
#undef PROCESS_ENTRY

Это, конечно, не работает, поскольку sizeof недоступен для препроцессора (по крайней мере, это то, что этот ответ, казалось, указывал).

Есть ли простой обходной путь, который я могу использовать, чтобы получить sizeof структуру данных для использования с макросом C, или я просто SOL?


person rampion    schedule 20.08.2009    source источник
comment
Итак, sizeof() является макросом. По крайней мере встроенный макрос.   -  person Havenard    schedule 21.08.2009
comment
sizeof не является макросом в любой форме.   -  person    schedule 21.08.2009
comment
sizeof не является макросом, хотя offsetof им является. sizeof больше похож на оператор.   -  person David Thornley    schedule 21.08.2009
comment
Кстати, вы уверены, что развертывание цикла — это оптимизация? Вы можете сократить время процессора или увеличить количество промахов в кеше. Если вам нужно, чтобы код выполнялся немного быстрее, вам нужно измерить. Если вы обнаружите, что вам не нужно разворачиваться, вам не нужно проходить через все это.   -  person David Thornley    schedule 21.08.2009
comment
Цикл в том виде, в котором он написан, выглядит просто, а верхняя граница представляет собой постоянное выражение. Я не удивлюсь, если GCC оптимизирует это.   -  person Johannes Schaub - litb    schedule 21.08.2009


Ответы (6)


Вы не можете сделать это в препроцессоре, но вам это и не нужно. Просто создайте простой if в своем макросе:

#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
if (sizeof(Entry) == 12) {
    PROCESS_ENTRY( 0);PROCESS_ENTRY( 1);PROCESS_ENTRY( 2);
    PROCESS_ENTRY( 3);PROCESS_ENTRY( 4);PROCESS_ENTRY( 5);
    PROCESS_ENTRY( 6);PROCESS_ENTRY( 7);PROCESS_ENTRY( 8);
    PROCESS_ENTRY( 9);PROCESS_ENTRY(10);PROCESS_ENTRY(11);
} else {
    size_t i;
    for (i = 0; i < sizeof(Entry); i++) {
        PROCESS_ENTRY(i);
    }
}

sizeof — это константное выражение, и сравнение константы с константой также является константой. Любой здравомыслящий компилятор C оптимизирует ветвь, которая всегда ложна во время компиляции — свертывание констант — одна из самых основных оптимизаций. Однако вы теряете #warning.

person Pavel Minaev    schedule 20.08.2009
comment
Всегда хорошо посмотреть на проблему немного под другим углом. +1 и слава! - person qrdl; 21.08.2009

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

В противном случае да, вам не повезло.

person Sean Bright    schedule 20.08.2009

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

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

Также вы можете попробовать просто выполнить if (sizeof(Entry) == 12) и посмотреть, способен ли ваш компилятор оценивать состояние ветвления во время компиляции и удалять мертвый код. Это не такая уж большая просьба.

person Steve Jessop    schedule 20.08.2009
comment
Идея использования assert() вместо #warning тоже хороша. Вы действительно хотите изящную деградацию или хотите, чтобы вас четко проинформировали о проблеме, чтобы вы могли ее исправить? - person Brooks Moses; 21.08.2009
comment
И есть две причины, по которым это не может быть 12. Одна из них заключается в том, что есть какое-то неожиданное заполнение (в этом случае вы, вероятно, хотите получить ошибку, поэтому вы можете использовать специфичные для компилятора прагмы для упаковки структуры), а вторая заключается в том, что вы добавили поле (в этом случае вы хотите обновить #define, чтобы он представлял новый размер, а затем также решить, следует ли обновлять развернутый цикл для обработки нового размера). Так что на самом деле у меня было бы утверждение в точке определения структуры и также предупреждение в точке развертывания. - person Steve Jessop; 21.08.2009
comment
Но я должен добавить, что я никогда не позволяю предупреждениям жить в любом случае, по крайней мере, не в моем собственном коде, поэтому для меня предупреждение означает, что это ошибка, которую, если это абсолютно необходимо, вы можете временно игнорировать, просто чтобы остальная часть кода скомпилировалась . - person Steve Jessop; 21.08.2009

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

person Pete Kirkham    schedule 20.08.2009
comment
Любой современный компилятор, генерирующий код медленнее, чем устройство Даффа, не очень хороший компилятор. - person Carson Myers; 21.08.2009
comment
Разве это не зависит от того, насколько параноидальным является размер кода? Компиляторы не обязательно умножают размер вашего кода на 12 просто для развлечения. Без ввода профайлера оптимизатор не может узнать, за какие циклы стоит платить за размер кода, чтобы увеличить скорость. Развертывание всего приводит к большому количеству кода, большому количеству промахов icache и замедлениям. С другой стороны, вы можете разумно выбирать, какие циклы разворачивать (или, что еще более разумно, использовать компилятор, который может оптимизировать с помощью данных профилировщика). Если только вы не имеете в виду, что Устройство Даффа теперь устарело из-за более новых, лучших трюков, о которых я не знаю. - person Steve Jessop; 22.08.2009

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

template <std::size_t SizeOfEntry>
void process_entry_loop(...)
{
    // ... the nonoptimized version of the loop
}

template <>
void process_entry_loop<12>(...)
{
    // ... the optimized version of the loop
}

// ...

process_entry_loop<sizeof(Entry)>(...);
person fbrereto    schedule 20.08.2009
comment
Хорошая идея, но было бы понятнее сделать sizeof(Entry) параметром шаблона и специализироваться на 12. - person Brooks Moses; 21.08.2009

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

В Visual C++ можно использовать #pragma pack, а в GCC вы можете использовать __attribute__((packed)) и __attribute__((aligned(num-bytes)).

person Matthew Iselin    schedule 20.08.2009
comment
Это не даст размер. Кроме того, хотя упаковка сэкономит место, она, скорее всего, будет стоить времени, а именно это он и пытается выиграть. - person David Thornley; 21.08.2009
comment
@David Thornley: размер не указан, но он хочет делать то, что он хочет сделать в этом блоке кода. Упаковав структуру, он точно будет знать, что отступы не мешают, поэтому структура имеет ровно 12 байт (или сумму размеров каждого элемента), и поэтому нет нужно использовать макрос препроцессора, чтобы определить размер структуры. PROCESS_ENTRY использует доступ на уровне байтов к незаполненной структуре, поэтому упаковка структуры позволяет использовать этот макрос, не беспокоясь о заполнении. - person Matthew Iselin; 21.08.2009
comment
Я должен добавить, что вообще лучше не вмешиваться и просто позволить оптимизатору делать свою работу (тем более, что он делает это намного лучше, чем большинство программистов). - person Matthew Iselin; 21.08.2009