Due cose:1) non stai utilizzando il database nella sua piena estensione e 2) il tuo problema è un ottimo esempio per un'estensione PostgreSQL personalizzata. Ecco perché.
Stai solo usando il database come memoria, memorizzando i colori come float. Nella tua configurazione attuale, indipendentemente dal tipo di query, il database dovrà sempre controllare tutti i valori (effettuare una scansione sequenziale). Ciò significa molto IO e molti calcoli per poche corrispondenze restituite. Stai cercando di trovare gli N colori più vicini, quindi ci sono alcune possibilità su come evitare di eseguire calcoli su tutti i dati.
Semplice miglioramento
La cosa più semplice è limitare i tuoi calcoli a un sottoinsieme di dati più piccolo. Si può presumere che la differenza sarà maggiore se i componenti differiscono maggiormente. Se riesci a trovare una differenza sicura tra i componenti, dove i risultati sono sempre inappropriati, puoi escludere del tutto quei colori usando il range WHERE con gli indici btree. Tuttavia, a causa della natura dello spazio colore L*a*b, questo probabilmente peggiorerà i tuoi risultati.
Per prima cosa crea gli indici:
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);
Quindi ho adattato la tua query per includere una clausola WHERE per filtrare solo i colori, in cui uno qualsiasi dei componenti differisce per un massimo di 20.
Aggiornamento: Dopo un'altra occhiata, l'aggiunta di un limite di 20 molto probabilmente peggiorerà i risultati, dal momento che ho trovato almeno un punto nello spazio, per il quale questo vale.:
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 ;
Ho riempito la tabella con 100000 di colori casuali con il tuo script e ho testato:
Tempo senza indici:44006.851 ms
Tempo con indici e query di intervallo:1293.092 ms
Puoi aggiungere questa clausola WHERE a delta_e_cie1976_query
inoltre, sui miei dati casuali diminuisce il tempo di query da ~110 ms a ~22 ms.
A proposito:ho ottenuto il numero 20 empiricamente:ho provato con 10, ma ho ottenuto solo 380 record, il che sembra un po' basso e potrebbe escludere alcune opzioni migliori poiché il limite è 100. Con 20 il set completo era di 2900 righe e uno può essere abbastanza certo che ci saranno le partite più vicine. Non ho studiato in dettaglio lo spazio colore DELTA_E_CIE2000 o L*a*b*, quindi potrebbe essere necessario regolare la soglia lungo diversi componenti affinché ciò sia effettivamente vero, ma vale il principio di esclusione dei dati non interessanti.
Riscrivi Delta E CIE 2000 in C
Come hai già detto, Delta E CIE 2000 è complesso e abbastanza inadatto per l'implementazione in SQL. Attualmente utilizza circa 0,4 ms per chiamata sul mio laptop. Implementarlo in C dovrebbe accelerare notevolmente questo. PostgreSQL assegna il costo predefinito alle funzioni SQL come 100 e le funzioni C come 1. Immagino che questo sia basato su un'esperienza reale.
Aggiornamento: Dal momento che questo graffia anche uno dei miei pruriti, ho reimplementato le funzioni Delta E dal modulo colormath in C come estensione PostgreSQL, disponibile su PGXN . Con questo posso vedere una velocità di circa 150x per CIE2000 quando si interrogano tutti i record dalla tabella con 100k record.
Con questa funzione C, ottengo tempi di query compresi tra 147 ms e 160 ms per 100.000 colori. Con WHERE extra, il tempo di query è di circa 20 ms, il che sembra abbastanza accettabile per me.
Soluzione migliore, ma avanzata
Tuttavia, poiché il tuo problema è la ricerca del vicino più vicino N nello spazio tridimensionale, puoi utilizzare l'indicizzazione K-Nearest-Neighbor che è in PostgreSQL dalla versione 9.1 .
Affinché funzioni, inseriresti i componenti L*a*b* in un cubo . Questa estensione non supporta ancora l'operatore a distanza ( è in lavorazione ), ma anche se lo facesse, non supporterebbe le distanze Delta E e dovresti reimplementarlo come estensione C.
Ciò significa implementare la classe dell'operatore dell'indice GiST (btree_gist estensione PostgreSQL
in contrib fa questo) per supportare l'indicizzazione in base alle distanze Delta E. La parte buona è che potresti quindi utilizzare diversi operatori per diverse versioni di Delta E, ad es. <->
per Delta E CIE 2000 e <#>
per Delta E CIE 1976 e le query sarebbero davvero molto veloci
per piccoli LIMITI anche con Delta E CIE 2000.
Alla fine può dipendere da quali sono i tuoi requisiti e vincoli (aziendali).