Есть ли более быстрый способ, чем x >= start && x <= end
в C или C ++, проверить, находится ли целое число между двумя целыми числами?
ОБНОВЛЕНИЕ : Моя конкретная платформа - iOS. Это часть функции размытия прямоугольника, которая ограничивает пиксели кругом в данном квадрате.
ОБНОВЛЕНИЕ : Попробовав принятый ответ , я получил ускорение на одну строку кода по сравнению с обычным x >= start && x <= end
способом.
ОБНОВЛЕНИЕ : Вот код после и до с ассемблером из XCode:
НОВЫЙ ПУТЬ
// diff = (end - start) + 1
#define POINT_IN_RANGE_AND_INCREMENT(p, range) ((p++ - range.start) < range.diff)
Ltmp1313:
ldr r0, [sp, #176] @ 4-byte Reload
ldr r1, [sp, #164] @ 4-byte Reload
ldr r0, [r0]
ldr r1, [r1]
sub.w r0, r9, r0
cmp r0, r1
blo LBB44_30
СТАРЫЙ ПУТЬ
#define POINT_IN_RANGE_AND_INCREMENT(p, range) (p <= range.end && p++ >= range.start)
Ltmp1301:
ldr r1, [sp, #172] @ 4-byte Reload
ldr r1, [r1]
cmp r0, r1
bls LBB44_32
mov r6, r0
b LBB44_33
LBB44_32:
ldr r1, [sp, #188] @ 4-byte Reload
adds r6, r0, #1
Ltmp1302:
ldr r1, [r1]
cmp r0, r1
bhs LBB44_36
Довольно удивительно, как сокращение или устранение ветвления может обеспечить такое резкое ускорение.
c++
c
performance
math
jjxtra
источник
источник
Ответы:
Есть старый трюк, чтобы сделать это только с одним сравнением / ответвлением. Может ли это действительно улучшить скорость, может быть под вопросом, и даже если это произойдет, это, вероятно, слишком мало, чтобы заметить или позаботиться о нем, но когда вы только начинаете с двух сравнений, шансы на огромное улучшение довольно малы. Код выглядит так:
В типичном современном компьютере (то есть, в любом, использующем дополнение по два) преобразование в unsigned на самом деле не имеет значения - просто изменение в том, как рассматриваются те же самые биты.
Обратите внимание, что в типичном случае вы можете предварительно вычислить
upper-lower
вне (предполагаемого) цикла, так что обычно это не дает значительного времени. Наряду с уменьшением количества команд ветвления это также (как правило) улучшает предсказание ветвления. В этом случае одна и та же ветвь берется независимо от того, находится ли число ниже нижнего конца или выше верхнего конца диапазона.Что касается того, как это работает, основная идея довольно проста: отрицательное число, если рассматривать его как число без знака, будет больше, чем все, что начиналось как положительное число.
На практике этот метод переводит
number
и интервал в точку происхождения и проверяет,number
находится ли интервал[0, D]
, гдеD = upper - lower
. Еслиnumber
ниже нижней границы: отрицательный , а если выше верхней границы: больше, чемD
.источник
lower <= x & x <= upper
(а неlower <= x && x <= upper
) привести к повышению производительности?Редко можно сделать существенную оптимизацию для кода в таком маленьком масштабе. Большой выигрыш в производительности достигается за счет наблюдения и изменения кода с более высокого уровня. Вы можете полностью исключить необходимость проверки диапазона или выполнить только O (n) вместо O (n ^ 2). Возможно, вам удастся изменить порядок тестов, чтобы всегда подразумевалась одна сторона неравенства. Даже если алгоритм идеален, выигрыш будет более вероятен, когда вы увидите, как этот код тестирует диапазон 10 миллионов раз, и вы найдете способ их пакетировать и использовать SSE для параллельного выполнения множества тестов.
источник
Это зависит от того, сколько раз вы хотите выполнить тест на одних и тех же данных.
Если вы выполняете тест один раз, вероятно, нет никакого существенного способа ускорить алгоритм.
Если вы делаете это для очень ограниченного набора значений, то вы можете создать справочную таблицу. Выполнение индексации может быть более дорогим, но если вы можете разместить всю таблицу в кеше, то вы можете удалить все ветвления из кода, что должно ускорить процесс.
Для ваших данных таблица поиска будет 128 ^ 3 = 2 097 152. Если вы можете управлять одной из трех переменных и учитывать все случаи, когда
start = N
одновременно, то размер рабочего набора уменьшается до128^2 = 16432
байтов, что должно соответствовать большинству современных кэшей.Вам все равно придется сравнить реальный код, чтобы увидеть, является ли таблица поиска без ответвлений достаточно быстрой, чем очевидные сравнения.
источник
bool between[start][end][x]
. Если вы знаете, как будет выглядеть ваш шаблон доступа (например, x монотонно увеличивается), вы можете создать таблицу, чтобы сохранить локальность, даже если вся таблица не помещается в памяти.Этот ответ должен сообщить о тестировании, выполненном с принятым ответом. Я выполнил тест с закрытым диапазоном для большого вектора отсортированного случайного целого числа, и, к моему удивлению, основной метод (low <= num && num <= high) фактически быстрее, чем принятый ответ выше! Тест проводился на HP Pavilion g6 (AMD A6-3400APU с оперативной памятью 6 ГБ. Вот основной код, использованный для тестирования:
по сравнению со следующим, который является принятым ответом выше:
Обратите внимание, что randVec является отсортированным вектором. Для любого размера MaxNum первый метод превосходит второй на моей машине!
источник
Для проверки любого переменного диапазона:
Это быстрее использовать битовую операцию:
Это уменьшит две ветви в одну.
Если вы заботитесь о типе сейфа:
Вы можете объединить больше проверки диапазона переменных вместе:
Это уменьшит 4 ветви в 1.
Это в 3,4 раза быстрее, чем старый в gcc:
источник
Разве нельзя просто выполнить побитовую операцию над целым числом?
Поскольку он должен быть между 0 и 128, если установлен 8-й бит (2 ^ 7), он равен 128 или более. Тем не менее, крайний случай будет болезненным, поскольку вы хотите получить инклюзивное сравнение.
источник
x <= end
, гдеend <= 128
. Неx <= 128
.