Если количество смен можно рассчитать заранее, у меня есть две идеи, которые могут сработать
Использование самоизменяющегося кода
Просто измените величину смены непосредственно в инструкции. Альтернативно генерировать код динамически для функций с переменной shift
Сгруппируйте значения с одним и тем же числом сдвигов, если это возможно, и выполните операцию одновременно, используя указатель устройства или функции Даффа, чтобы минимизировать ошибочное прогнозирование ветвления
// shift by constant functions
typedef int (*shiftFunc)(int); // the shift function
#define SHL(n) int shl##n(int x) { return x << (n); }
SHL(1)
SHL(2)
SHL(3)
...
shiftFunc shiftLeft[] = { shl1, shl2, shl3... };
int arr[MAX]; // all the values that need to be shifted with the same amount
shiftFunc shl = shiftLeft[3]; // when you want to shift by 3
for (int i = 0; i < MAX; i++)
arr[i] = shl(arr[i]);
Этот метод также можно использовать в сочетании с самоизменяющимся кодом или генерацией кода во время выполнения, чтобы устранить необходимость в указателе функции.
Редактировать: Как уже отмечалось, к сожалению, предсказание ветвления при переходе на регистрацию вообще отсутствует, поэтому единственный способ, которым это может сработать, - это генерировать код, как я уже говорил выше, или использовать SIMD
Если диапазон значений невелик, справочная таблица является другим возможным решением
#define S(x, n) ((x) + 0) << (n), ((x) + 1) << (n), ((x) + 2) << (n), ((x) + 3) << (n), \
((x) + 4) << (n), ((x) + 5) << (n), ((x) + 6) << (n), ((x) + 7 << (n)
#define S2(x, n) S((x + 0)*8, n), S((x + 1)*8, n), S((x + 2)*8, n), S((x + 3)*8, n), \
S((x + 4)*8, n), S((x + 5)*8, n), S((x + 6)*8, n), S((x + 7)*8, n)
uint8_t shl[256][8] = {
{ S2(0U, 0), S2(8U, 0), S2(16U, 0), S2(24U, 0) },
{ S2(0U, 1), S2(8U, 1), S2(16U, 1), S2(24U, 1) },
...
{ S2(0U, 7), S2(8U, 7), S2(16U, 7), S2(24U, 7) },
}
Теперь x << n
- это просто shl[x][n]
с x, равным uint8_t
. Таблица стоит 2 КБ (8 × 256 B) памяти. Однако для 16-битных значений вам понадобится таблица размером 1 МБ (16 × 64 КБ), которая все еще может быть жизнеспособной, и вы можете сделать 32-битный сдвиг, комбинируя два 16-битных сдвига вместе