Existe uma maneira mais rápida do que x >= start && x <= end
em C ou C ++ para testar se um número inteiro está entre dois números inteiros?
ATUALIZAÇÃO : minha plataforma específica é iOS. Isso faz parte de uma função de desfoque de caixa que restringe os pixels a um círculo em um determinado quadrado.
ATUALIZAÇÃO : Depois de tentar a resposta aceita , recebi um aumento de ordem de magnitude na única linha de código ao fazê-lo da x >= start && x <= end
maneira normal .
UPDATE : Aqui está o código depois e antes com o assembler do XCode:
NEW WAY
// 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
À MODA ANTIGA
#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
É incrível como reduzir ou eliminar a ramificação pode proporcionar uma velocidade tão dramática.
c++
c
performance
math
jjxtra
fonte
fonte
Respostas:
Há um velho truque para fazer isso com apenas uma comparação / ramificação. Se isso realmente melhora a velocidade pode ser questionável, e mesmo que isso aconteça, é provavelmente muito pouco para se notar ou se preocupar, mas quando você está começando apenas com duas comparações, as chances de uma grande melhoria são bastante remotas. O código se parece com:
Com um computador moderno e típico (ou seja, qualquer coisa que use dois complementos), a conversão para não assinado é realmente uma nop - apenas uma mudança na maneira como os mesmos bits são visualizados.
Observe que, em um caso típico, você pode pré-calcular
upper-lower
fora de um loop (presumido), para que isso normalmente não contribua com um tempo significativo. Juntamente com a redução do número de instruções de ramificação, isso também (geralmente) melhora a previsão de ramificação. Nesse caso, a mesma ramificação é obtida, independentemente do número estar abaixo da extremidade inferior ou acima da extremidade superior do intervalo.Quanto a como isso funciona, a idéia básica é bastante simples: um número negativo, quando visto como um número não assinado, será maior do que qualquer coisa que tenha começado como um número positivo.
Na prática, esse método converte
number
e o intervalo para o ponto de origem e verifica senumber
está no intervalo[0, D]
, ondeD = upper - lower
. Senumber
abaixo do limite inferior: negativo e se acima do limite superior: maior queD
.fonte
lower <= x & x <= upper
(em vez delower <= x && x <= upper
) resultaria em melhor desempenho também?É raro conseguir otimizações significativas para codificar em uma escala tão pequena. Grandes ganhos de desempenho advêm da observação e modificação do código de um nível superior. Você pode eliminar completamente a necessidade do teste de faixa ou apenas O (n) deles em vez de O (n ^ 2). Você pode reordenar os testes para que um lado da desigualdade esteja sempre implícito. Mesmo que o algoritmo seja ideal, é mais provável que ocorram ganhos quando você ver como esse código faz o teste de intervalo 10 milhões de vezes e encontrar uma maneira de agrupá-los e usar o SSE para fazer muitos testes em paralelo.
fonte
Depende de quantas vezes você deseja executar o teste com os mesmos dados.
Se você estiver executando o teste uma única vez, provavelmente não há uma maneira significativa de acelerar o algoritmo.
Se você estiver fazendo isso para um conjunto muito finito de valores, poderá criar uma tabela de pesquisa. A execução da indexação pode ser mais cara, mas se você pode ajustar a tabela inteira no cache, poderá remover todas as ramificações do código, o que deve acelerar as coisas.
Para seus dados, a tabela de pesquisa seria 128 ^ 3 = 2.097.152. Se você pode controlar uma das três variáveis e considerar todas as instâncias em que
start = N
ao mesmo tempo, o tamanho do conjunto de trabalho cai para128^2 = 16432
bytes, que devem se encaixar bem nos caches mais modernos.Você ainda teria que fazer referência ao código real para ver se uma tabela de pesquisa sem ramificação é suficientemente mais rápida que as comparações óbvias.
fonte
bool between[start][end][x]
. Se você sabe como será o seu padrão de acesso (por exemplo, x está aumentando monotonicamente), é possível projetar a tabela para preservar a localidade, mesmo que a tabela inteira não caiba na memória.Esta resposta é para relatar um teste feito com a resposta aceita. Realizei um teste de faixa fechada em um grande vetor de número inteiro aleatório classificado e, para minha surpresa, o método básico de (baixo <= num && num <= alto) é de fato mais rápido que a resposta aceita acima! O teste foi realizado no HP Pavilion g6 (AMD A6-3400APU com 6 GB de RAM. Aqui está o código principal usado para o teste:
comparado com o seguinte, que é a resposta aceita acima:
Preste atenção que o randVec é um vetor classificado. Para qualquer tamanho do MaxNum, o primeiro método supera o segundo na minha máquina!
fonte
Para qualquer verificação de faixa variável:
É mais rápido usar a operação de bit:
Isso reduzirá dois ramos em um.
Se você se preocupa com o tipo seguro:
Você pode combinar mais verificação de faixa variável:
Isso reduzirá 4 ramificações em 1.
É 3,4 vezes mais rápido que o antigo no gcc:
fonte
Não é possível apenas executar uma operação bit a bit no número inteiro?
Como deve estar entre 0 e 128, se o 8º bit estiver definido (2 ^ 7), será 128 ou mais. O caso extremo será um problema, pois você deseja uma comparação inclusiva.
fonte
x <= end
, ondeend <= 128
. Nãox <= 128
.