2016-11-16 17 views
4

Я хочу иметь возможность устанавливать или очищать (несколько) бит uintX_t t.Оптимизация «i = b? (I | mask): (i & ~ mask)«

i является переменной времени выполнения (uintX_t). b - это переменная времени выполнения (uintX_t), которая ограничена 0 или 1.

mask - постоянная времени компиляции.

Есть ли лучший способ, чем:

i = b ? (i | mask) : (i & ~mask) 

Я ищу, чтобы избежать ветвления, если это возможно. Цель - ARM, если это имеет значение.

+0

'я = b' или' я == b' ? –

+0

@HongOoi 'i = (b? (I | mask): (i & ~ mask))' – fadedbee

ответ

5

Другой вариант: всегда установлен бит 0 (левая часть) и, необязательно, установите бит в 1 (правая часть).

i = (i & ~mask) | (mask * b); 
+0

Это также следует из ответа Гильерме и использования этого или распространения и – harold

+0

@harold. Ваш комментарий случайно чего-то не хватает? Конец этого кажется странным. – user694733

+0

Нет, я просто не форматировал его четко, я имею в виду, что OR распределяется по AND – harold

5

Идея заключается в том, чтобы заменить ветвь с помощью умножения, где мы можем обнулить каждую сторону в зависимости от величины б:

i = (i | (mask * b)) & (~mask | (mask * b)); 
+0

Я тестировал на Java, если хотите, я могу опубликовать тестовый код в java, который показывает, что он действительно работает –

+0

Теперь Вопрос: что хуже с точки зрения потери производительности, ветвления или умножения? –

+0

В моих тестах я использовал i = 101 (двоичный), mask = 110 (двоичный) –

6

Эксплуатируя тот факт, что -1u является значением со всеми битами, установленными:

i = (i & ~mask) | (mask & -b); 

или

i ^= (i^-b) & mask; 

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

+1

'-b' и' -1u' - это не одно и то же.Если 'b' - это малый целочисленный тип любой подписанности, вы в конечном итоге получаете отрицательное число временно. В отличие от '-1u', который всегда является положительным числом. Это не будет проблемой в этом конкретном случае, но было бы катастрофой в коде, например '(mask & -b) << n'. – Lundin

+1

@Lundin Правильно, на самом деле проблема с кодом в моем ответе, если 'b' уже, чем' int', и платформа не использует дополнение двух. В этом случае требуется дополнительное литье '- (без знака) b'. – nwellnhof

1

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

Как всегда бывает с битовыми операторами, вы должны быть осторожны с неявными рекламными акциями типа. Например, неосторожное использование ~ имеет тенденцию создавать неявные рекламные ошибки. (Оператор ?: также молча способствует результат уравновешивая 2-го и 3-го операнда друг с другом.)

читаемым, портативный, безопасный код:

uintx_t i = ... ; 
uintx_t b = ... ; // 1 or 0 

i &= (uintx_t)~mask; // always clear the bit 
i |= mask * b;   // if b is 1, set the bit, otherwise OR with 0 
+0

Спасибо, мне нравится этот читаемый подход. Если 'i' является PORTA на AVR, это может привести к сбою GPIO? Или два заявления объединены в один во время компиляции? Я * ожидаю *, что PORTA отмечен volatile, чтобы остановить это. – fadedbee

+1

@chrisdew Это приведет к тому, что GPIO снизится на пару тактов. Компилятору не разрешается оптимизировать код, поскольку 'PORTA' будет изменчивым регистром. На некоторых аппаратных средствах портам GPIO может потребоваться несколько тактов для стабилизации. – Lundin

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