С++ упаковать RGBA в беззнаковое целое

Со следующим классом, предполагая, что RGBA находится в диапазоне от 0 до 255

class Color {
    public:
        short int r;
        short int g;
        short int b;
        short int a;

Я видел такие библиотеки, как устаревшая библиотека GD, использующая битовый сдвиг и &, например

 ((r & 0x7F000000) << 24) & ...

но я обеспокоен тем, что это может быть медленным, и я бы предпочел более распространенный подход. Кто-нибудь знает, как я мог бы упаковать значения RGBA в беззнаковое целое без использования чрезмерных побитовых операторов (подход GD использует около 6-8 битовых сдвигов на байт).


person Precursor    schedule 22.07.2011    source источник
comment
Есть ли у вас действительно веские основания полагать, что скорость битового сдвига является узким местом в вашем приложении?   -  person Kerrek SB    schedule 23.07.2011
comment
Если вы действительно опасаетесь, что это потребует слишком много времени, вы все равно можете использовать union, который содержит unsigned int, а также безымянный struct с 4 полями char. Обратите внимание, что 4 short int в вашем фрагменте кода не будет тривиально вписываться в unsigned int.   -  person Damon    schedule 23.07.2011
comment
Дело скорее в том, что это будет back-end класс, и если кто-то позже решит, что этот класс отстает от их приложения или метод, который я использовал, слишком медленный, я это выслушаю.   -  person Precursor    schedule 23.07.2011
comment
Маскированный битовый сдвиг — самый быстрый способ сделать это на современных процессорах. Единственный способ улучшить это — напрямую вызвать смену с помощью маскированного аппаратного опкода вставки на ЦП, у которых он есть, например, rlwimi op PPC. Я оптимизирую именно такой код для жизни. Предостережение заключается в том, что коэффициент сдвига должен быть непосредственной константой — если вы выполняете сдвиг на переменную величину (например, r << x, где x — целое число, а не непосредственное), то это будет намного медленнее. Использование объединения обычно медленнее, потому что большинство компиляторов не создают очень хороший код для объединений. (Они могут создать оптимальный код... но не делают этого.)   -  person Crashworks    schedule 23.07.2011
comment
@Crashworks, это очень интересно. Я должен буду проверить это как-нибудь.   -  person Mark Ransom    schedule 23.07.2011


Ответы (4)


Самый простой способ — переопределить класс Color так, чтобы он содержал unsigned char, а не short, и убедиться, что они расположены в правильном порядке для вашего процессора. Затем сделайте его объединением с 32-битным целочисленным типом.

person Mark Ransom    schedule 22.07.2011
comment
А затем наблюдайте, как компилятор вставляет отступы между элементами, и весь ваш код ужасно ломается. - person Jerry Coffin; 23.07.2011
comment
@Jerry Джерри, это случилось со мной однажды. Это привело к довольно гневному звонку в службу поддержки поставщику компилятора и поспешному патчу от них. - person Crashworks; 23.07.2011
comment
@Crashworks: Этого не должно было быть - они были правы, а ты ошибался. - person Jerry Coffin; 23.07.2011
comment
@Jerry На практике так много игр зависят от поведения упакованного объединения, что компилятор, который вставляет ненужное заполнение, демонстрирует ошибку. Помимо нарушения работы графических библиотек, это также тратит впустую драгоценную кэш-память и снижает производительность. - person Crashworks; 23.07.2011

Битовые сдвиги постоянной величины не медленные. Маскировка вообще не медленная.

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

person MSN    schedule 22.07.2011
comment
р ‹‹ 24 | г ‹‹ 16 | б ‹‹ 8 | a предполагает некоторое смещение. Так как его значения 16-битные, они также должны быть замаскированы. Если бы они были 8-битными, а ваша память была выровнена по 4 байтам, вы могли бы обойтись без сдвигов. - person Michael Dorgan; 23.07.2011
comment
@MSN: на самом деле это может быть довольно медленным на некоторых довольно распространенных процессорах. У старых процессоров Intel была частичная остановка регистра, с которой вы, вероятно, столкнулись. Более новые избегают этого, но все равно должны вставлять sync микрооперацию после того, как вы ИЛИ соединяете части вместе. Это не так медленно, как у PRS, но все же существенное замедление. - person Jerry Coffin; 23.07.2011

Если вы храните значения rgba в классе в соответствующем порядке, просто интерпретируйте экземпляр класса как целое без знака. Нет времени. Это делается все время в C.

См. ответ Марка для более подробной информации.

person Patrick87    schedule 22.07.2011
comment
Но в этом случае, конечно, следует использовать unsigned char вместо short (обычно это 16 бит). - person Christian Rau; 23.07.2011
comment
Но для этого OP пришлось бы изменить определение класса, чтобы использовать unsigned chars, не? - person Kerrek SB; 23.07.2011
comment
О да, они должны быть беззнаковыми символами... Но это выполнимо, верно? - person Patrick87; 23.07.2011
comment
Что с выравниванием? не может ли это быть проблемой, по крайней мере, теоретически? - person Andriy Tylychko; 23.07.2011
comment
@Энди: да, по крайней мере косвенно. Компилятор может вставлять заполнение между членами структуры. Он должен иметь доступ к chars без заполнения, но он может в любом случае вставить дополнение для дополнительной производительности. - person Jerry Coffin; 23.07.2011
comment
Да, расклад вполне мог. - person Michael Dorgan; 23.07.2011

Побитовые операторы на самом деле не "медленные". На самом деле, обычно это одни из самых быстрых операций, которые может выполнять ЦП. Heck ARM большую часть времени дает вам смены бесплатно. Когда дело доходит до этого, если вы хотите безопасно упаковать vars в меньшее пространство (убедившись в отсутствии битового переполнения), вам нужно использовать побитовые функции. Даже битовые поля по умолчанию для них.

person Michael Dorgan    schedule 22.07.2011