Utilizo una variación de un filtro mediano de 5 cruces en los datos de imagen en un pequeño sistema integrado, es decir
x
x x x
x
El algoritmo es realmente simple: lea 5 valores enteros sin signo, obtenga los 2 más altos, haga algunos cálculos y escriba el resultado entero sin signo.
Lo que es bueno es que los 5 valores de entrada enteros están todos en el rango de 0-20. ¡El valor entero calculado también está en el rango 0-20!
A través de la elaboración de perfiles, he descubierto que obtener los dos números más grandes es el cuello de botella, así que quiero acelerar esta parte. ¿Cuál es la forma más rápida de realizar esta selección?
El algoritmo actual usa una máscara de 32 bits con 1 en la posición dada por los 5 números y una función CLZ compatible con HW.
Debo decir que la CPU es propietaria, no está disponible fuera de mi empresa. Mi compilador es GCC pero hecho a medida para esta CPU.
He intentado averiguar si puedo usar una tabla de búsqueda, pero no he podido generar una clave que pueda usar.
Tengo combinaciones para la entrada, pero el orden no es importante, es decir, es el mismo que .[5,0,0,0,5]
[5,5,0,0,0]
¡Sucede que la siguiente función hash produce un hash perfecto sin colisiones!
def hash(x):
h = 0
for i in x:
h = 33*h+i
return h
Pero el hash es enorme y simplemente no hay suficiente memoria para usarlo.
¿Hay un mejor algoritmo que pueda usar? ¿Es posible resolver mi problema usando una tabla de búsqueda y generando una clave?
fuente
hash
ya realiza más operaciones. ¿Las llamadas posteriores al método están relacionadas, por ejemplo, la central sex
mueve a través de la matriz fila por fila?Respuestas:
En mi otra respuesta , sugiero que los saltos condicionales podrían ser el principal impedimento para la eficiencia. Como consecuencia, las redes de clasificación vienen a la mente: son independientes de los datos, es decir, la misma secuencia de comparaciones se ejecuta sin importar la entrada, y solo los intercambios son condicionales.
Por supuesto, la clasificación puede ser demasiado trabajo; solo necesitamos los dos números más grandes. Por suerte para nosotros, las redes de selección también se han estudiado. Knuth nos dice que encontrar los dos números más pequeños de cinco² se puede hacer con comparaciones [1, 5.3.4 ex 19] (y como máximo tantos intercambios).U^2(5)=6
La red que da en las soluciones (reescrita en matrices basadas en cero) es
que implementa, después de ajustar la dirección de las comparaciones, en pseudocódigo como
Ahora, las implementaciones ingenuas todavía tienen saltos condicionales (a través del código de intercambio). Sin embargo, dependiendo de su máquina, puede evitarlos con instrucciones condicionales. x86 parece ser su fango habitual; ARM parece más prometedor ya que aparentemente la mayoría de las operaciones son condicionales en sí mismas. Si entiendo las instrucciones correctamente, el primer intercambio se traduce en esto, suponiendo que nuestros valores de matriz se hayan cargado en los registros a
R0
través deR4
:Sí, sí, por supuesto, puede usar el intercambio XOR con EOR .
Solo espero que tu procesador tenga esto o algo similar. Por supuesto, si construye la cosa para este propósito, ¿tal vez pueda conectar la red allí?
Esto es probablemente (¿probablemente?) Lo mejor que puede hacer en el ámbito clásico, es decir, sin hacer uso del dominio limitado y realizar magias malvadas dentro de las palabras.
fuente
Solo para que esté sobre la mesa, aquí hay un algoritmo directo:
Mediante la implementación inteligente de
if ... else
, uno puede deshacerse de algunos saltos incondicionales que tendría una traducción directa.Esto es feo pero solo toma
De hecho, seis comparaciones son óptimas para este problema como lo muestra el Teorema S en la sección 5.3.3 de [1]; aquí necesitamos .W2(5)
Sin embargo, no se puede esperar que esto sea rápido en máquinas con tuberías; dado que tienen un alto porcentaje de saltos condicionales, la mayor parte del tiempo probablemente se gastaría en el puesto.
Tenga en cuenta que una variante más simple, ordenar
x1
yx2
luego insertar los otros valores posteriormente, toma de cuatro a siete comparaciones y solo de cinco a seis asignaciones. Como espero que los saltos sean de mayor costo aquí, me quedé con este.fuente
Esta podría ser una gran aplicación y un caso de prueba para el proyecto Souper . Souper es un superoptimizador : una herramienta que toma una secuencia corta de código como entrada y trata de optimizarla lo más posible (trata de encontrar una secuencia equivalente de código que sea más rápida).
Souper es de código abierto. Puede intentar ejecutar Souper en su fragmento de código para ver si puede hacerlo mejor.
Vea también el concurso de John Regehr sobre cómo escribir código rápido para ordenar 16 valores de 4 bits ; Es posible que algunas de las técnicas allí sean útiles.
fuente
T[T[T[441*a+21*b+c]*21+d]*21+e]
fuente