Dos cosas:1) no está utilizando la base de datos en toda su extensión y 2) su problema es un gran ejemplo para una extensión personalizada de PostgreSQL. He aquí por qué.
Solo está utilizando la base de datos como almacenamiento, almacenando colores como flotadores. En su configuración actual, independientemente del tipo de consulta, la base de datos siempre tendrá que verificar todos los valores (hacer un escaneo secuencial). Esto significa mucho IO y mucho cálculo para pocas coincidencias devueltas. Está tratando de encontrar los N colores más cercanos, por lo que hay algunas posibilidades sobre cómo evitar realizar cálculos en todos los datos.
Mejora sencilla
Lo más simple es limitar sus cálculos a un subconjunto de datos más pequeño. Puede suponer que la diferencia será mayor si los componentes difieren más. Si puede encontrar una diferencia segura entre los componentes, donde los resultados siempre son inapropiados, puede excluir esos colores por completo usando WHERE en rango con índices btree. Sin embargo, debido a la naturaleza del espacio de color L*a*b, es probable que esto empeore los resultados.
Primero crea los índices:
CREATE INDEX color_lab_l_btree ON color USING btree (lab_l);
CREATE INDEX color_lab_a_btree ON color USING btree (lab_a);
CREATE INDEX color_lab_b_btree ON color USING btree (lab_b);
Luego adapté su consulta para incluir una cláusula WHERE para filtrar solo colores, donde cualquiera de los componentes difiere en un máximo de 20.
Actualización: Después de otra mirada, agregar un límite de 20 muy probablemente empeorará los resultados, ya que encontré al menos un punto en el espacio, para el cual esto es cierto.:
SELECT
c.rgb_r, c.rgb_g, c.rgb_b,
DELTA_E_CIE2000(
25.805780252087963, 53.33446637366859, -45.03961353720049,
c.lab_l, c.lab_a, c.lab_b,
1.0, 1.0, 1.0) AS de2000
FROM color c
WHERE
c.lab_l BETWEEN 25.805780252087963 - 20 AND 25.805780252087963 + 20
AND c.lab_a BETWEEN 53.33446637366859 - 20 AND 53.33446637366859 + 20
AND c.lab_b BETWEEN -45.03961353720049 - 20 AND -45.03961353720049 + 20
ORDER BY de2000 ;
Llené la tabla con 100000 de colores aleatorios con tu script y probé:
Tiempo sin índices:44006,851 ms
Tiempo con consulta de índices y rango:1293.092 ms
Puede agregar esta cláusula WHERE a delta_e_cie1976_query
también, en mis datos aleatorios, reduce el tiempo de consulta de ~110 ms a ~22 ms.
Por cierto:obtuve el número 20 empíricamente:probé con 10, pero solo obtuve 380 registros, lo que parece un poco bajo y podría excluir algunas opciones mejores ya que el límite es 100. Con 20, el conjunto completo fue de 2900 filas y uno puede ser bastante Seguro que los partidos más cercanos estarán allí. No estudié el espacio de color DELTA_E_CIE2000 o L*a*b* en detalle, por lo que es posible que el umbral deba ajustarse a lo largo de diferentes componentes para que eso sea realmente cierto, pero se mantiene el principio de excluir datos que no son de interés.
Reescribir Delta E CIE 2000 en C
Como ya ha dicho, Delta E CIE 2000 es complejo y bastante inadecuado para implementar en SQL. Actualmente usa alrededor de 0,4 ms por llamada en mi computadora portátil. Implementarlo en C debería acelerar esto considerablemente. PostgreSQL asigna el costo predeterminado a las funciones SQL como 100 y las funciones C como 1. Supongo que esto se basa en la experiencia real.
Actualización: Como esto también soluciona uno de mis problemas, volví a implementar las funciones Delta E del módulo colormath en C como una extensión de PostgreSQL, disponible en PGXN . Con esto puedo ver una aceleración de alrededor de 150x para CIE2000 al consultar todos los registros de la tabla con 100k registros.
Con esta función de C, obtengo tiempos de consulta entre 147 ms y 160 ms para 100 000 colores. Con WHERE adicional, el tiempo de consulta es de unos 20 ms, lo que me parece bastante aceptable.
La mejor solución, pero avanzada
Sin embargo, dado que su problema es la búsqueda de N vecino más cercano en un espacio tridimensional, podría usar la indexación de K-Nearest-Neighbor que está en PostgreSQL desde la versión 9.1 .
Para que eso funcione, colocaría los componentes L*a*b* en un cubo . Esta extensión aún no es compatible con el operador de distancia ( está en proceso ), pero incluso si lo hiciera, no admitiría distancias Delta E y tendría que volver a implementarlo como una extensión C.
Esto significa implementar la clase de operador de índice GiST (btree_gist PostgreSQL extension
en contribución hace esto) para admitir la indexación de acuerdo con las distancias Delta E. Lo bueno es que luego podría usar diferentes operadores para diferentes versiones de Delta E, por ejemplo. <->
para Delta E CIE 2000 y <#>
para Delta E CIE 1976 y las consultas serían muy, muy rápido
para LIMIT pequeño incluso con Delta E CIE 2000.
Al final, puede depender de cuáles sean sus requisitos y limitaciones (empresariales).