Da quando ho iniziato a usare Darktable, intorno al 2012, sono sempre rimasto sorpreso dalla poca RAM che utilizzava. La gente pensa che sia una buona cosa che un’applicazione usi la memoria con parsimonia, e questo è senz’altro vero se parliamo del tuo ambiente desktop. Ma parlando di un software di produzione che esegue un pesante rendering dei pixel su immagini da 12 a 54 Mpixel, questo significa che gli stessi calcoli pesanti vengono ripetuti più e più volte invece di essere salvati per essere riutilizzati in seguito. È a questo che serve una cache: evitare calcoli costosi. E dovrebbe usare tutta la RAM disponibile a tale scopo, perché calcolare significa sprecare energia, e questo ha un impatto concreto se stai lavorando a batteria. Inoltre, hai pagato per quella RAM e usarla non ti scarica la batteria. La CPU/GPU d’altro canto…

Sì, i fotoreporter e molti nomadi digitali ritoccano a batteria su laptop, sul campo. È ora che il mondo open-source si renda conto che non tutti i fotografi sono seduti alla loro scrivania la sera e nei fine settimana, dopo il loro «vero» lavoro diurno. Quindi, calcolare e ricalcolare le stesse cose più e più volte significa semplicemente sprecare energia, che tu sia collegato alla presa o alimentato a batteria.

Nel frattempo, Darktable ha sempre avuto quella strana piccola impostazione della RAM che ti permetteva di definire quanta volevi che ne usasse. Solo che non aveva alcun modo di tracciare effettivamente quanta RAM usava, quindi era più una guida che una regola. In seguito questa è stata sostituita da una preferenza qualitativa delle «risorse» completamente idiota che sia non significa nulla, sia in realtà non ti dice quanta RAM allochi al software.

Quindi, la cache RAM di Darktable non ha mai davvero funzionato, era sottoutilizzata e c’era bisogno di sistemarla. Perché, di conseguenza, tutto veniva ricalcolato in continuazione senza motivo.

Ma… C’era un motivo per cui la cache era sottoutilizzata e non funzionava davvero: quel software aveva visibilità zero sul ciclo di vita dei dati, che è una specie di prerequisito se vuoi memorizzare dati da riutilizzare. Questo derivava da una pipeline dei pixel davvero contorta (mi chiedo davvero se qualcuno sia in grado di disegnare il diagramma di flusso di ciò che facciamo ai pixel, dall’inizio alla fine, tra gli «sviluppatori» di Darktable), ma anche da metadati delle immagini che venivano calcolati in vari punti, copiati e memorizzati (e poi parzialmente aggiornati) in vari punti…

La cache della pipeline aveva un solo scopo: allocare i buffer di input e output dei moduli, e possibilmente riutilizzarli se già disponibili. Ma la sua dimensione era definita in numero di voci, che era davvero limitato, e non in quantità di memoria allocata. Questo la rendeva moderatamente utile e ci costringeva a usarla in modo troppo conservativo, perché la sua effettiva dimensione in memoria era del tutto imprevedibile.

Inutile dire che «sistemare la cache» implicava «sistemare la pipeline», che a sua volta implicava «rendere il ciclo di vita dei dati visibile, tracciabile e gestito globalmente», il che implicava riprogettare l’architettura di alto livello del software. Come vedremo, questo ha avuto benefici inaspettati sulle prestazioni, che superano ampiamente le ottimizzazioni interne a livello di modulo che Darktable ha fatto nello stesso periodo.

Cosa è cambiato dal 2022

Questo articolo è il seguito diretto di Sistemare la cache della pipeline e bug vecchi di 10 anni. Quel capitolo precedente riguardava l’individuazione del marciume e l’arresto dei fallimenti più imbarazzanti. Questo riguarda ciò che è accaduto dopo, quando è diventato ovvio che i bug della cache non erano affatto bug isolati, ma sintomi di un renderer le cui responsabilità erano state mescolate insieme per troppo tempo.

Dove questo si colloca storicamente

Il problema originale non era semplicemente «la cache non memorizza abbastanza». Il vero problema era che l’intero stack della pipeline era cresciuto attorno a comportamenti accidentali. I ridisegni della GUI potevano finire per avviare l’elaborazione delle immagini, in modo non regolamentato. L’anteprima delle maschere faceva trapelare lo stato della GUI nel renderer. La logica dell’istogramma e del contagocce dipendeva da effetti collaterali particolari della pipeline. Esportazione, miniature, anteprima e camera oscura avevano ognuno le loro piccole varianti di «sicuramente possiamo semplicemente copiare quel buffer un’altra volta ancora». E ogni volta che la coerenza diventava difficile da dimostrare (durante la visualizzazione delle maschere o le modifiche del ritaglio), la strategia di ripiego era spesso svuotare le righe di cache e ricalcolare tutto, perché a quanto pare «dagli fuoco e ricomincia da capo» è passato per architettura per un certo periodo.

Alcuni dei commenti nel codice lo dicevano già. C’erano avvisi sul ricalcolo guidato dai ridisegni. C’era persino una vecchia nota che diceva che la cache sarebbe dovuta alla fine diventare globale. Quindi gli ultimi anni non hanno riguardato l’invenzione di una nuova teoria da zero. Hanno riguardato per lo più il fare finalmente la pulizia che il codice implorava fin dagli anni 2010.

Il cambiamento storico chiave è che la pipeline ha smesso di essere trattata come un unico blob ricorsivo dal quale ci si aspettava che scoprisse le proprie esigenze durante l’esecuzione. È stata progressivamente suddivisa in percorsi di esecuzione più chiari per il rendering interattivo, il rendering headless, l’elaborazione GPU, l’elaborazione CPU e i consumatori lato GUI. Allo stesso tempo, il comportamento della GUI è stato rispedito alla GUI. Disegnare è tornato a essere disegnare. Elaborare è tornato a essere elaborare. Sembra banale, il che è di solito un segno che il progetto precedente era diventato assurdo.

I checksum, ovvero come la validità ha smesso di essere una questione di indovinelli

Una volta che il renderer non era più intrecciato con ogni widget e ogni canale collaterale, è diventato possibile definire lo stato dell’immagine in modo esplicito. Ogni voce di cronologia ora porta con sé un checksum del modulo costruito a partire dai parametri del modulo, dai parametri di fusione e da qualsiasi forma di maschera pertinente. Questo dà a ogni stato del modulo un’impronta digitale canonica non appena entra nella cronologia, invece di lasciare che la validità venga dedotta in seguito da qualunque cosa sia capitato di eseguire.

Questo da solo non era sufficiente, perché una pipeline non è solo un mucchio di impostazioni dei moduli. Dipende anche dall’ordine, dalla regione di interesse, dai contratti dei buffer e dallo stato relativo alla visualizzazione. Quindi la fase di pianificazione accumula quelle impronte digitali a livello di modulo attraverso l’intera catena e incorpora il resto del contratto di runtime prima che si muova un solo pixel. Il risultato è un’identità stabile per ogni fase e un’identità finale della pipeline.

È qui che appare finalmente la visibilità. La pipeline può sapere se è ancora valida confrontando l’hash della sua cronologia. Il backbuffer visualizzato può sapere se corrisponde ancora allo stato attuale della pipe e della cronologia. Una riga di cache può sapere se appartiene davvero esattamente alla fase, alla ROI e ai parametri attualmente richiesti. Suona quasi offensivamente ragionevole, ma ha richiesto prima un sacco di pulizia: la sincronizzazione della cronologia con la pipeline ha dovuto essere resa esplicita, il bypass della cache ha dovuto essere dichiarato in anticipo (per l’anteprima delle maschere e le modifiche di ritaglio), la visualizzazione delle maschere non poteva più mutare le regole a metà esecuzione, e i vecchi effetti collaterali innescati dai ridisegni dovevano sparire. Non si ottengono hash affidabili sopra un flusso di controllo inaffidabile.

Il beneficio pratico è che la coerenza non viene più mantenuta per superstizione. Non abbiamo più bisogno di chiederci «la GUI ha probabilmente invalidato la cosa giusta al momento giusto?» La risposta è nei checksum. O lo stato corrisponde, o non corrisponde. E possiamo prendere qualsiasi output di modulo dal livello GUI, senza eseguire una pipeline, perché il checksum del modulo è prevedibile e può essere calcolato prima del rendering, o in parallelo.

L’architettura cache-first

Hash affidabili cambiano l’architettura quasi per forza. Una volta che ogni stato significativo della pipeline ha un’identità stabile nota in anticipo, la cache smette di essere un’ottimizzazione best-effort e diventa il centro del flusso di dati.

Ecco perché la pipeline ora ha una vera fase di pianificazione prima dell’esecuzione. Prima viene sincronizzata la cronologia. Le regioni di interesse vengono propagate attraverso l’intera catena. Ogni fase ottiene un contratto esplicito di input e output. La politica di cache lato host viene sigillata prima del runtime. Gli hash globali vengono calcolati prima dell’esecuzione. In altre parole, il motore sa cosa sta per fare prima di iniziare a farlo, il che è una piacevole rottura con il vecchio metodo di scoprirlo in corso d’opera e compensare con degli hack.

Da lì, le ricerche nella cache diventano deterministiche. Una fase può riaprire esattamente il buffer che corrisponde al suo stato, così un modulo può bypassare interamente i calcoli semplicemente guardando nella cache se una riga di cache corrisponde già al suo stato interno, come una ricerca per checksum. I consumatori a valle possono riaprire i risultati pubblicati per hash invece di chiedere al renderer di eseguire un’altra danza speciale solo per loro. La cache smette di essere un mucchio di memoria vagamente riutilizzabile e diventa il registro autorevole dei risultati intermedi e finali validi.

Questo spiega anche perché così tanto codice ha potuto essere rimosso o semplificato. Una volta che la validità è centralizzata, servono meno euristiche, meno rami speciali, meno clausole «tranne quando la GUI è in questa modalità», e meno svuotamenti d’emergenza. Il codice diventa più corto non perché il problema si sia rimpicciolito, ma perché ha smesso di essere risolto in sei punti contraddittori tutti in una volta.

Le righe di cache legano anche la memoria GPU e CPU, il che ci permette di usare la memoria pinned di OpenCL. Da sempre esiste una preferenza utente OpenCL che permetteva di abilitare o meno la memoria pinned, ma ho scoperto che quell’opzione veniva usata solo per l’elaborazione a tasselli, quando i moduli non riuscivano a far stare tutte le loro esigenze di memoria sulla GPU. La memoria pinned è ora stata estesa anche alle operazioni normali, poiché consente di mappare RAM e VRAM senza copiare. L’uso della memoria pinned su una pipeline GPU completamente in cache riduce il tempo speso a spostare i buffer di memoria fino a un fattore di 7.

Ma forzare la memorizzazione in cache degli output dei moduli OpenCL, anche con buffer pinned a copia zero, ha comunque un certo costo, ed è più costoso che saltare del tutto la cache. Per alcuni moduli pesanti (riduzione del rumore profilata, diffusione o nitidezza, equalizzatore di contrasto) l’overhead della cache vale sicuramente la pena, ma per quelli veloci (bilanciamento del bianco, esposizione, profilo colore di input) il costo della memorizzazione in cache dei buffer OpenCL faceva sì che gli interi moduli girassero effettivamente più lentamente su GPU che su CPU.

Quindi abbiamo ora euristiche per modulo che decidono se un modulo OpenCL pubblicherà il suo buffer di output sulla cache RAM. I moduli pesanti e tutti i moduli che catturano il contagocce colore o gli istogrammi vengono pubblicati per impostazione predefinita. Per gli altri, gli utenti possono decidere da soli:

image

Questo permetterà agli utenti anche di disabilitare OpenCL selettivamente su alcuni moduli quando hanno problemi ricorrenti con essi. Per esempio, riduzione del rumore profilata e antifoschia consumano molta memoria GPU e possono fallire durante l’esecuzione per mancanza di memoria disponibile, anche se la pianificazione iniziale della memoria aveva rilevato che dovesse essere sufficiente. Quando ciò accade, il modulo aveva già iniziato a calcolare da un po’ di tempo e ripiegherà sulla CPU. Se ciò accade di frequente, meglio bypassare la GPU su quel modulo tutte le volte piuttosto che pagare due volte la penalità.

Ma non finisce qui. I buffer GPU vengono mantenuti in vita anche sulla GPU. Significa che riavviare una pipeline parziale a partire da un modulo OpenCL ottiene l’input direttamente dalla GPU quando possibile, senza alcuna copia dalla RAM. Significa anche che i moduli leggeri sono in grado di riutilizzare il loro precedente buffer GPU se ci sta, invece di allocarne uno nuovo. Questo da solo apre la possibilità di avere una pipeline in tempo reale, necessaria per il nuovo modulo Disegno, con circa 60-80 ms di latenza tra le pennellate e gli aggiornamenti dell’immagine. Non avrei mai pensato che questo sarebbe stato possibile.

La stessa riallocazione di memoria viene eseguita nella RAM poiché Ansel ora ospita l’intera cache nella sua arena di memoria . Questo schema non solo ci permette di pre-riservare un blocco di memoria contiguo dal sistema operativo all’avvio dell’applicazione, portando ad allocazioni più veloci, ma garantisce anche che non superiamo il volume di memoria definito dall’utente. Quindi, quando decidi di allocare 8 GB di RAM ad Ansel, ora siamo in grado di garantire che ciò accada davvero: riempiremo tutto quel volume fino a che è pieno, dopodiché iniziamo a riciclare i buffer più vecchi. Tutti i buffer di pixel, input e output dei moduli, ma anche i blocchi temporanei interni di appunti, vengono allocati nella nostra arena di memoria.

E, ultima ma non meno importante, c’è una sola cache della pipeline per l’intera applicazione, il che significa che tutti i tipi di pipeline (anteprima della camera oscura, immagine principale della camera oscura, esportazione delle miniature ed esportazione dei file) condividono la stessa cache. Sebbene ciò abbia dovuto essere gestito per l’accesso concorrente multithread, significa che se una pipeline ha bisogno dell’output di un modulo che è già stato calcolato alla stessa dimensione da un’altra pipeline, può prendere direttamente la riga di cache e bypassare il proprio ricalcolo.

Per sfruttare ciò, l’«anteprima» della camera oscura precedentemente a dimensione fissa (ovvero la versione ridotta dell’immagine completa, usata nel widget di navigazione, per campionare istogrammi e contagocce colore, e come riempimento transitorio «sfocato» mentre l’immagine principale viene ricalcolata) è stata modificata per essere dimensionata dinamicamente al livello di zoom «adatta» dell’immagine principale (quella visualizzata al centro della camera oscura). Il risultato è che solo un’immagine deve essere calcolata quando entri nella camera oscura, e tornare allo zoom «adatta» è istantaneo.

Ma, poiché quell’anteprima a dimensione fissa era stata usata pigramente in molti punti della GUI per rimappare le coordinate del cursore nello spazio del widget alle coordinate dell’immagine RAW a piena risoluzione, tramite copia-incolla dello stesso brutto blocco di codice ovunque (copiando persino il commento che diceva «non capisco cosa faccia questo»), l’intero sistema di coordinate ha dovuto essere rifatto per accogliere questo cambiamento. E, di nuovo, quelle coordinate venivano calcolate solo all’interno di una pipeline, quindi la GUI doveva aspettare che la pipeline ricalcolasse le nuove dimensioni per sapere dove posizionare gli oggetti, il che innescava glitch della GUI specialmente quando si cambiava il ritaglio. Ma ora, tutto ciò viene calcolato in anticipo da un’API unificata, quindi niente più stupidi copia-incolla moltiplicati per il numero di moduli.

Cosa è cambiato al di fuori del core

La prima conseguenza esterna è che le copie hanno smesso di mascherarsi da funzioni. La camera oscura non ha più bisogno di un backbuffer GUI dedicato per visualizzare ciò che il renderer ha già prodotto. Può prendere l’immagine pubblicata direttamente dalla cache, legarci sopra una superficie Cairo e visualizzarla in pochi millisecondi. In precedenza, il percorso era più lento e affaccendato in copie senza alcuna buona ragione oltre alla confusione di progettazione ereditata. La camera oscura può anche verificare direttamente se un dato backbuffer della pipeline è aggiornato rispetto alla cronologia di sviluppo attuale, tramite i checksum. In precedenza, ciò si basava su un flag «dirty» che poteva essere sovrascritto da diversi thread e impediva qualsiasi ridisegno «best effort» della camera oscura (nel senso di: sbatti lì l’ultimo backbuffer valido noto in attesa del ricalcolo di quello aggiornato). Questo era importante quando l’utente cambiava i parametri dei moduli più velocemente di quanto la pipeline potesse ricalcolare: abbiamo comunque bisogno di qualcosa per riempire la vista per evitare lo sfarfallio.

La stessa logica ora si estende all’esportazione e alle miniature, che possono riaprire il risultato finale pubblicato invece di ricostruire la propria realtà privata. Anche il codice dell’istogramma e del contagocce si è allontanato dall’appoggiarsi all’esecuzione della pipeline. Gli oscilloscopi seguono segnali di completamento espliciti dal percorso di anteprima, memorizzano nella cache la propria superficie Cairo rispetto allo stato di origine, e campionano direttamente dai backbuffer supportati dalla cache. L’istogramma raw (campionato dopo il modulo demosaicizzazione) è stato aggiunto all’output della vecchia pipeline: passare dall’uno all’altro non richiede un ricalcolo della pipeline, e nemmeno il prelievo del colore in nessuno dei due. Si tratta di un progetto molto più noioso, che è esattamente il motivo per cui è migliore.

Un’altra conseguenza importante è che le varianti di pipeline non vivono più in silos di cache isolati. Anteprima, camera oscura, esportazione, miniature e consumatori di istogramma possono ora condividere lo stato della cache. Questa era l’intenzione già accennata molto tempo fa nel codice della cache; c’è solo voluta una pulizia completa del tracciamento della validità per renderla reale. Una volta che tutti quei consumatori concordano sugli stessi hash, possono finalmente concordare anche sugli stessi buffer. Il beneficio è che una pipeline può riutilizzare la riga di cache già calcolata da un’altra pipe se ci sta (stesso parametro, stessa dimensione di input e output). Quindi l’inizio delle pipeline della camera oscura è stato commutato a piena risoluzione: la demosaicizzazione e la riduzione del rumore saranno più lente all’ingresso nella camera oscura, ma poi lo zoom e la panoramica diventano istantanei. E l’esportazione di un’immagine prenderà anche queste fasi iniziali direttamente dalla cache. Tra l’altro, esportare la stessa immagine due volte a due risoluzioni diverse ricalcola solo a partire dal modulo di scalatura, alla fine della pipeline.

La gestione della memoria ha seguito la stessa logica. Le righe di cache sono diventate oggetti con conteggio dei riferimenti, bloccabili e riutilizzabili invece che blob usa e getta passati in giro attraverso archiviazioni collaterali ad hoc. I percorsi CPU e GPU ora condividono molto di più lo stesso modello di proprietà. Sul lato OpenCL, i buffer host e device sono gestiti con regole esplicite sul ciclo di vita, la memoria pinned viene riutilizzata invece di essere continuamente rigenerata, e RAM e VRAM restano sincronizzate tramite trasferimenti pinned e a copia zero dove possibile, invece di essere rispecchiate attraverso inutili copie intermedie. Questo non rende la GPU magicamente più veloce nella matematica. Per lo più impedisce all’idraulica circostante di sprecare i guadagni.

Il beneficio non è solo la velocità nei benchmark. È anche la latenza, la robustezza e il consumo energetico. L’avvio della camera oscura diventa più breve perché ci sono meno copie e meno passaggi duplicati. Zoom, panoramica, anteprima delle maschere e navigazione della cronologia smettono di invalidare metà dell’universo solo perché dimostrare la correttezza era troppo difficile. OpenCL diventa meno casuale perché il ripiego, la suddivisione in tasselli e la proprietà della memoria sono dichiarati in modo più esplicito. Il software passa meno tempo a spostare i pixel in giro solo per rassicurarsi che esistano ancora da qualche parte.

Come effetto collaterale, la gestione degli errori è stata migliorata nella pipeline. I moduli che non riescono ad allocare memoria ora lanciano immediatamente un errore e interrompono la pipeline. In precedenza, i fallimenti di allocazione non venivano controllati ovunque, e un’intera pipeline poteva portare un buffer NULL fino alla fine… o andare in crash bello e buono con errori di segmentation fault. Questo viene ora prevenuto a monte in modo uniforme in tutto il software.

Il campionamento del contagocce colore e gli istogrammi possono campionare dati senza innescare un ricalcolo della pipeline recuperando direttamente dalla cache, se la riga di cache richiesta esiste. La casella degli strumenti globale oscilloscopi ti permette di scegliere tra RGB RAW (campionato dopo la demosaicizzazione) o RGB finale della pipeline (campionato o dopo il profilo colore di output in virgola mobile a 32 bit o nel backbuffer in interi a 8 bit): passare dall’uno all’altro non riavvia una pipeline ma carica semplicemente una riga di cache diversa, e il prelievo del colore avviene su quella riga di cache in modo trasparente.

Cosa è stato effettivamente sistemato facendolo per bene

Molti fastidi visibili risalgono a questa riprogettazione più che a una qualsiasi singola micro-ottimizzazione. La gestione della ROI ad alta DPI è migliorata perché la proprietà della geometria è diventata più chiara e la mappatura delle coordinate tra la GUI e l’immagine RAW è ora gestita in un’API centrale. La risincronizzazione cronologia-pipeline è diventata meno fragile perché la cronologia ha smesso di essere un effetto collaterale incidentale del flusso della GUI ed è diventata un input di prima classe per la pianificazione. L’anteprima delle maschere in tempo reale non deve più combattere contro un modello di cache progettato per un’altra epoca. L’aggiornamento dell’istogramma è diventato prevedibile perché ora consuma dati pubblicati invece di sperare che il buffer intermedio giusto sia per caso ancora vivo da qualche parte. Loop infiniti, righe di cache obsolete, backbuffer non validi e il classico «l’anteprima ha ricalcolato l’intera pipe dal modulo 0 senza motivo ovvio» diventano tutti più facili da diagnosticare una volta che il ciclo di vita è esplicito.

Quindi la parte interessante qui non è che sia avvenuto un grande refactor in isolamento. È che una lunga serie di pulizie si è finalmente allineata attorno a un’unica idea: la validità dovrebbe essere calcolata, denominata e condivisa in anticipo. Una volta che ciò è accaduto, il resto è seguito quasi meccanicamente. La cache è diventata centrale perché gli hash erano affidabili. I consumatori esterni hanno potuto smettere di copiare perché i buffer pubblicati nella cache sono diventati autorevoli. I percorsi CPU e GPU hanno potuto condividere più stato perché la proprietà è diventata esplicita. E il renderer ha smesso di comportarsi come una casa infestata dove muovere un solo cursore poteva risvegliare tre sottosistemi non correlati e un callback di ridisegno del 2013.

Benchmark

I benchmark sono eseguiti su entrambe le applicazioni compilate localmente con build Release e GCC14 usando i rispettivi script build.sh --build-type Release.

Config 1
L’hardware è un Thinkpad P51, con 8 × CPU Intel Xeon E3-1505M v6 @ 3.00GHz e GPU Nvidia Quadro M2200, con Fedora 41 Plasma su Wayland. La versione del driver Nvidia è 580.105.08. L’Intel Xeon è una CPU di fascia alta rilasciata nel 2015. La Quadro M2200 è una CPU professionale di fascia media rilasciata nel 2017.
Config 2
L’hardware è un PC desktop con 4 × Intel Core i3-4130 e GPU AMD Radeon RX 580 Polaris 10, con Ubuntu 24.04 LTS su Wayland. La versione del driver AMD è RustiCL 25.2.8. Il Core i3 è una CPU di fascia media rilasciata nel 2013. L’AMD Polaris 10 è una GPU di fascia medio-alta rilasciata nel 2017.

Mostriamo qui l’output di ansel -d perf e darktable -d perf. I file di configurazione sia di Ansel sia di Darktable sono impostati con questo script  per garantire la stessa configurazione di base. L’immagine di test da 45 Mpx è presa da qui  e l’XMP di editing può essere scaricato qui.

Caricamento iniziale in camera oscura

Attenzione: in Ansel, è importante eseguire questo test senza alcun modulo con il focus nella GUI (cioè, non collassato). In questa situazione, Ansel memorizza sempre in cache l’output del modulo precedente così da garantire che, quando vengono apportate modifiche in quel modulo con il focus, si riavviino sempre i calcoli dall’input immediato. Ma forzare la cache in questo modo innesca un overhead di memoria che può essere visibile per i moduli leggeri.

Config 1

 13.690316 [dev_pixelpipe] took 0.000 secs (0.000 CPU) to load the image.
 24.201079 [dev_pixelpipe] pipeline resync with history took 0.029 secs (0.026 CPU) for pipe virtual-preview
 34.260916 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
 44.260963 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
 54.327523 [dev_pixelpipe] pipeline resync with history took 0.019 secs (0.025 CPU) for pipe preview
 64.384013 [dev_pixelpipe] took 0.056 secs (0.072 CPU) initing base buffer [preview]
 74.402520 [dev_pixelpipe] took 0.018 secs (0.031 CPU) processed `Raw settings' on GPU, blended on  [preview]
 84.421489 [dev_pixelpipe] took 0.016 secs (0.012 CPU) processed `white balance' on GPU, blended on  [preview]
 96.912680 [dev_pixelpipe] took 2.491 secs (2.470 CPU) processed `highlight reconstruction' on GPU, blended on GPU [preview]
107.167233 [dev_pixelpipe] took 0.254 secs (0.144 CPU) processed `demosaic' on GPU, blended on  [preview]
117.470521 [dev_pixelpipe] took 0.303 secs (0.173 CPU) processed `lens correction' on GPU, blended on  [preview]
127.629764 [dev_pixelpipe] took 0.159 secs (0.172 CPU) processed `Initial resampling' on GPU, blended on  [preview]
137.635593 [dev_pixelpipe] took 0.006 secs (0.006 CPU) processed `exposure' on GPU, blended on GPU [preview]
147.638110 [dev_pixelpipe] took 0.002 secs (0.001 CPU) processed `input color profile' on GPU, blended on  [preview]
157.639667 [dev_pixelpipe] took 0.002 secs (0.002 CPU) processed `color calibration' on GPU, blended on GPU [preview]
168.726634 [dev_pixelpipe] took 1.087 secs (1.090 CPU) processed `diffuse or sharpen' on GPU, blended on GPU [preview]
178.735900 [dev_pixelpipe] took 0.009 secs (0.009 CPU) processed `color balance rgb' on GPU, blended on GPU [preview]
188.742309 [dev_pixelpipe] took 0.006 secs (0.007 CPU) processed `filmic rgb' on GPU, blended on GPU [preview]
198.765175 [dev_pixelpipe] took 0.023 secs (0.029 CPU) processed `output color profile' on GPU, blended on  [preview]
208.808546 [dev_pixelpipe] took 0.043 secs (0.049 CPU) processed `dithering' on CPU, blended on  [preview]
218.820376 [dev_pixelpipe] took 0.012 secs (0.061 CPU) processed `display encoding' on CPU, blended on  [preview]
228.820439 [pixelpipe] preview internal pixel pipeline processing took 4.493 secs (4.332 CPU)
238.820483 [dev_process_preview] pipeline processing thread took 4.493 secs (4.333 CPU)
248.858716 [dev_pixelpipe] pipeline resync with history took 0.026 secs (0.032 CPU) for pipe full
258.858805 [dev_process_full] pipeline processing thread took 0.000 secs (0.000 CPU)
 110,5815 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
 210,6799 [dev_pixelpipe] took 0,018 secs (0,029 CPU) initing base buffer [full]
 310,6968 [dev_pixelpipe] took 0,017 secs (0,020 CPU) [full] processed `rawprepare' on GPU, blended on GPU
 410,7094 [dev_pixelpipe] took 0,013 secs (0,011 CPU) [full] processed `temperature' on GPU, blended on GPU
 514,4091 [dev_pixelpipe] took 3,700 secs (3,719 CPU) [full] processed `highlights' on GPU with tiling, blended on CPU
 614,8078 [resample_cl] plan 0,000 secs (0,000 CPU) resample 0,010 secs (0,002 CPU)
 714,8988 [dev_pixelpipe] took 0,490 secs (0,349 CPU) [full] processed `demosaic' on GPU, blended on GPU
 814,9094 [dev_pixelpipe] took 0,010 secs (0,001 CPU) [full] processed `lens' on GPU, blended on GPU
 914,9200 [dev_pixelpipe] took 0,010 secs (0,002 CPU) [full] processed `exposure' on GPU, blended on GPU
1014,9516 [dev_pixelpipe] took 0,032 secs (0,010 CPU) [full] processed `colorin' on GPU, blended on GPU
1114,9654 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,013 secs (0,000 GPU) [channelmixerrgb]
1214,9902 [dev_pixelpipe] took 0,039 secs (0,006 CPU) [full] processed `channelmixerrgb' on GPU, blended on GPU
1316,6800 [dev_pixelpipe] took 1,690 secs (1,745 CPU) [full] processed `diffuse' on GPU, blended on GPU
1416,6923 [dev_pixelpipe] took 0,012 secs (0,007 CPU) [full] processed `colorbalancergb' on GPU, blended on GPU
1516,7037 [dev_pixelpipe] took 0,011 secs (0,005 CPU) [full] processed `filmicrgb' on GPU, blended on GPU
1616,7100 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,006 secs (0,000 GPU) [colorout]
1716,7555 [dev_pixelpipe] took 0,052 secs (0,020 CPU) [full] processed `colorout' on GPU, blended on GPU
1816,8435 [dev_pixelpipe] took 0,088 secs (0,052 CPU) [full] processed `dither' on CPU, blended on CPU
1916,8525 [dev_pixelpipe] took 0,009 secs (0,066 CPU) [full] processed `gamma' on CPU, blended on CPU
2016,8594 [dev_process_image] pixel pipeline took 6,197 secs (6,048 CPU) processing `2022-06-17__DSC00078.arw'
21
2216,8918 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
2316,9470 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
2416,9968 [dev_pixelpipe] took 0,000 secs (0,000 CPU) initing base buffer [preview]
2516,9986 [dev_pixelpipe] took 0,002 secs (0,002 CPU) [preview] processed `rawprepare' on GPU, blended on GPU
2616,9995 [dev_pixelpipe] took 0,001 secs (0,001 CPU) [preview] processed `temperature' on GPU, blended on GPU
2717,0698 [dev_pixelpipe] took 0,070 secs (0,081 CPU) [preview] processed `highlights' on GPU, blended on GPU
2817,0811 [dev_pixelpipe] took 0,011 secs (0,005 CPU) [preview] processed `demosaic' on GPU, blended on GPU
2917,0829 [dev_pixelpipe] took 0,002 secs (0,000 CPU) [preview] processed `lens' on GPU, blended on GPU
3017,0853 [dev_pixelpipe] took 0,002 secs (0,001 CPU) [preview] processed `exposure' on GPU, blended on GPU
3117,0886 [dev_pixelpipe] took 0,003 secs (0,001 CPU) [preview] processed `colorin' on GPU, blended on GPU
3217,0915 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,003 secs (0,000 GPU) [channelmixerrgb]
3317,0952 [dev_pixelpipe] took 0,007 secs (0,002 CPU) [preview] processed `channelmixerrgb' on GPU, blended on GPU
3417,4667 [dev_pixelpipe] took 0,371 secs (0,354 CPU) [preview] processed `diffuse' on GPU, blended on GPU
3517,4702 [dev_pixelpipe] took 0,003 secs (0,001 CPU) [preview] processed `colorbalancergb' on GPU, blended on GPU
3617,4748 [dev_pixelpipe] took 0,004 secs (0,001 CPU) [preview] processed `filmicrgb' on GPU, blended on GPU
3717,4776 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,002 secs (0,000 GPU) [colorout]
3817,4939 [dev_pixelpipe] took 0,019 secs (0,009 CPU) [preview] processed `colorout' on GPU, blended on GPU
3917,5141 [dev_pixelpipe] took 0,020 secs (0,007 CPU) [preview] processed `dither' on CPU, blended on CPU
4017,5195 [dev_pixelpipe] took 0,005 secs (0,021 CPU) [preview] processed `gamma' on CPU, blended on CPU
4117,5355 [dt_ioppr_transform_image_colorspace_rgb] `Système' -> `sRVB' took 0,016 secs (0,067 CPU) [final histogram]
4217,6218 [dev_process_image] pixel pipeline took 0,625 secs (0,657 CPU) processing `2022-06-17__DSC00078.arw'
 14.146713 [dev_pixelpipe] took 0.000 secs (0.000 CPU) to load the image.
 24.642451 [dev_pixelpipe] pipeline resync with history took 0.029 secs (0.026 CPU) for pipe virtual-preview
 34.703889 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
 44.703915 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
 54.782635 [dev_pixelpipe] pipeline resync with history took 0.033 secs (0.041 CPU) for pipe preview
 64.796521 [dev_pixelpipe] took 0.014 secs (0.018 CPU) initing base buffer [preview]
 74.831601 [dev_pixelpipe] took 0.035 secs (0.061 CPU) processed `Raw settings' on CPU, blended on  [preview]
 84.862626 [dev_pixelpipe] took 0.031 secs (0.077 CPU) processed `white balance' on CPU, blended on  [preview]
 910.978874 [dev_pixelpipe] took 6.116 secs (43.505 CPU) processed `highlight reconstruction' on CPU, blended on CPU [preview]
1011.369357 [dev_pixelpipe] took 0.390 secs (2.623 CPU) processed `demosaic' on CPU, blended on  [preview]
1111.449953 [dev_pixelpipe] took 0.081 secs (0.474 CPU) processed `lens correction' on CPU, blended on  [preview]
1211.797645 [dev_pixelpipe] took 0.348 secs (2.088 CPU) processed `Initial resampling' on CPU, blended on  [preview]
1311.802788 [dev_pixelpipe] took 0.005 secs (0.025 CPU) processed `exposure' on CPU, blended on CPU [preview]
1411.807751 [dev_pixelpipe] took 0.005 secs (0.024 CPU) processed `input color profile' on CPU, blended on  [preview]
1511.877682 [dev_pixelpipe] took 0.070 secs (0.361 CPU) processed `color calibration' on CPU, blended on CPU [preview]
1615.025427 [dev_pixelpipe] took 3.148 secs (23.610 CPU) processed `diffuse or sharpen' on CPU, blended on CPU [preview]
1715.290600 [dev_pixelpipe] took 0.262 secs (1.469 CPU) processed `color balance rgb' on CPU, blended on CPU [preview]
1815.425400 [dev_pixelpipe] took 0.135 secs (0.761 CPU) processed `filmic rgb' on CPU, blended on CPU [preview]
1915.438259 [dev_pixelpipe] took 0.013 secs (0.072 CPU) processed `output color profile' on CPU, blended on  [preview]
2015.463031 [dev_pixelpipe] took 0.025 secs (0.039 CPU) processed `dithering' on CPU, blended on  [preview]
2115.474803 [dev_pixelpipe] took 0.012 secs (0.058 CPU) processed `display encoding' on CPU, blended on  [preview]
2215.474885 [pixelpipe] preview internal pixel pipeline processing took 10.692 secs (75.269 CPU)
2315.474906 [dev_process_preview] pipeline processing thread took 10.692 secs (75.269 CPU)
2415.522324 [dev_pixelpipe] pipeline resync with history took 0.037 secs (0.047 CPU) for pipe full
2515.522431 [dev_process_full] pipeline processing thread took 0.000 secs (0.000 CPU)
 14,5577 [dt_dev_load_raw] loading the image. took 0,131 secs (0,040 CPU)
 25,4913 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
 35,6087 [dev_pixelpipe] took 0,017 secs (0,029 CPU) initing base buffer [full]
 45,6491 [dev_pixelpipe] took 0,040 secs (0,120 CPU) [full] processed `rawprepare' on CPU, blended on CPU
 55,6782 [dev_pixelpipe] took 0,029 secs (0,061 CPU) [full] processed `temperature' on CPU, blended on CPU
 612,0060 [dev_pixelpipe] took 6,328 secs (45,005 CPU) [full] processed `highlights' on CPU, blended on CPU
 712,6762 [resample_plain] plan 0,001 secs (0,001 CPU) resample 0,249 secs (1,829 CPU)
 812,7230 [dev_pixelpipe] took 0,717 secs (4,499 CPU) [full] processed `demosaic' on CPU, blended on CPU
 912,7370 [dev_pixelpipe] took 0,013 secs (0,029 CPU) [full] processed `lens' on CPU, blended on CPU
1012,7487 [dev_pixelpipe] took 0,012 secs (0,029 CPU) [full] processed `exposure' on CPU, blended on CPU
1112,7637 [dev_pixelpipe] took 0,015 secs (0,045 CPU) [full] processed `colorin' on CPU, blended on CPU
1212,7693 [dt_ioppr_transform_image_colorspace] IOP_CS_LAB-->IOP_CS_RGB took 0,006 secs (0,028 CPU) [channelmixerrgb]
1312,8473 [dev_pixelpipe] took 0,084 secs (0,438 CPU) [full] processed `channelmixerrgb' on CPU, blended on CPU
1417,0570 [dev_pixelpipe] took 4,210 secs (32,406 CPU) [full] processed `diffuse' on CPU, blended on CPU
1517,3181 [dev_pixelpipe] took 0,261 secs (1,872 CPU) [full] processed `colorbalancergb' on CPU, blended on CPU
1617,4583 [dev_pixelpipe] took 0,140 secs (0,898 CPU) [full] processed `filmicrgb' on CPU, blended on CPU
1717,4634 [dt_ioppr_transform_image_colorspace] IOP_CS_RGB-->IOP_CS_LAB took 0,005 secs (0,033 CPU) [colorout]
1817,4792 [dev_pixelpipe] took 0,021 secs (0,116 CPU) [full] processed `colorout' on CPU, blended on CPU
1917,5381 [dev_pixelpipe] took 0,059 secs (0,037 CPU) [full] processed `dither' on CPU, blended on CPU
2017,5472 [dev_pixelpipe] took 0,009 secs (0,061 CPU) [full] processed `gamma' on CPU, blended on CPU
2117,5543 [dev_process_image] pixel pipeline took 11,962 secs (85,649 CPU) processing `2022-06-17__DSC00078.arw'
22
2317,5861 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
2417,6428 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
2517,6993 [dev_pixelpipe] took 0,000 secs (0,000 CPU) initing base buffer [preview]
2617,7004 [dev_pixelpipe] took 0,001 secs (0,002 CPU) [preview] processed `rawprepare' on CPU, blended on CPU
2717,7015 [dev_pixelpipe] took 0,001 secs (0,004 CPU) [preview] processed `temperature' on CPU, blended on CPU
2817,8101 [dev_pixelpipe] took 0,109 secs (0,570 CPU) [preview] processed `highlights' on CPU, blended on CPU
2917,8261 [dev_pixelpipe] took 0,016 secs (0,071 CPU) [preview] processed `demosaic' on CPU, blended on CPU
3017,8308 [dev_pixelpipe] took 0,005 secs (0,006 CPU) [preview] processed `lens' on CPU, blended on CPU
3117,8345 [dev_pixelpipe] took 0,004 secs (0,006 CPU) [preview] processed `exposure' on CPU, blended on CPU
3217,8393 [dev_pixelpipe] took 0,005 secs (0,016 CPU) [preview] processed `colorin' on CPU, blended on CPU
3317,8411 [dt_ioppr_transform_image_colorspace] IOP_CS_LAB-->IOP_CS_RGB took 0,002 secs (0,008 CPU) [channelmixerrgb]
3417,8670 [dev_pixelpipe] took 0,028 secs (0,144 CPU) [preview] processed `channelmixerrgb' on CPU, blended on CPU
3519,0763 [dev_pixelpipe] took 1,209 secs (8,091 CPU) [preview] processed `diffuse' on CPU, blended on CPU
3619,1926 [dev_pixelpipe] took 0,116 secs (0,708 CPU) [preview] processed `colorbalancergb' on CPU, blended on CPU
3719,2628 [dev_pixelpipe] took 0,070 secs (0,342 CPU) [preview] processed `filmicrgb' on CPU, blended on CPU
3819,2667 [dt_ioppr_transform_image_colorspace] IOP_CS_RGB-->IOP_CS_LAB took 0,003 secs (0,012 CPU) [colorout]
3919,2758 [dev_pixelpipe] took 0,012 secs (0,045 CPU) [preview] processed `colorout' on CPU, blended on CPU
4019,2860 [dev_pixelpipe] took 0,010 secs (0,003 CPU) [preview] processed `dither' on CPU, blended on CPU
4119,2907 [dev_pixelpipe] took 0,005 secs (0,020 CPU) [preview] processed `gamma' on CPU, blended on CPU
4219,3052 [dt_ioppr_transform_image_colorspace_rgb] `Système' -> `sRVB' took 0,014 secs (0,071 CPU) [final histogram]
4319,3928 [dev_process_image] pixel pipeline took 1,694 secs (10,211 CPU) processing `2022-06-17__DSC00078.arw'

Config 2

 136.764111 [dev_pixelpipe] took 0.313 secs (0.056 CPU) to load the image.
 237.528673 [dev_pixelpipe] pipeline resync with history took 0.044 secs (0.040 CPU) for pipe virtual-preview
 337.691646 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
 437.691699 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
 537.745145 [dev_pixelpipe] pipeline resync with history took 0.036 secs (0.043 CPU) for pipe preview
 637.916228 [dev_pixelpipe] took 0.171 secs (0.115 CPU) initing base buffer [preview]
 737.953157 [dev_pixelpipe] took 0.037 secs (0.024 CPU) processed `Raw settings' on GPU, blended on  [preview]
 837.959823 [dev_pixelpipe] took 0.007 secs (0.002 CPU) processed `white balance' on GPU, blended on  [preview]
 939.377680 [dev_pixelpipe] took 1.418 secs (0.196 CPU) processed `highlight reconstruction' on GPU, blended on GPU [preview]
1039.526422 [dev_pixelpipe] took 0.149 secs (0.039 CPU) processed `demosaic' on GPU, blended on  [preview]
1141.012847 [dev_pixelpipe] took 1.486 secs (0.271 CPU) processed `lens correction' on GPU, blended on  [preview]
1241.266624 [dev_pixelpipe] took 0.254 secs (0.110 CPU) processed `Initial resampling' on GPU, blended on  [preview]
1341.299983 [dev_pixelpipe] took 0.033 secs (0.025 CPU) processed `exposure' on GPU, blended on GPU [preview]
1441.311230 [dev_pixelpipe] took 0.011 secs (0.009 CPU) processed `input color profile' on GPU, blended on  [preview]
1541.313526 [dev_pixelpipe] took 0.002 secs (0.002 CPU) processed `color calibration' on GPU, blended on GPU [preview]
1641.471857 [dev_pixelpipe] took 0.158 secs (0.049 CPU) processed `diffuse or sharpen' on GPU, blended on GPU [preview]
1741.482377 [dev_pixelpipe] took 0.010 secs (0.008 CPU) processed `color balance rgb' on GPU, blended on GPU [preview]
1841.486457 [dev_pixelpipe] took 0.004 secs (0.007 CPU) processed `filmic rgb' on GPU, blended on GPU [preview]
1941.508325 [dev_pixelpipe] took 0.022 secs (0.018 CPU) processed `output color profile' on GPU, blended on  [preview]
2041.521171 [dev_pixelpipe] took 0.013 secs (0.016 CPU) processed `dithering' on CPU, blended on  [preview]
2141.529057 [dev_pixelpipe] took 0.008 secs (0.014 CPU) processed `display encoding' on CPU, blended on  [preview]
2241.529178 [pixelpipe] preview internal pixel pipeline processing took 3.784 secs (0.906 CPU)
2341.529291 [dev_process_preview] pipeline processing thread took 3.784 secs (0.906 CPU)
2441.576629 [dev_pixelpipe] pipeline resync with history took 0.036 secs (0.049 CPU) for pipe full
2541.579279 [dev_process_full] pipeline processing thread took 0.000 secs (0.000 CPU)
 123.6180 [dt_dev_load_raw] loading the image. took 0.290 secs (0.055 CPU)
 224.6549 [dt_dev_process_image_job] loading image. took 0.000 secs (0.000 CPU)
 324.7621 [dev_pixelpipe] took 0.023 secs (0.034 CPU) initing base buffer [full]
 424.8212 [dev_pixelpipe] took 0.059 secs (0.022 CPU) [full] processed `rawprepare' on GPU, blended on GPU
 524.8286 [dev_pixelpipe] took 0.007 secs (0.000 CPU) [full] processed `temperature' on GPU, blended on GPU
 626.6983 [dev_pixelpipe] took 1.870 secs (0.433 CPU) [full] processed `highlights' on GPU, blended on GPU
 726.7416 [resample_cl] plan 0.002 secs (0.001 CPU) resample 0.032 secs (0.000 CPU)
 826.8215 [dev_pixelpipe] took 0.123 secs (0.009 CPU) [full] processed `demosaic' on GPU, blended on GPU
 926.8260 [dev_pixelpipe] took 0.004 secs (0.001 CPU) [full] processed `lens' on GPU, blended on GPU
1026.8282 [dev_pixelpipe] took 0.002 secs (0.001 CPU) [full] processed `ashift' on GPU, blended on GPU
1126.8306 [dev_pixelpipe] took 0.002 secs (0.001 CPU) [full] processed `exposure' on GPU, blended on GPU
1226.8347 [dev_pixelpipe] took 0.004 secs (0.001 CPU) [full] processed `colorin' on GPU, blended on GPU
1326.8370 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0.002 secs (0.001 GPU) [channelmixerrgb]
1426.8384 [dev_pixelpipe] took 0.004 secs (0.001 CPU) [full] processed `channelmixerrgb' on GPU, blended on GPU
1527.3567 [dev_pixelpipe] took 0.518 secs (0.022 CPU) [full] processed `diffuse' on GPU, blended on GPU
1627.3613 [dev_pixelpipe] took 0.004 secs (0.001 CPU) [full] processed `colorbalancergb' on GPU, blended on GPU
1727.4019 [dev_pixelpipe] took 0.041 secs (0.011 CPU) [full] processed `filmicrgb' on GPU, blended on GPU
1827.4053 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0.003 secs (0.001 GPU) [colorout]
1927.4345 [dev_pixelpipe] took 0.032 secs (0.009 CPU) [full] processed `colorout' on GPU, blended on GPU
2027.4814 [dev_pixelpipe] took 0.047 secs (0.025 CPU) [full] processed `dither' on CPU, blended on CPU
2127.4962 [dev_pixelpipe] took 0.015 secs (0.045 CPU) [full] processed `gamma' on CPU, blended on CPU
2227.4990 [dev_process_image] pixel pipeline took 2.760 secs (0.618 CPU) processing `2022-06-17__DSC00078.arw'
23
2427.5249 [dt_dev_process_image_job] loading image. took 0.005 secs (0.013 CPU)
2527.5866 [dt_dev_process_image_job] loading image. took 0.000 secs (0.000 CPU)
2627.6513 [dev_pixelpipe] took 0.000 secs (0.000 CPU) initing base buffer [preview]
2727.6561 [dev_pixelpipe] took 0.005 secs (0.006 CPU) [preview] processed `rawprepare' on GPU, blended on GPU
2827.6580 [dev_pixelpipe] took 0.002 secs (0.001 CPU) [preview] processed `temperature' on GPU, blended on GPU
2927.7797 [dev_pixelpipe] took 0.122 secs (0.062 CPU) [preview] processed `highlights' on GPU, blended on GPU
3027.7845 [dev_pixelpipe] took 0.005 secs (0.002 CPU) [preview] processed `demosaic' on GPU, blended on GPU
3127.7854 [dev_pixelpipe] took 0.001 secs (0.000 CPU) [preview] processed `lens' on GPU, blended on GPU
3227.8023 [dev_pixelpipe] took 0.017 secs (0.002 CPU) [preview] processed `ashift' on GPU, blended on GPU
3327.8034 [dev_pixelpipe] took 0.001 secs (0.000 CPU) [preview] processed `exposure' on GPU, blended on GPU
3427.8058 [dev_pixelpipe] took 0.002 secs (0.000 CPU) [preview] processed `colorin' on GPU, blended on GPU
3527.8077 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0.001 secs (0.000 GPU) [channelmixerrgb]
3627.8089 [dev_pixelpipe] took 0.003 secs (0.000 CPU) [preview] processed `channelmixerrgb' on GPU, blended on GPU
3728.5157 [dev_pixelpipe] took 0.707 secs (0.076 CPU) [preview] processed `diffuse' on GPU, blended on GPU
3828.5240 [dev_pixelpipe] took 0.008 secs (0.004 CPU) [preview] processed `colorbalancergb' on GPU, blended on GPU
3928.6037 [dev_pixelpipe] took 0.079 secs (0.031 CPU) [preview] processed `filmicrgb' on GPU, blended on GPU
4028.6092 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0.005 secs (0.000 GPU) [colorout]
4128.6323 [dev_pixelpipe] took 0.028 secs (0.007 CPU) [preview] processed `colorout' on GPU, blended on GPU
4228.6675 [dev_pixelpipe] took 0.035 secs (0.010 CPU) [preview] processed `dither' on CPU, blended on CPU
4328.6881 [dev_pixelpipe] took 0.021 secs (0.066 CPU) [preview] processed `gamma' on CPU, blended on CPU
4428.8183 [dev_process_image] pixel pipeline took 1.167 secs (0.442 CPU) processing `2022-06-17__DSC00078.arw'
 113.969925 [dev_pixelpipe] took 0.271 secs (0.044 CPU) to load the image.
 214.519352 [dev_pixelpipe] pipeline resync with history took 0.039 secs (0.037 CPU) for pipe virtual-preview
 314.609258 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
 414.609291 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
 514.685917 [dev_pixelpipe] pipeline resync with history took 0.036 secs (0.048 CPU) for pipe preview
 614.724434 [dev_pixelpipe] took 0.038 secs (0.024 CPU) initing base buffer [preview]
 714.787570 [dev_pixelpipe] took 0.063 secs (0.064 CPU) processed `Raw settings' on CPU, blended on  [preview]
 814.857062 [dev_pixelpipe] took 0.069 secs (0.080 CPU) processed `white balance' on CPU, blended on  [preview]
 927.961302 [dev_pixelpipe] took 13.104 secs (32.983 CPU) processed `highlight reconstruction' on CPU, blended on CPU [preview]
1028.949286 [dev_pixelpipe] took 0.988 secs (3.057 CPU) processed `demosaic' on CPU, blended on  [preview]
1129.075925 [dev_pixelpipe] took 0.127 secs (0.326 CPU) processed `lens correction' on CPU, blended on  [preview]
1230.176998 [dev_pixelpipe] took 1.101 secs (3.061 CPU) processed `Initial resampling' on CPU, blended on  [preview]
1330.190589 [dev_pixelpipe] took 0.013 secs (0.024 CPU) processed `exposure' on CPU, blended on CPU [preview]
1430.192681 [dev_pixelpipe] took 0.002 secs (0.005 CPU) processed `input color profile' on CPU, blended on  [preview]
1530.239877 [dev_pixelpipe] took 0.047 secs (0.103 CPU) processed `color calibration' on CPU, blended on CPU [preview]
1631.944712 [dev_pixelpipe] took 1.705 secs (4.286 CPU) processed `diffuse or sharpen' on CPU, blended on CPU [preview]
1732.096771 [dev_pixelpipe] took 0.152 secs (0.407 CPU) processed `color balance rgb' on CPU, blended on CPU [preview]
1832.190015 [dev_pixelpipe] took 0.093 secs (0.228 CPU) processed `filmic rgb' on CPU, blended on CPU [preview]
1932.205505 [dev_pixelpipe] took 0.015 secs (0.028 CPU) processed `output color profile' on CPU, blended on  [preview]
2032.214424 [dev_pixelpipe] took 0.009 secs (0.011 CPU) processed `dithering' on CPU, blended on  [preview]
2132.223982 [dev_pixelpipe] took 0.009 secs (0.021 CPU) processed `display encoding' on CPU, blended on  [preview]
2232.224049 [pixelpipe] preview internal pixel pipeline processing took 17.538 secs (44.709 CPU)
2332.224090 [dev_process_preview] pipeline processing thread took 17.538 secs (44.710 CPU)
2432.276027 [dev_pixelpipe] pipeline resync with history took 0.042 secs (0.054 CPU) for pipe full
2532.276152 [dev_process_full] pipeline processing thread took 0.000 secs (0.000 CPU)
 112.5778 [dt_dev_process_image_job] loading image. took 0.000 secs (0.000 CPU)
 212.6692 [dt_dev_process_image_job] loading image. took 0.000 secs (0.000 CPU)
 312.7607 [dev_pixelpipe] took 0.018 secs (0.022 CPU) initing base buffer [full]
 412.8098 [dev_pixelpipe] took 0.049 secs (0.105 CPU) [full] processed `rawprepare' on CPU
 512.8448 [dev_pixelpipe] took 0.035 secs (0.042 CPU) [full] processed `temperature' on CPU
 625.7535 [dev_pixelpipe] took 12.909 secs (44.992 CPU) [full] processed `highlights' on CPU
 727.5129 [resample_plain] plan 0.001 secs (0.001 CPU) resample 0.732 secs (2.749 CPU)
 827.5515 [dev_pixelpipe] took 1.798 secs (6.012 CPU) [full] processed `demosaic' on CPU
 927.5571 [dev_pixelpipe] took 0.006 secs (0.005 CPU) [full] processed `lens' on CPU
1027.5603 [dev_pixelpipe] took 0.003 secs (0.003 CPU) [full] processed `ashift' on CPU
1127.5634 [dev_pixelpipe] took 0.003 secs (0.005 CPU) [full] processed `exposure' on CPU
1227.5963 [dev_pixelpipe] took 0.033 secs (0.100 CPU) [full] processed `colorin' on CPU
1327.6038 [dt_ioppr_transform_image_colorspace] IOP_CS_LAB-->IOP_CS_RGB took 0.007 secs (0.018 CPU) [channelmixerrgb]
1427.6464 [dev_pixelpipe] took 0.050 secs (0.163 CPU) [full] processed `channelmixerrgb' on CPU
1531.0132 [dev_pixelpipe] took 3.367 secs (12.611 CPU) [full] processed `diffuse' on CPU
1631.1921 [dev_pixelpipe] took 0.179 secs (0.647 CPU) [full] processed `colorbalancergb' on CPU
1731.3065 [dev_pixelpipe] took 0.114 secs (0.406 CPU) [full] processed `filmicrgb' on CPU
1831.3321 [dt_ioppr_transform_image_colorspace] IOP_CS_RGB-->IOP_CS_LAB took 0.025 secs (0.091 CPU) [colorout]
1931.3475 [dev_pixelpipe] took 0.041 secs (0.124 CPU) [full] processed `colorout' on CPU
2031.3754 [dev_pixelpipe] took 0.028 secs (0.020 CPU) [full] processed `dither' on CPU
2131.3849 [dev_pixelpipe] took 0.009 secs (0.034 CPU) [full] processed `gamma' on CPU
2231.3870 [dev_process_image] pixel pipeline took 18.644 secs (65.292 CPU) processing `2022-06-17__DSC00078.arw'
23
2431.4040 [dt_dev_process_image_job] loading image. took 0.000 secs (0.000 CPU)
2531.5018 [dt_dev_process_image_job] loading image. took 0.000 secs (0.000 CPU)
2631.5722 [dev_pixelpipe] took 0.000 secs (0.000 CPU) initing base buffer [preview]
2731.5824 [dev_pixelpipe] took 0.010 secs (0.014 CPU) [preview] processed `rawprepare' on CPU
2831.5861 [dev_pixelpipe] took 0.004 secs (0.008 CPU) [preview] processed `temperature' on CPU
2932.7796 [dev_pixelpipe] took 1.193 secs (4.129 CPU) [preview] processed `highlights' on CPU
3032.8917 [dev_pixelpipe] took 0.112 secs (0.387 CPU) [preview] processed `demosaic' on CPU
3132.9099 [dev_pixelpipe] took 0.018 secs (0.031 CPU) [preview] processed `lens' on CPU
3232.9433 [dev_pixelpipe] took 0.033 secs (0.050 CPU) [preview] processed `ashift' on CPU
3332.9610 [dev_pixelpipe] took 0.018 secs (0.023 CPU) [preview] processed `exposure' on CPU
3433.1087 [dev_pixelpipe] took 0.148 secs (0.532 CPU) [preview] processed `colorin' on CPU
3533.1362 [dt_ioppr_transform_image_colorspace] IOP_CS_LAB-->IOP_CS_RGB took 0.027 secs (0.096 CPU) [channelmixerrgb]
3633.3353 [dev_pixelpipe] took 0.227 secs (0.825 CPU) [preview] processed `channelmixerrgb' on CPU
3755.6386 [dev_pixelpipe] took 22.303 secs (82.427 CPU) [preview] processed `diffuse' on CPU
3856.4763 [dev_pixelpipe] took 0.837 secs (3.180 CPU) [preview] processed `colorbalancergb' on CPU
3957.0002 [dev_pixelpipe] took 0.523 secs (1.966 CPU) [preview] processed `filmicrgb' on CPU
4057.1225 [dt_ioppr_transform_image_colorspace] IOP_CS_RGB-->IOP_CS_LAB took 0.122 secs (0.437 CPU) [colorout]
4157.1852 [dev_pixelpipe] took 0.184 secs (0.637 CPU) [preview] processed `colorout' on CPU
4257.2043 [dev_pixelpipe] took 0.019 secs (0.018 CPU) [preview] processed `dither' on CPU
4357.2471 [dev_pixelpipe] took 0.043 secs (0.155 CPU) [preview] processed `gamma' on CPU
4457.4090 [dev_process_image] pixel pipeline took 25.837 secs (94.647 CPU) processing `2022-06-17__DSC00078.arw'

Rapporto

Prima di tutto, quando si entra nella camera oscura, Darktable calcola 2 pipeline per lo stesso risultato di Ansel, che ne calcola solo una. Quindi, indipendentemente dai runtime effettivi, già di per sé si tratta della metà di calcoli inutili.

Poi, Ansel calcola l’immagine a piena risoluzione fino al modulo ricampionamento iniziale, mentre Darktable lo fa solo fino a prima della demosaicizzazione. Questo è importante per il confronto.

Config 1 (Intel Xeon, Nvidia Quadro)

SoftwareGPUCPU
Ansel4.493 s10.692 s
Darktable 5.4.16.822 s13.66 s

Config 2 (Intel i3, AMD Radeon)

SoftwareGPUCPU
Ansel3.784 s17.538 s
Darktable 5.4.13.927 s44.481 s

Sulla config 1, Darktable riesce a essere più lento di Ansel su GPU anche guardando solo il rendering della singola pipeline principale e persino ridimensionando l’immagine alla risoluzione del display più presto nella pipeline. Ma vediamo effettivamente lo stesso effetto su CPU, nonostante tutte le presunte «ottimizzazioni» fatte sui moduli solo-CPU intorno al 2023-2024 in Darktable. Complessivamente, Darktable congelerà il tuo computer per 2,3-3,0 s in più.

Sulla config 2, il percorso CPU mostra una penalità di 0,8 s per Ansel solo sulla pipeline principale. La GPU AMD mostra un brutto colpo sul modulo correzione dell’obiettivo, che elabora a piena risoluzione su Ansel mentre elabora alla risoluzione dello schermo in Darktable. Tenendo conto della seconda pipeline che Darktable esegue, Ansel è più veloce di 0,14 s su GPU. Su CPU però, Darktable ora congela la macchina per 27,07 s in più a causa di quel lavoro di pipeline duplicato. In ogni caso, i moduli lenti sono all’inizio della pipeline e non verranno ricalcolati per l’intera sessione.

Questo ci fornisce i seguenti confronti affiancati tra moduli. Confrontiamo solo i moduli che girano alla stessa risoluzione per la sola pipeline dell’immagine principale. Nella scheda delle medie, i fattori sono mediati con una media geometrica e le differenze relative con una media armonica:

Modulo / chipAnsel MasterDarktable 5.4.1Accelerazione Ansel
Raw settings* / CPU35 ms40 ms×1.14 / +13 %
Raw settings* / GPU18 ms17 ms×0.96 / -5.9 %
White balance* / CPU31 ms29 ms×0.94 / -6.9 %
White balance* / GPU16 ms13 ms×0.81 / -23 %
Highlights reconstruction* / CPU6.12 s6.33 s×1.03 / +3.4 %
Highlights reconstruction* / GPU2.49 s3.70 s×1.49 / +33 %
Demosaic* / CPU348 ms717 ms×2.06 / +52 %
Demosaic* / GPU254 ms490 ms×1.93 / +48 %
Exposure / CPU5 ms12 ms×2.4 / +58 %
Exposure / GPU6 ms10 ms×1.67 / +40 %
Input color profile / CPU3 ms15 ms×3 / +67 %
Input color profile / GPU2 ms32 ms×16 / +94 %
Color calibration / CPU70 ms84 ms×1.20 / +25 %
Color calibration / GPU1 ms39 ms×39 / +97 %
Diffuse or sharpen / CPU3.15 s4.21 s×1.34 / +25.2 %
Diffuse or sharpen / GPU1.09 s1.69 s×1.55 / +35 %
Color balance RGB / CPU262 ms261 ms×1.00 / -0.4 %
Color balance RGB / GPU9 ms12 ms×1.33 / +25 %
Filmic RGB / CPU137 ms140 ms×1.02 / +2.1 %
Filmic RGB / GPU6 ms11 ms×1.83 / +45 %
Output color profile / CPU13 ms21 ms×1.62 / +38 %
Output color profile / GPU23 ms52 ms×2.26 / +56 %
Dithering (CPU only) / CPU pipeline24 ms59 ms×2.46 / +59 %
Dithering (CPU only) / GPU pipeline43 ms88 ms×2.05 / +51 %
Gamma / display encoding (CPU only) / GPU pipeline12 ms9 ms×0.75 / -33 %
Gamma / display encoding (CPU only) / CPU pipeline12 ms9 ms×0.75 / -33 %

*: moduli che girano a piena risoluzione su entrambe le applicazioni.

Modulo / chipAnsel MasterDarktable 5.4.1Accelerazione Ansel
Raw settings* / CPU63 ms49 ms×0.78 / -29 %
Raw settings* / GPU37 ms59 ms×1.59 / +37 %
White balance* / CPU69 ms35 ms×0.5 / -97 %
White balance* / GPU7 ms7 ms×1 / 0 %
Highlights reconstruction* / CPU13.10 s12.91 s×0.99 / -1.5 %
Highlights reconstruction* / GPU1.42 s1.87 s×1.32 / +24 %
Demosaic* / CPU0.99 s1.80 s×1.82 / +45 %
Demosaic* / GPU149 ms123 ms×0.83 / -21 %
Exposure / CPU13 ms3 ms×0.23 / -333 %
Exposure / GPU33 ms2 ms×0.06 / -1550 %
Input color profile / CPU2 ms33 ms×16.5 / +94 %
Input color profile / GPU11 ms4 ms×0.36 / -175 %
Color calibration / CPU47 ms50 ms×1.06 / 6 %
Color calibration / GPU2 ms4 ms×2 / +50 %
Diffuse or sharpen / CPU1.71 s3.37 s×1.97 / +49.4 %
Diffuse or sharpen / GPU158 ms518 ms×3.28 / +70 %
Color balance RGB / CPU152 ms179 ms×1.18 / +15 %
Color balance RGB / GPU10 ms4 ms×0.40 / -150 %
Filmic RGB / CPU93 ms114 ms×1.23 / +18 %
Filmic RGB / GPU4 ms41 ms×10.26 / +90 %
Output color profile / CPU15 ms41 ms×2.73 / +63 %
Output color profile / GPU22 ms32 ms×1.45 / +31 %
Dithering (CPU only) / CPU pipeline9 ms28 ms×3.62 / +72 %
Dithering (CPU only) / GPU pipeline13 ms47 ms×2.76 / +64 %
Gamma / display encoding (CPU only) / CPU pipeline9 ms9 ms×1.0 / 0 %
Gamma / display encoding (CPU only) / GPU pipeline8 ms15 ms×1.88 / +47 %

*: moduli che girano a piena risoluzione su entrambe le applicazioni.

Media sulle config 1 e 2. I fattori sono mediati con una media geometrica, e le differenze relative con una media armonica.

Modulo / chipAccelerazione AnselCodice Ansel
Raw settings* / CPU×0.94Custom (vettorizzato manualmente)
Raw settings* / GPU×1.23Uguale a DT 5.4.1
White balance* / CPU×0.69Custom (vettorizzato manualmente)
White balance* / GPU×0.97Uguale a DT 5.4.1
Highlights reconstruction* / CPU×1.1Custom (vettorizzato manualmente)
Highlights reconstruction* / GPU×1.4Custom
Demosaic* / CPU×1.94Uguale a DT 4.0
Demosaic* / GPU×1.26Custom
Exposure / CPU×0.74Custom (vettorizzato manualmente)
Exposure / GPU×0.32Uguale a DT 4.0
Input color profile / CPU×7.04Custom (vettorizzato manualmente)
Input color profile / GPU×2.41Custom
Color calibration / CPU×1.13Custom (vettorizzato manualmente)
Color calibration / GPU×8.83Uguale a DT 4.0
Diffuse or sharpen / CPU×1.08Custom
Diffuse or sharpen / GPU×2.26Custom
Color balance RGB / CPU×1.08Custom (vettorizzato manualmente)
Color balance RGB / GPU×0.73Custom
Filmic RGB / CPU×1.13Custom (vettorizzato manualmente)
Filmic RGB / GPU×4.33Custom
Output color profile / CPU×2.10Custom (vettorizzato manualmente)
Output color profile / GPU×1.81Custom
Dithering (CPU only) / CPU pipeline×2.71Uguale a DT 4.0
Dithering (CPU only) / GPU pipeline×2.72Uguale a DT 4.0
Gamma / display encoding (CPU only) / CPU pipeline×0.87Uguale a DT 5.4.1
Gamma / display encoding (CPU only) / GPU pipeline×1.63Uguale a DT 5.4.1

*: moduli che girano a piena risoluzione su entrambe le applicazioni.

Velocità relativa per modulo (GPU). Più alto è meglio.

Ansel mostra miglioramenti delle prestazioni GPU anche su kernel che non sono cambiati dai tempi di Darktable 4.0. Le accelerazioni che vediamo sono chiaramente il beneficio della gestione della memoria di alto livello, usando la memoria pinned di OpenCL o semplici buffer di memoria solo-device dove pertinente.

Su tutti i moduli, Ansel è 1,6 volte più veloce di Darktable su GPU (media geometrica).

Velocità relativa per modulo (CPU). Più alto è meglio.

Su CPU, le cose sono più sfumate.

Darktable 5.x dovrebbe aver ricevuto ottimizzazioni CPU per il dithering e la demosaicizzazione (RCD), eppure entrambi quei moduli hanno prestazioni peggiori di quelli di Ansel su entrambe le config.

Ansel ha codice pixel CPU vettorizzato manualmente per color balance RGB, color calibration, input/output color profile, raw settings, white balance, exposure e filmic, mentre quello di Darktable usa suggerimenti al compilatore per ottenere la vettorizzazione. Tutti hanno prestazioni migliori di quelli di Darktable sull’Intel Xeon. Sull’Intel i3, per il lavoro di base sulle matrici colore, Ansel ha prestazioni (molto) migliori, ma sta leggermente penalizzando i moduli di base come raw settings, exposure o white balance.

Diffuse or sharpen dovrebbe usare ancora lo stesso codice di Darktable 4.0 su Ansel e su Darktable 5.4, ma Ansel è più veloce su entrambe le config, il che mostra il beneficio dell’usare il nostro array di memoria pre-allocato per la cache, e di allocare i buffer di memoria al di fuori dei circuiti del sistema operativo. Questo dimostra che ottimizzare a livello di modulo è l’ambito sbagliato, e che la pipeline globale aveva prima bisogno di una revisione.

Ma è ovviamente più facile affrontare le ottimizzazioni a livello di modulo perché i moduli sono… modulari, cioè autoconclusi e autosufficienti. C’è poca probabilità di rovinare il software nel suo insieme quando si lavora sui moduli perché sono isolati dal resto. I cambiamenti dell’architettura globale dell’applicazione, d’altro canto, sono pericolosi, complicati ed esigenti. E impongono di fermare del tutto lo sviluppo di qualsiasi nuovo modulo mentre l’architettura si sta stabilizzando. Cosa che Darktable non avrà mai la disciplina di fare, perché il progetto è non gestito e pull request casuali spuntano su Github in momenti casuali da collaboratori casuali.

Su tutti i moduli, Ansel è 1,8 volte più veloce di Darktable su CPU (media geometrica).

Combinando i percorsi GPU e CPU su entrambe le config, aprire la camera oscura è 1,5 volte più veloce su Ansel (media geometrica).

Zoom al 100%

In camera oscura, dalla dimensione «zoom adatta», zooma al 100% con un solo clic usando il pulsante centrale del mouse

 112.083270 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
 212.111607 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe full
 312.177976 [dev_pixelpipe] took 0.066 secs (0.095 CPU) processed `Initial resampling' on GPU, blended on  [full]
 412.179976 [dev_pixelpipe] took 0.002 secs (0.002 CPU) processed `exposure' on GPU, blended on GPU [full]
 512.183194 [dev_pixelpipe] took 0.003 secs (0.005 CPU) processed `input color profile' on GPU, blended on  [full]
 612.185327 [dev_pixelpipe] took 0.002 secs (0.002 CPU) processed `color calibration' on GPU, blended on GPU [full]
 714.887725 [dev_pixelpipe] took 2.702 secs (2.705 CPU) processed `diffuse or sharpen' on GPU, blended on GPU [full]
 814.899213 [dev_pixelpipe] took 0.011 secs (0.013 CPU) processed `color balance rgb' on GPU, blended on GPU [full]
 914.907159 [dev_pixelpipe] took 0.008 secs (0.008 CPU) processed `filmic rgb' on GPU, blended on GPU [full]
1014.940617 [dev_pixelpipe] took 0.033 secs (0.039 CPU) processed `output color profile' on GPU, blended on  [full]
1115.005967 [dev_pixelpipe] took 0.065 secs (0.057 CPU) processed `dithering' on CPU, blended on  [full]
1215.030966 [dev_pixelpipe] took 0.025 secs (0.036 CPU) processed `display encoding' on GPU, blended on  [full]
1315.031021 [pixelpipe] full internal pixel pipeline processing took 2.919 secs (2.963 CPU)
1415.031080 [dev_process_full] pipeline processing thread took 2.919 secs (2.963 CPU)
 189,3415 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
 289,3440 [dev_pixelpipe] took 0,002 secs (0,001 CPU) initing base buffer [full]
 389,3544 [dev_pixelpipe] took 0,010 secs (0,008 CPU) [full] processed `rawprepare' on GPU, blended on GPU
 489,3573 [dev_pixelpipe] took 0,003 secs (0,001 CPU) [full] processed `temperature' on GPU, blended on GPU
 589,8611 [dev_pixelpipe] took 0,504 secs (0,511 CPU) [full] processed `highlights' on GPU, blended on GPU
 689,9200 [dev_pixelpipe] took 0,059 secs (0,023 CPU) [full] processed `demosaic' on GPU, blended on GPU
 789,9251 [dev_pixelpipe] took 0,005 secs (0,003 CPU) [full] processed `lens' on GPU, blended on GPU
 889,9346 [dev_pixelpipe] took 0,009 secs (0,003 CPU) [full] processed `exposure' on GPU, blended on GPU
 989,9480 [dev_pixelpipe] took 0,013 secs (0,003 CPU) [full] processed `colorin' on GPU, blended on GPU
1089,9576 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,009 secs (0,002 GPU) [channelmixerrgb]
1189,9736 [dev_pixelpipe] took 0,026 secs (0,008 CPU) [full] processed `channelmixerrgb' on GPU, blended on GPU
1294,9286 [dev_pixelpipe] took 4,955 secs (5,096 CPU) [full] processed `diffuse' on GPU, blended on GPU
1394,9528 [dev_pixelpipe] took 0,024 secs (0,007 CPU) [full] processed `colorbalancergb' on GPU, blended on GPU
1494,9836 [dev_pixelpipe] took 0,031 secs (0,009 CPU) [full] processed `filmicrgb' on GPU, blended on GPU
1594,9925 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,009 secs (0,000 GPU) [colorout]
1695,0700 [dev_pixelpipe] took 0,086 secs (0,028 CPU) [full] processed `colorout' on GPU, blended on GPU
1795,2184 [dev_pixelpipe] took 0,148 secs (0,093 CPU) [full] processed `dither' on CPU, blended on CPU
1895,2404 [dev_pixelpipe] took 0,022 secs (0,104 CPU) [full] processed `gamma' on CPU, blended on CPU
1995,2537 [dev_process_image] pixel pipeline took 5,912 secs (5,904 CPU) processing `2022-06-17__DSC00078.arw'
2095,2634 [dev_process_image] pixel pipeline took 0,010 secs (0,004 CPU) processing `2022-06-17__DSC00078.arw'

Poiché Ansel elabora le fasi iniziali della pipeline a piena risoluzione, esse vengono anche memorizzate in cache a piena risoluzione, il che significa che la panoramica e lo zoom recuperano la cache a monte del modulo ricampionamento iniziale, e ricalcolano solo a valle. Quindi impiega 2,9 s per il rendering. Darktable, d’altro canto, ha una cache non funzionante, quindi ricalcola tutto dall’inizio, impiegando 5,9 s per rendere la stessa cosa. È una penalità prestazionale di ×2 per Darktable.

Panoramica al 100%

 155.456510 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe full
 255.536149 [dev_pixelpipe] took 0.080 secs (0.121 CPU) processed `Initial resampling' on GPU, blended on  [full]
 355.541720 [dev_pixelpipe] took 0.005 secs (0.003 CPU) processed `exposure' on GPU, recycled cacheline, blended on GPU [full]
 455.551346 [dev_pixelpipe] took 0.009 secs (0.006 CPU) processed `input color profile' on GPU, recycled cacheline, blended on  [full]
 555.559669 [dev_pixelpipe] took 0.008 secs (0.005 CPU) processed `color calibration' on GPU, recycled cacheline, blended on GPU [full]
 658.290943 [dev_pixelpipe] took 2.731 secs (2.819 CPU) processed `diffuse or sharpen' on GPU, blended on GPU [full]
 758.310400 [dev_pixelpipe] took 0.016 secs (0.017 CPU) processed `color balance rgb' on GPU, recycled cacheline, blended on GPU [full]
 858.322514 [dev_pixelpipe] took 0.012 secs (0.014 CPU) processed `filmic rgb' on GPU, recycled cacheline, blended on GPU [full]
 958.364803 [dev_pixelpipe] took 0.042 secs (0.054 CPU) processed `output color profile' on GPU, blended on  [full]
1058.411730 [dev_pixelpipe] took 0.045 secs (0.052 CPU) processed `dithering' on CPU, recycled cacheline, blended on  [full]
1158.440432 [dev_pixelpipe] took 0.029 secs (0.042 CPU) processed `display encoding' on GPU, blended on  [full]
1258.440478 [pixelpipe] full internal pixel pipeline processing took 2.984 secs (3.133 CPU)
1358.440516 [dev_process_full] pipeline processing thread took 2.984 secs (3.133 CPU)
 1253,1082 [dev_pixelpipe] took 0,003 secs (0,004 CPU) initing base buffer [full]
 2253,1132 [dev_pixelpipe] took 0,005 secs (0,004 CPU) [full] processed `rawprepare' on GPU, blended on GPU
 3253,1152 [dev_pixelpipe] took 0,002 secs (0,001 CPU) [full] processed `temperature' on GPU, blended on GPU
 4253,5743 [dev_pixelpipe] took 0,459 secs (0,441 CPU) [full] processed `highlights' on GPU, blended on GPU
 5253,6257 [dev_pixelpipe] took 0,051 secs (0,022 CPU) [full] processed `demosaic' on GPU, blended on GPU
 6253,6307 [dev_pixelpipe] took 0,005 secs (0,002 CPU) [full] processed `lens' on GPU, blended on GPU
 7253,6394 [dev_pixelpipe] took 0,009 secs (0,003 CPU) [full] processed `exposure' on GPU, blended on GPU
 8253,6521 [dev_pixelpipe] took 0,013 secs (0,003 CPU) [full] processed `colorin' on GPU, blended on GPU
 9253,6610 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,008 secs (0,001 GPU) [channelmixerrgb]
10253,6760 [dev_pixelpipe] took 0,024 secs (0,005 CPU) [full] processed `channelmixerrgb' on GPU, blended on GPU
11258,6306 [dev_pixelpipe] took 4,954 secs (4,882 CPU) [full] processed `diffuse' on GPU, blended on GPU
12258,6548 [dev_pixelpipe] took 0,024 secs (0,005 CPU) [full] processed `colorbalancergb' on GPU, blended on GPU
13258,6857 [dev_pixelpipe] took 0,031 secs (0,009 CPU) [full] processed `filmicrgb' on GPU, blended on GPU
14258,6945 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,009 secs (0,000 GPU) [colorout]
15258,7725 [dev_pixelpipe] took 0,087 secs (0,034 CPU) [full] processed `colorout' on GPU, blended on GPU
16258,9198 [dev_pixelpipe] took 0,147 secs (0,086 CPU) [full] processed `dither' on CPU, blended on CPU
17258,9373 [dev_pixelpipe] took 0,017 secs (0,110 CPU) [full] processed `gamma' on CPU, blended on CPU
18258,9407 [dev_process_image] pixel pipeline took 5,859 secs (5,614 CPU) processing `2022-06-17__DSC00078.arw'
19258,9735 [dev_process_image] pixel pipeline took 0,033 secs (0,014 CPU) processing `2022-06-17__DSC00078.arw'

Il panning porta alla stessa situazione dello zoom: Ansel ha l’immagine a piena risoluzione in cache, quindi ricalcoliamo dal ricampionamento iniziale, che richiede 3 s. Darktable non riesce a gestire correttamente una cache, quindi ti tocca un ricalcolo completo e 5,9 s di computer bloccato.

Avanti e indietro tra darkroom e tavolo luminoso

1184.536988 [dev_pixelpipe] took 0.000 secs (0.000 CPU) to load the image.
2184.574776 [dev_pixelpipe] pipeline resync with history took 0.019 secs (0.016 CPU) for pipe virtual-preview
3185.012395 [dev_pixelpipe] pipeline resync with history took 0.020 secs (0.017 CPU) for pipe virtual-preview
4185.046379 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
5185.046424 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe virtual-preview
6185.049883 [dev_pixelpipe] pipeline resync with history took 0.026 secs (0.049 CPU) for pipe preview
7185.049931 [dev_pixelpipe] pipeline resync with history took 0.000 secs (0.000 CPU) for pipe preview
8185.068516 [dev_pixelpipe] pipeline resync with history took 0.019 secs (0.020 CPU) for pipe full
9185.068568 [dev_process_full] pipeline processing thread took 0.000 secs (0.000 CPU)
 13324,5295 [histogram] took 0,006 secs (0,005 CPU) scope draw
 23324,5837 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
 33324,6371 [dev_process_image] pixel pipeline took 0,002 secs (0,001 CPU) processing `2022-06-17__DSC00078.arw'
 4
 53324,7352 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
 63324,8074 [dev_pixelpipe] took 0,000 secs (0,000 CPU) initing base buffer [preview]
 73324,8155 [dev_pixelpipe] took 0,008 secs (0,005 CPU) [preview] processed `rawprepare' on GPU, blended on GPU
 83324,8165 [dev_pixelpipe] took 0,001 secs (0,001 CPU) [preview] processed `temperature' on GPU, blended on GPU
 93324,8178 [dev_pixelpipe] took 0,001 secs (0,003 CPU) [preview] processed `highlights' on GPU, blended on GPU
103324,8337 [dev_pixelpipe] took 0,016 secs (0,021 CPU) [preview] processed `demosaic' on GPU, blended on GPU
113324,8351 [dev_pixelpipe] took 0,001 secs (0,002 CPU) [preview] processed `lens' on GPU, blended on GPU
123324,8387 [dev_pixelpipe] took 0,003 secs (0,013 CPU) [preview] processed `exposure' on GPU, blended on GPU
133324,8440 [dev_pixelpipe] took 0,005 secs (0,015 CPU) [preview] processed `colorin' on GPU, blended on GPU
143324,8491 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,005 secs (0,012 GPU) [channelmixerrgb]
153324,8545 [dev_pixelpipe] took 0,010 secs (0,019 CPU) [preview] processed `channelmixerrgb' on GPU, blended on GPU
163325,6440 [dev_pixelpipe] took 0,789 secs (0,921 CPU) [preview] processed `diffuse' on GPU, blended on GPU
173325,6482 [dev_pixelpipe] took 0,004 secs (0,003 CPU) [preview] processed `colorbalancergb' on GPU, blended on GPU
183325,6534 [dev_pixelpipe] took 0,005 secs (0,003 CPU) [preview] processed `filmicrgb' on GPU, blended on GPU
193325,6569 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,003 secs (0,002 GPU) [colorout]
203325,6747 [dev_pixelpipe] took 0,021 secs (0,008 CPU) [preview] processed `colorout' on GPU, blended on GPU
213325,6979 [dev_pixelpipe] took 0,023 secs (0,009 CPU) [preview] processed `dither' on CPU, blended on CPU
223325,7061 [dev_pixelpipe] took 0,008 secs (0,029 CPU) [preview] processed `gamma' on CPU, blended on CPU
233325,7263 [dt_ioppr_transform_image_colorspace_rgb] `Système' -> `sRVB' took 0,020 secs (0,068 CPU) [final histogram]
243325,7357 [dev_process_image] pixel pipeline took 0,935 secs (1,152 CPU) processing `2022-06-17__DSC00078.arw'

La cache delle pipeline di Ansel sopravvive alle pipeline stesse, il che significa che uscire dalla camera oscura e rientrarci è quasi istantaneo: bastano solo 60 ms per risincronizzare tutte le pipeline con la cronologia di sviluppo e capire che abbiamo già l’output della pipeline in cache tramite il suo checksum. Nota che qui non c’è alcuna gestione speciale di alcun tipo, è un comportamento generico della cache.

Per Darktable, funziona quasi per l’immagine principale, ma non per quella di anteprima, quindi ti tocca 1 s di ricalcolo inutile. Dato che questo funziona su una pipe ma non sull’altra, scommetto che è gestito tramite casi speciali. Rispetto ad Ansel, è una penalità di prestazioni di ×1,75.

Disattivare un modulo a metà della pipeline

Ho disattivato diffuse or sharpen con lo zoom impostato su dimensione “riempi”.

 1124.206298 [dev_pixelpipe] pipeline resync with history took 0.011 secs (0.010 CPU) for pipe virtual-preview
 2124.237640 [dev_pixelpipe] pipeline resync with history took 0.011 secs (0.028 CPU) for pipe preview
 3124.290940 [dev_pixelpipe] took 0.023 secs (0.028 CPU) processed `color balance rgb' on GPU, recycled cacheline, blended on GPU [preview]
 4124.303176 [dev_pixelpipe] took 0.012 secs (0.012 CPU) processed `filmic rgb' on GPU, recycled cacheline, blended on GPU [preview]
 5124.331334 [dev_pixelpipe] took 0.028 secs (0.030 CPU) processed `output color profile' on GPU, blended on  [preview]
 6124.370193 [darkroom] took 0.007 secs (0.011 CPU) redraw
 7124.382425 [dev_pixelpipe] took 0.051 secs (0.055 CPU) processed `dithering' on CPU, blended on  [preview]
 8124.410053 [dev_pixelpipe] took 0.028 secs (0.028 CPU) processed `display encoding' on GPU, blended on  [preview]
 9124.410088 [pixelpipe] preview internal pixel pipeline processing took 0.172 secs (0.197 CPU)
10124.410115 [dev_process_preview] pipeline processing thread took 0.172 secs (0.197 CPU)
11124.433186 [dev_pixelpipe] pipeline resync with history took 0.013 secs (0.017 CPU) for pipe full
12124.433286 [dev_process_full] pipeline processing thread took 0.000 secs (0.000 CPU)
 1300,9771 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
 2301,0564 [dev_pixelpipe] took 0,016 secs (0,028 CPU) initing base buffer [full]
 3301,0936 [dev_pixelpipe] took 0,037 secs (0,037 CPU) [full] processed `rawprepare' on GPU, blended on GPU
 4301,1102 [dev_pixelpipe] took 0,017 secs (0,017 CPU) [full] processed `temperature' on GPU, blended on GPU
 5304,8700 [dev_pixelpipe] took 3,760 secs (3,786 CPU) [full] processed `highlights' on GPU with tiling, blended on CPU
 6305,2630 [resample_cl] plan 0,001 secs (0,000 CPU) resample 0,010 secs (0,001 CPU)
 7305,3543 [dev_pixelpipe] took 0,484 secs (0,284 CPU) [full] processed `demosaic' on GPU, blended on GPU
 8305,3648 [dev_pixelpipe] took 0,010 secs (0,002 CPU) [full] processed `lens' on GPU, blended on GPU
 9305,3764 [dev_pixelpipe] took 0,012 secs (0,001 CPU) [full] processed `exposure' on GPU, blended on GPU
10305,4083 [dev_pixelpipe] took 0,032 secs (0,007 CPU) [full] processed `colorin' on GPU, blended on GPU
11305,4220 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,013 secs (0,001 GPU) [channelmixerrgb]
12305,4460 [dev_pixelpipe] took 0,038 secs (0,008 CPU) [full] processed `channelmixerrgb' on GPU, blended on GPU
13307,1388 [dev_pixelpipe] took 1,693 secs (1,645 CPU) [full] processed `diffuse' on GPU, blended on GPU
14307,1500 [dev_pixelpipe] took 0,011 secs (0,003 CPU) [full] processed `colorbalancergb' on GPU, blended on GPU
15307,1612 [dev_pixelpipe] took 0,011 secs (0,003 CPU) [full] processed `filmicrgb' on GPU, blended on GPU
16307,1675 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,006 secs (0,001 GPU) [colorout]
17307,2185 [dev_pixelpipe] took 0,057 secs (0,020 CPU) [full] processed `colorout' on GPU, blended on GPU
18307,3095 [dev_pixelpipe] took 0,091 secs (0,056 CPU) [full] processed `dither' on CPU, blended on CPU
19307,3235 [dev_pixelpipe] took 0,014 secs (0,065 CPU) [full] processed `gamma' on CPU, blended on CPU
20307,3333 [dev_process_image] pixel pipeline took 6,293 secs (5,969 CPU) processing `2022-06-17__DSC00078.arw'
21307,3843 [dev_process_image] pixel pipeline took 0,051 secs (0,018 CPU) processing `2022-06-17__DSC00078.arw'
22
23307,4258 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
24307,4875 [dev_pixelpipe] took 0,000 secs (0,000 CPU) initing base buffer [preview]
25307,4895 [dev_pixelpipe] took 0,002 secs (0,002 CPU) [preview] processed `rawprepare' on GPU, blended on GPU
26307,4906 [dev_pixelpipe] took 0,001 secs (0,001 CPU) [preview] processed `temperature' on GPU, blended on GPU
27307,5609 [dev_pixelpipe] took 0,070 secs (0,072 CPU) [preview] processed `highlights' on GPU, blended on GPU
28307,5722 [dev_pixelpipe] took 0,011 secs (0,005 CPU) [preview] processed `demosaic' on GPU, blended on GPU
29307,5740 [dev_pixelpipe] took 0,002 secs (0,000 CPU) [preview] processed `lens' on GPU, blended on GPU
30307,5765 [dev_pixelpipe] took 0,002 secs (0,001 CPU) [preview] processed `exposure' on GPU, blended on GPU
31307,5798 [dev_pixelpipe] took 0,003 secs (0,002 CPU) [preview] processed `colorin' on GPU, blended on GPU
32307,5827 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,002 secs (0,002 GPU) [channelmixerrgb]
33307,5864 [dev_pixelpipe] took 0,007 secs (0,003 CPU) [preview] processed `channelmixerrgb' on GPU, blended on GPU
34307,9576 [dev_pixelpipe] took 0,371 secs (0,356 CPU) [preview] processed `diffuse' on GPU, blended on GPU
35307,9613 [dev_pixelpipe] took 0,003 secs (0,002 CPU) [preview] processed `colorbalancergb' on GPU, blended on GPU
36307,9659 [dev_pixelpipe] took 0,005 secs (0,000 CPU) [preview] processed `filmicrgb' on GPU, blended on GPU
37307,9686 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,002 secs (0,000 GPU) [colorout]
38307,9847 [dev_pixelpipe] took 0,019 secs (0,008 CPU) [preview] processed `colorout' on GPU, blended on GPU
39308,0043 [dev_pixelpipe] took 0,020 secs (0,011 CPU) [preview] processed `dither' on CPU, blended on CPU
40308,0103 [dev_pixelpipe] took 0,006 secs (0,023 CPU) [preview] processed `gamma' on CPU, blended on CPU
41308,0244 [dt_ioppr_transform_image_colorspace_rgb] `Système' -> `sRVB' took 0,014 secs (0,081 CPU) [final histogram]
42308,0353 [dev_process_image] pixel pipeline took 0,553 secs (0,604 CPU) processing `2022-06-17__DSC00078.arw'

Di nuovo, la cache di Ansel funziona e basta: disattivare diffuse or sharpen porta a ricalcolare solo i moduli a valle, il che richiede 172 ms. Di nuovo, la cache di Darktable non funziona, il che porta a ricalcolare entrambe le 2 pipeline (anteprima e principale), per un costo totale di 6,9 s. È una penalità di prestazioni di ×40, gente! Questo rende del tutto irrilevanti tutti i +15 % di accelerazioni fittizie di Ralf Brown! Ma ehi! Continua a cercare le tue chiavi perdute dove riesci a vederti i piedi invece che dove le hai perse! .

Prelevare un colore in un modulo

Impostiamo il bilanciamento del bianco in color calibration usando il selettore di colore, al livello di zoom “adatta alla finestra”.

 1colorpicker stats reading took 0.020 secs (0.088 CPU)
 2colorpicker stats reading took 0.018 secs (0.087 CPU)
 312.128030 [dev_pixelpipe] pipeline resync with history took 0.024 secs (0.037 CPU) for pipe virtual-preview
 412.137829 [dev_pixelpipe] pipeline resync with history took 0.024 secs (0.056 CPU) for pipe preview
 512.157665 [dev_pixelpipe] took 0.020 secs (0.048 CPU) processed `color calibration' on GPU, blended on GPU [preview]
 613.327326 [dev_pixelpipe] took 1.170 secs (1.231 CPU) processed `diffuse or sharpen' on GPU, blended on GPU [preview]
 713.336618 [dev_pixelpipe] took 0.009 secs (0.008 CPU) processed `color balance rgb' on GPU, recycled cacheline, blended on GPU [preview]
 813.345393 [dev_pixelpipe] took 0.009 secs (0.007 CPU) processed `filmic rgb' on GPU, recycled cacheline, blended on GPU [preview]
 913.368053 [dev_pixelpipe] took 0.023 secs (0.030 CPU) processed `output color profile' on GPU, blended on  [preview]
1013.418643 [dev_pixelpipe] took 0.050 secs (0.061 CPU) processed `dithering' on CPU, blended on  [preview]
1113.435846 [dev_pixelpipe] took 0.017 secs (0.024 CPU) processed `display encoding' on GPU, blended on  [preview]
1213.435919 [pixelpipe] preview internal pixel pipeline processing took 1.298 secs (1.408 CPU)
1313.435949 [dev_process_preview] pipeline processing thread took 1.298 secs (1.408 CPU)
1413.526316 [dev_pixelpipe] pipeline resync with history took 0.080 secs (0.376 CPU) for pipe full
1513.526443 [dev_process_full] pipeline processing thread took 0.000 secs (0.000 CPU)
16colorpicker stats reading took 0.185 secs (0.741 CPU)
17image colorspace transform RGB-->RGB took 0.000 secs (0.000 CPU) [[histogram] sample swatch]
18image colorspace transform RGB-->RGB took 0.000 secs (0.000 CPU) [[histogram] sample swatch]
19image colorspace transform RGB-->RGB took 0.000 secs (0.000 CPU) [[histogram] sample swatch]
 116,1190 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
 216,1364 dt_color_picker_helper stats reading 4 channels (filters 2492765332) cst 2 -> 2 size 1118880 denoised 0 took 0,001 secs (0,005 CPU)
 316,1396 [dev_pixelpipe] took 0,020 secs (0,025 CPU) [preview] processed `channelmixerrgb' on GPU, blended on GPU
 416,2071 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
 516,6064 [dev_pixelpipe] took 0,467 secs (0,567 CPU) [preview] processed `diffuse' on GPU, blended on GPU
 616,6106 [dev_pixelpipe] took 0,004 secs (0,004 CPU) [preview] processed `colorbalancergb' on GPU, blended on GPU
 716,6160 [dev_pixelpipe] took 0,005 secs (0,001 CPU) [preview] processed `filmicrgb' on GPU, blended on GPU
 816,6192 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,003 secs (0,002 GPU) [colorout]
 916,6364 [dev_pixelpipe] took 0,020 secs (0,010 CPU) [preview] processed `colorout' on GPU, blended on GPU
1016,6513 [dev_pixelpipe] took 0,015 secs (0,005 CPU) [preview] processed `dither' on CPU, blended on CPU
1116,6572 [dev_pixelpipe] took 0,006 secs (0,023 CPU) [preview] processed `gamma' on CPU, blended on CPU
1216,6584 dt_color_picker_helper stats reading 4 channels (filters 2492765332) cst 2 -> 2 size 1118880 denoised 0 took 0,001 secs (0,005 CPU)
1316,6586 [dt_ioppr_transform_image_colorspace] IOP_CS_RGB-->IOP_CS_LAB took 0,000 secs (0,000 CPU) [gamma]
1416,6587 [dt_ioppr_transform_image_colorspace_rgb] `Système' -> `sRVB' took 0,000 secs (0,000 CPU) [primary picker]
1516,6754 [dt_ioppr_transform_image_colorspace_rgb] `Système' -> `sRVB' took 0,017 secs (0,063 CPU) [final histogram]
1616,6843 [histogram] took 0,026 secs (0,093 CPU) final waveform
1716,6847 [dev_process_image] pixel pipeline took 0,566 secs (0,733 CPU) processing `2022-06-17__DSC00078.arw'
18
1916,6854 [dt_dev_process_image_job] loading image. took 0,000 secs (0,000 CPU)
2020,3395 [dev_pixelpipe] took 3,606 secs (3,778 CPU) [full] processed `highlights' on GPU with tiling, blended on CPU
2120,7350 [resample_cl] plan 0,001 secs (0,000 CPU) resample 0,010 secs (0,001 CPU)
2220,8263 [dev_pixelpipe] took 0,487 secs (0,285 CPU) [full] processed `demosaic' on GPU, blended on GPU
2320,8389 [dev_pixelpipe] took 0,012 secs (0,002 CPU) [full] processed `lens' on GPU, blended on GPU
2420,8505 [dev_pixelpipe] took 0,012 secs (0,002 CPU) [full] processed `exposure' on GPU, blended on GPU
2520,8812 [dev_pixelpipe] took 0,031 secs (0,008 CPU) [full] processed `colorin' on GPU, blended on GPU
2620,8972 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,016 secs (0,004 GPU) [channelmixerrgb]
2720,9551 [dev_pixelpipe] took 0,074 secs (0,023 CPU) [full] processed `channelmixerrgb' on GPU, blended on GPU
2821,8137 [dev_pixelpipe] took 0,000 secs (0,000 CPU) initing base buffer [preview]
2921,8160 [dev_pixelpipe] took 0,001 secs (0,005 CPU) [preview] processed `rawprepare' on CPU, blended on CPU
3021,8173 [dev_pixelpipe] took 0,001 secs (0,001 CPU) [preview] processed `temperature' on CPU, blended on CPU
3121,9879 [dev_pixelpipe] took 0,171 secs (0,758 CPU) [preview] processed `highlights' on CPU, blended on CPU
3222,0020 [dev_pixelpipe] took 0,014 secs (0,080 CPU) [preview] processed `demosaic' on CPU, blended on CPU
3322,0103 [dev_pixelpipe] took 0,008 secs (0,016 CPU) [preview] processed `lens' on CPU, blended on CPU
3422,0127 [dev_pixelpipe] took 0,002 secs (0,014 CPU) [preview] processed `exposure' on CPU, blended on CPU
3522,0155 [dev_pixelpipe] took 0,003 secs (0,015 CPU) [preview] processed `colorin' on CPU, blended on CPU
3622,0172 [dt_ioppr_transform_image_colorspace] IOP_CS_LAB-->IOP_CS_RGB took 0,002 secs (0,009 CPU) [channelmixerrgb]
3722,0457 dt_color_picker_helper stats reading 4 channels (filters 2492765332) cst 2 -> 2 size 1118880 denoised 0 took 0,002 secs (0,013 CPU)
3822,0458 [dev_pixelpipe] took 0,030 secs (0,180 CPU) [preview] processed `channelmixerrgb' on CPU, blended on CPU
3922,6461 [dev_pixelpipe] took 1,691 secs (5,549 CPU) [full] processed `diffuse' on GPU, blended on GPU
4022,6595 [dev_pixelpipe] took 0,013 secs (0,079 CPU) [full] processed `colorbalancergb' on GPU, blended on GPU
4122,6725 [dev_pixelpipe] took 0,013 secs (0,073 CPU) [full] processed `filmicrgb' on GPU, blended on GPU
4222,6824 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,007 secs (0,041 GPU) [colorout]
4322,7443 [dev_pixelpipe] took 0,072 secs (0,398 CPU) [full] processed `colorout' on GPU, blended on GPU
4422,8624 [dev_pixelpipe] took 0,118 secs (0,664 CPU) [full] processed `dither' on CPU, blended on CPU
4522,8878 [dev_pixelpipe] took 0,025 secs (0,172 CPU) [full] processed `gamma' on CPU, blended on CPU
4622,8894 [dev_process_image] pixel pipeline took 6,637 secs (11,596 CPU) processing `2022-06-17__DSC00078.arw'
4723,4204 [dev_pixelpipe] took 1,374 secs (8,556 CPU) [preview] processed `diffuse' on CPU, blended on CPU
4823,5271 [dev_pixelpipe] took 0,106 secs (0,617 CPU) [preview] processed `colorbalancergb' on CPU, blended on CPU
4923,5788 [dev_pixelpipe] took 0,051 secs (0,291 CPU) [preview] processed `filmicrgb' on CPU, blended on CPU
5023,5817 [dt_ioppr_transform_image_colorspace] IOP_CS_RGB-->IOP_CS_LAB took 0,003 secs (0,011 CPU) [colorout]
5123,5870 [dev_pixelpipe] took 0,008 secs (0,037 CPU) [preview] processed `colorout' on CPU, blended on CPU
5223,5913 [dev_pixelpipe] took 0,004 secs (0,004 CPU) [preview] processed `dither' on CPU, blended on CPU
5323,5964 [dev_pixelpipe] took 0,005 secs (0,021 CPU) [preview] processed `gamma' on CPU, blended on CPU
5423,5973 dt_color_picker_helper stats reading 4 channels (filters 2492765332) cst 2 -> 2 size 1118880 denoised 0 took 0,001 secs (0,006 CPU)
5523,5976 [dt_ioppr_transform_image_colorspace] IOP_CS_RGB-->IOP_CS_LAB took 0,000 secs (0,000 CPU) [gamma]
5623,5977 [dt_ioppr_transform_image_colorspace_rgb] `Système' -> `sRVB' took 0,000 secs (0,000 CPU) [primary picker]
5723,6099 [dt_ioppr_transform_image_colorspace_rgb] `Système' -> `sRVB' took 0,012 secs (0,067 CPU) [final histogram]
5823,6215 [histogram] took 0,024 secs (0,097 CPU) final waveform
5923,6219 [dev_process_image] pixel pipeline took 6,889 secs (15,651 CPU) processing `2022-06-17__DSC00078.arw'

Ansel non ha bisogno di avviare un nuovo rendering della pipeline per campionare con il selettore di colore, perché l’input e l’output del modulo sono ancora in cache, quindi bastano in totale 40 ms per campionare. Poi i nuovi parametri vengono applicati al modulo, il che modifica la cronologia e attiva un ricalcolo del modulo color calibration e di tutti i moduli a valle. Tempo totale impiegato: 1,4 s.

Darktable ha bisogno di riavviare un nuovo rendering della pipeline per campionare con il selettore di colore perché… perché no? Questo richiede 0,6 s. Sarai lieto di sapere che Darktable campiona il colore su GPU, cosa che richiede solo 1 ms (nel mezzo di quei 0,6 s di runtime della pipeline), mentre Ansel campiona su CPU in 20 ms. Poi i parametri del selettore di colore vengono applicati alla cronologia, il che riavvia un ricalcolo completo delle 2 pipeline. Tempo totale impiegato: 7,5 s.

È una penalità di ×5,4 per Darktable, tutto questo perché non migliorano le loro pipeline ma si lanciano in ogni possibile caccia alle streghe che ci sia.

Esportare a piena risoluzione

Esportiamo dopo aver aperto l’immagine in camera oscura. Questo è importante. In Darktable viene usato il campionamento “alta qualità”, in Ansel è l’unica modalità.

 118.917740 [dev_pixelpipe] took 0.000 secs (0.000 CPU) to load the image.
 218.970877 [export] creating pixelpipe took 0.054 secs (0.074 CPU)
 319.215438 [dev_pixelpipe] took 0.244 secs (0.222 CPU) processed `exposure' on GPU, blended on GPU [export]
 419.241544 [dev_pixelpipe] took 0.026 secs (0.025 CPU) processed `input color profile' on GPU, blended on  [export]
 519.267887 [dev_pixelpipe] took 0.026 secs (0.026 CPU) processed `color calibration' on GPU, blended on GPU [export]
 656.171662 [dev_pixelpipe] took 36.904 secs (36.592 CPU) processed `diffuse or sharpen' on GPU with tiling, blended on CPU [export]
 756.364588 [dev_pixelpipe] took 0.193 secs (0.156 CPU) processed `color balance rgb' on GPU, blended on GPU [export]
 857.293819 [dev_pixelpipe] took 0.929 secs (0.468 CPU) processed `filmic rgb' on GPU with tiling, blended on CPU [export]
 957.516252 [dev_pixelpipe] took 0.222 secs (0.176 CPU) processed `output color profile' on GPU, blended on  [export]
1057.534628 [dev_pixelpipe] took 0.018 secs (0.017 CPU) processed `Final resampling' on GPU, blended on  [export]
1158.462789 [dev_pixelpipe] took 0.928 secs (0.544 CPU) processed `dithering' on CPU, blended on  [export]
1258.462829 [pixelpipe] export internal pixel pipeline processing took 39.492 secs (38.227 CPU)
1358.462898 [dev_process_export] pixel pipeline processing thread took 39.492 secs (38.227 CPU)
14[export_job] exported to `/home/aurelienpierre/Téléchargements/2022-06-17__DSC00078_08.jpg'
 1526,9861 [export] creating pixelpipe took 0,063 secs (0,091 CPU)
 2526,9862 [dev_pixelpipe] took 0,000 secs (0,000 CPU) initing base buffer [export]
 3527,0072 [dev_pixelpipe] took 0,021 secs (0,020 CPU) [export] processed `rawprepare' on GPU, blended on GPU
 4527,0237 [dev_pixelpipe] took 0,016 secs (0,007 CPU) [export] processed `temperature' on GPU, blended on GPU
 5530,7560 [dev_pixelpipe] took 3,732 secs (3,868 CPU) [export] processed `highlights' on GPU with tiling, blended on CPU
 6531,1444 [dev_pixelpipe] took 0,388 secs (0,201 CPU) [export] processed `demosaic' on GPU, blended on GPU
 7531,9899 [dev_pixelpipe] took 0,845 secs (0,493 CPU) [export] processed `lens' on GPU with tiling, blended on CPU
 8532,1378 [dev_pixelpipe] took 0,148 secs (0,139 CPU) [export] processed `exposure' on GPU, blended on GPU
 9532,1968 [dev_pixelpipe] took 0,059 secs (0,047 CPU) [export] processed `colorin' on GPU, blended on GPU
10532,2287 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,031 secs (0,019 GPU) [channelmixerrgb]
11532,3206 [dev_pixelpipe] took 0,124 secs (0,089 CPU) [export] processed `channelmixerrgb' on GPU, blended on GPU
12575,5645 [dev_pixelpipe] took 43,244 secs (43,220 CPU) [export] processed `diffuse' on GPU with tiling, blended on CPU
13575,7422 [dev_pixelpipe] took 0,178 secs (0,167 CPU) [export] processed `colorbalancergb' on GPU, blended on GPU
14576,2975 [dev_pixelpipe] took 0,555 secs (0,474 CPU) [export] processed `filmicrgb' on GPU with tiling, blended on CPU
15576,3897 [resample_cl] took 0,000 secs (0,000 CPU) 1:1 copy/crop of 7968x5320 pixels
16576,4081 [dev_pixelpipe] took 0,111 secs (0,101 CPU) [export] processed `finalscale' on GPU, blended on GPU
17576,4403 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,032 secs (0,020 GPU) [colorout]
18576,5618 [dev_pixelpipe] took 0,154 secs (0,120 CPU) [export] processed `colorout' on GPU, blended on GPU
19577,0202 [dev_pixelpipe] took 0,458 secs (0,456 CPU) [export] processed `dither' on CPU, blended on CPU
20577,0204 [dev_process_export] pixel pipeline processing took 50,034 secs (49,403 CPU)
21578,2407 [export_job] exported to `/home/aurelienpierre/Téléchargements/2022-06-17__DSC00078_06.jpg'

In Ansel, dato che abbiamo aperto in precedenza l’immagine in camera oscura, le prime fasi della pipeline sono ancora in cache, quindi l’esportazione parte dal modulo exposure e richiede 39,5 s. In Darktable, beh… il solito: è un ricalcolo completo della pipeline che richiede 50 s.

Esportare di nuovo a metà risoluzione

Esportiamo a metà risoluzione subito dopo aver esportato a piena risoluzione.

165.601240 [dev_pixelpipe] took 0.000 secs (0.000 CPU) to load the image.
265.658874 [export] creating pixelpipe took 0.060 secs (0.079 CPU)
365.912677 [dev_pixelpipe] took 0.254 secs (0.233 CPU) processed `output color profile' on GPU, blended on  [export]
465.972780 [dev_pixelpipe] took 0.060 secs (0.073 CPU) processed `Final resampling' on GPU, blended on  [export]
566.160165 [dev_pixelpipe] took 0.187 secs (0.238 CPU) processed `dithering' on CPU, blended on  [export]
666.160184 [pixelpipe] export internal pixel pipeline processing took 0.501 secs (0.544 CPU)
766.160202 [dev_process_export] pixel pipeline processing thread took 0.501 secs (0.545 CPU)
8[export_job] exported to `/home/aurelienpierre/Téléchargements/2022-06-17__DSC00078_09.jpg'
 1607,7584 [export] creating pixelpipe took 0,064 secs (0,088 CPU)
 2607,7586 [dev_pixelpipe] took 0,000 secs (0,000 CPU) initing base buffer [export]
 3607,7918 [dev_pixelpipe] took 0,033 secs (0,028 CPU) [export] processed `rawprepare' on GPU, blended on GPU
 4607,8083 [dev_pixelpipe] took 0,016 secs (0,007 CPU) [export] processed `temperature' on GPU, blended on GPU
 5611,5555 [dev_pixelpipe] took 3,747 secs (3,865 CPU) [export] processed `highlights' on GPU with tiling, blended on CPU
 6611,9454 [dev_pixelpipe] took 0,390 secs (0,201 CPU) [export] processed `demosaic' on GPU, blended on GPU
 7612,7083 [dev_pixelpipe] took 0,763 secs (0,462 CPU) [export] processed `lens' on GPU with tiling, blended on CPU
 8612,8559 [dev_pixelpipe] took 0,148 secs (0,137 CPU) [export] processed `exposure' on GPU, blended on GPU
 9612,9149 [dev_pixelpipe] took 0,059 secs (0,045 CPU) [export] processed `colorin' on GPU, blended on GPU
10612,9468 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_LAB-->IOP_CS_RGB took 0,032 secs (0,021 GPU) [channelmixerrgb]
11613,0390 [dev_pixelpipe] took 0,124 secs (0,090 CPU) [export] processed `channelmixerrgb' on GPU, blended on GPU
12656,1897 [dev_pixelpipe] took 43,151 secs (42,852 CPU) [export] processed `diffuse' on GPU with tiling, blended on CPU
13656,3673 [dev_pixelpipe] took 0,178 secs (0,167 CPU) [export] processed `colorbalancergb' on GPU, blended on GPU
14656,9163 [dev_pixelpipe] took 0,549 secs (0,467 CPU) [export] processed `filmicrgb' on GPU with tiling, blended on CPU
15657,0078 [resample_cl] plan 0,001 secs (0,000 CPU) resample 0,001 secs (0,000 CPU)
16657,1070 [dev_pixelpipe] took 0,191 secs (0,181 CPU) [export] processed `finalscale' on GPU, blended on GPU
17657,1233 [dt_ioppr_transform_image_colorspace_cl] IOP_CS_RGB-->IOP_CS_LAB took 0,016 secs (0,004 GPU) [colorout]
18657,1792 [dev_pixelpipe] took 0,072 secs (0,030 CPU) [export] processed `colorout' on GPU, blended on GPU
19657,3186 [dev_pixelpipe] took 0,139 secs (0,136 CPU) [export] processed `dither' on CPU, blended on CPU
20657,3187 [dev_process_export] pixel pipeline processing took 49,560 secs (48,669 CPU)
21657,8107 [export_job] exported to `/home/aurelienpierre/Téléchargements/2022-06-17__DSC00078_07.jpg'

Poiché Ansel esporta sempre a piena risoluzione fino al modulo Final resampling (la modalità chiamata “alta qualità” in Darktable), la pipeline resta per lo più la stessa indipendentemente dalla dimensione finale. Dato che la cache di Ansel funziona, l’immagine a piena risoluzione esportata in precedenza è ancora in cache, quindi ricalcoliamo solo le ultime fasi: output color profile, downsampling, dithering. Questo richiede 0,5 s.

Darktable, come al solito, non gestisce ciò che ha calcolato in precedenza, quindi ti tocca un’altra esportazione completa di ~50 s. Ovvero una penalità di prestazioni di ×100.

Nota che la cache delle pipeline di Ansel ha una garbage collection che svuota tutte le righe di cache inutilizzate più vecchie di 5 minuti, quindi questo non funzionerebbe se aspetti troppo a lungo.

Conclusione

È esilarante leggere, su forum da tech-bro come discuss.pixls.us , che le persone si fidano più di Darktable che di Ansel perché il primo ha molte più scimmie che agitano le mani in aria fingendo di lavorare, il che a quanto pare basta a dare un senso di affidabilità e fiducia del tutto errato. È solo la prova definitiva che le persone non hanno idea di cosa succeda nel codice. Ma è una malattia capitalistica scambiare l’agitazione per lavoro, e un lavoro che non crea valore può comunque essere venduto come progresso con il marketing giusto.

Io non faccio opinioni, faccio benchmark. I numeri che ho pubblicato qui possono essere interamente riprodotti:

Darktable performance penalty per benchmark case
  • i singoli moduli sono in media 1,4 volte più veloci su Ansel CPU rispetto a Darktable CPU,
  • i singoli moduli sono in media 1,6 volte più veloci su Ansel GPU rispetto a Darktable GPU,
  • le pipeline complete sono ovunque tra 1,5 volte e 100 volte più veloci su Ansel rispetto a Darktable GPU, perché Ansel ha una cache funzionante,
  • Ansel non è solo più veloce, è anche più efficiente dal punto di vista energetico, perché vengono (ri)calcolate meno cose.

Tutto questo è stato ottenuto rimuovendo il codice della GUI dal codice di rendering della pipeline, rendendo il ciclo di vita dei dati più chiaro, più semplice e più robusto, non aggiungendo altra gestione di casi speciali e soluzioni improvvisate. Ansel è in grado di usare molta più RAM di Darktable, pur facendo la contabilità necessaria per sapere quanta ne usa davvero e se dovrebbe iniziare a riciclare memoria.

Tutto questo è la prova che il “team” di Darktable non sta migliorando il software, che la sua (mancanza di) gestione sta attivamente danneggiando il software. Ci sono state molte “ottimizzazioni” annunciate nelle note di versione dal 2022. Eppure il loro software è ancora una bestia di inefficienza dolorosamente lenta.

Nota che le ottimizzazioni a livello di modulo fatte in Darktable possono essere riportate in Ansel, anche se non tutte sembrano vere accelerazioni da quanto mostrato qui. Alcune lo sono già state. Ma Darktable non può riportare l’architettura della pipeline di Ansel, da cui proviene la maggior parte delle nostre accelerazioni.

Durante questo lavoro, ho anche perso fiducia in Vkdt . Vkdt è un tentativo rinnovato, da parte del fondatore di Darktable, Johannes Hannika, di ricreare da zero un software fotografico RAW che gira interamente su GPU tramite Vulkan. Ma da 4 anni passati a ripulire la merda degli altri, ci sono un paio di cose che ho imparato:

  1. I rallentamenti di Darktable non sono da imputare allo stack GTK o allo stack OpenCL. È vero, non sono granché, ma li si può gestire. Sono errori dei programmatori e cattiva progettazione che non è stata rivalutata al cambiare delle esigenze e dei requisiti del software.
  2. Un design cache-first permette di nascondere molte latenze evitando i ricalcoli, e un flusso di lavoro interamente su GPU si scontrerà presto con i limiti di memoria delle GPU.
  3. Le GPU stanno diventando sempre più costose, dopo la corsa alle criptovalute e ora la corsa all’IA.
  4. Ciò che ha ucciso Darktable è stata una mancanza di gestione e una mancanza di visione, una cultura del “laissez faire” e della felice casualità, che funziona solo con una manciata di sviluppatori che si conoscono tutti tra loro. Vkdt è diretto nella stessa direzione. Se al codice e alle funzioni è permesso crescere senza priorità e senza consolidamento delle API, è un treno che deraglia al rallentatore. “più nuovo = migliore” è solo uno stato transitorio. La vera sfida è gestire la crescita.

Dal punto di vista di uno sviluppatore, Darktable ha 3 malattie principali:

  1. Ai suoi sviluppatori mancano completamente (e da molto tempo) le capacità di astrazione per separare cognitivamente ciò che appartiene al livello GUI e ciò che appartiene al livello backend. Di conseguenza, troverai stati GUI/GTK incrostati in profondità nei livelli backend, il che è tanto più fastidioso dato che darktable-cli gira senza interfaccia grafica e costringe a gestire entrambe le modalità. Ma trovi anche ripetutamente problemi della GUI risolti nel livello backend, e viceversa. Questi provocano veri problemi di affidabilità, oltre a rendere il tutto complicato, illeggibile e aggrovigliato.
  2. I suoi sviluppatori non capiscono il concetto di API private e di incapsulamento: tutte le strutture dati sono pubbliche, tutti i file header vengono importati in tutto il software, quindi l’intero software conosce l’intero software ed è autorizzato a modificare le strutture dati in loco da qualsiasi punto del codice.
  3. La complessità viene “risolta” con più complessità, e nessuno sembra vederlo come un problema chiamato debito tecnico.

Le basi di codice sane proteggono i dati rendendoli privati e forzando le interazioni attraverso funzioni getter/setter definite come API pubbliche. Questo permette una facile gestione della thread safety quando arriva il momento, ma ci permette anche di tracciare cosa scrive dove e quando da un punto centrale. E poi, includi il set minimo di API in ciascun file, per isolare il codice il più possibile.

Invece di questo, il codice sorgente di Darktable scrive ovunque da ovunque, senza interfacce adeguate. Quindi qualsiasi tipo di modifica alla base di codice di Darktable provoca sistematicamente effetti collaterali e rompe qualcosa di inaspettato all’altro capo del software, in deprimenti sessioni di whack-a-mole nate dalla pigrizia e dalla stupidità.

Quindi, in Ansel è stato fatto molto lavoro noioso per consolidare le API, rendere private quante più strutture dati possibile e isolare le librerie tra loro per districare in qualche modo il grafo delle dipendenze.

Appendice: i piani

Architettura complessiva

Ecco il diagramma di flusso ad alto livello dell’intera architettura, dal database ai backbuffer. Ora possiamo finalmente disegnare un diagramma di flusso di questo caos.

flowchart TD
  subgraph Module
    M["Module instance
dt_iop_module_t"] end subgraph Develop H["dev->history
history items"] D["dt_develop_t
history_end
history_hash"] end subgraph Persist["DB + image cache"] DB["SQLite history tables"] IC["image cache
dt_image_t"] end subgraph Pipeline F["Pipe dirty flags"] P["Pipe nodes / pieces"] G["global_hash"] B["Backbuf"] end subgraph Cache C["Pixelpipe cache"] end subgraph Consumers["Backbuffer consumers"] DR["darkroom.c"] NAV["navigation.c"] EXP["imageio.c"] end M -->|"write snapshot
dt_dev_add_history_item_real()"| H H -->|"update history_end / history_hash
dt_dev_add_history_item_real()"| D D -->|"write history
dt_dev_write_history_ext()"| DB D -->|"write image-side hash
dt_dev_write_history_ext()"| IC DB -.->|"read history
dt_dev_read_history_ext()"| H IC -.->|"read image metadata
dt_dev_read_history_ext()"| H H -->|"apply to modules
dt_dev_pop_history_items_ext()"| M D -->|"mark pipes dirty
dt_dev_add_history_item_real()"| F D -->|"check history mismatch
dt_dev_darkroom_pipeline()"| F H -->|"sync history into pipe
dt_dev_pixelpipe_change()"| P F -->|"consume dirty flags
dt_dev_pixelpipe_change()"| P P -->|"compute cache keys
dt_pixelpipe_get_global_hash()"| G G -->|"key cache entries
dt_dev_pixelpipe_process_rec()"| C C -.->|"read exact-hit / input
dt_dev_pixelpipe_process_rec()"| P P -->|"publish stage output
dt_dev_pixelpipe_process_rec()"| C C -->|"promote final output
dt_dev_pixelpipe_process()"| B B -->|"borrow main/preview backbuf
_darkroom_expose()"| DR B -->|"copy preview backbuf
_lib_navigation_draw_callback()"| NAV B -->|"borrow export backbuf
dt_imageio_export_with_flags()"| EXP EXP -->|"update mipmap hash
dt_imageio_export_with_flags()"| IC

Gestione della memoria RAM/vRAM

Gestione dei buffer di cache:

flowchart TD
  H["CPU RAM buffer"]
  P["Pinned vRAM buffer
CL_MEM_USE_HOST_PTR"] V["Device-only vRAM buffer"] G["pixelpipe_process_on_GPU()"] C["pixelpipe_process_on_CPU()"] H -->|"prepare GPU input
dt_dev_pixelpipe_cache_prepare_cl_input()"| P P -->|"sync host/device if needed
dt_dev_pixelpipe_cache_sync_cl_buffer()"| G V -->|"reuse existing vRAM input
dt_dev_pixelpipe_cache_prepare_cl_input()"| G G -->|"publish pinned output
dt_dev_pixelpipe_cache_release_cl_buffer()"| P G -->|"publish device-only output
dt_dev_pixelpipe_cache_release_cl_buffer()"| V V -->|"GPU to CPU fallback copy
dt_dev_pixelpipe_cache_restore_cl_buffer()"| H H -->|"resume CPU processing
pixelpipe_process_on_CPU()"| C

Se l’output del modulo è previsto per la memorizzazione in cache (a causa delle preferenze dell’utente, della necessità di questo buffer per istogramma/selettore di colore, o perché il modulo successivo è quello attivo nella GUI), OpenCL usa un buffer di memoria pinned (agganciato alla cache RAM). Agganciamo anche i buffer di memoria di input, quando la GPU prende input dalla cache RAM. Questo è leggermente più costoso rispetto all’uso di un buffer puramente sul dispositivo, ma meno costoso della copia di quel buffer puramente sul dispositivo verso l’host. Se l’output OpenCL non è previsto per la memorizzazione in cache, usiamo un buffer puramente sul dispositivo che potrà essere riciclato in seguito ma non verrà mai copiato in RAM.

In caso di un errore OpenCL per un particolare modulo (il più ovvio: memoria esaurita), il buffer di input della GPU viene copiato in modo trasparente di nuovo nella cache RAM prima di ripiegare sul percorso puramente CPU.

Tutti questi buffer sono tracciati da voci di cache, che collegano un buffer RAM con uno vRAM (se li usiamo entrambi in una situazione pinned), oppure semplicemente tengono traccia di ciò che è allocato. Se un’allocazione di memoria GPU fallisce, prima scorriamo la cache per rilasciare tutti i buffer allocati in precedenza. Se l’allocazione di memoria GPU fallisce di nuovo dopo questo, ripieghiamo sulla CPU.

Voci di cache

La cache è una hashtable di voci di cache. La loro chiave è un hash/checksum. Abbiamo 2 tipi di voci di cache:

  • quelle interne, usate come buffer riutilizzabili per gli input/output dei moduli,
  • quelle esterne, usate come buffer usa e getta per i calcoli intermedi dei moduli.

Le voci di cache interne sono indicizzate dal global_hash del loro modulo genitore, e possono essere ritrovate in seguito (anche dalla GUI) perché quell’hash è stabile tra esecuzioni e pipeline. Possono essere reindicizzate se riutilizziamo il loro buffer di memoria, così la nuova chiave corrisponde al nuovo stato del loro modulo genitore. Queste voci di cache possono essere bloccate in modalità lettura o scrittura per un accesso concorrente sicuro tra thread paralleli. Un blocco di scrittura può essere acquisito da un solo thread se nessun altro thread sta leggendo, un blocco di lettura può essere acquisito da diversi thread se nessun altro thread sta scrivendo. Poiché il blocco di scrittura viene rilasciato dal modulo che scrive il suo output prima che il blocco di lettura venga acquisito dal modulo che usa la stessa voce di cache come suo input, e c’è un rischio di eliminazione nel frattempo, hanno anche un meccanismo di refcount: il produttore della voce di cache lo incrementa, il consumatore lo decrementa. Lo stesso meccanismo è usato dalla camera oscura per recuperare il backbuffer senza copia.

Le voci di cache esterne sono indicizzate dall’indirizzo di memoria del loro buffer di dati. Si suppone che abbiano vita breve.

Aggiungere/rimuovere voci di cache incrementa/decrementa automaticamente la dimensione globale della cache. Continuiamo ad aggiungere nuove voci finché la dimensione globale della cache è al di sotto della soglia consentita, dopodiché iniziamo a distruggere le voci più vecchie se il loro refcount è 0 e non sono bloccate.

flowchart TD
  A["absent"]
  B["reserved for write
refcount + 1
write-locked"] C["published
exact-hit eligible"] D["borrowed by consumer
refcount > 0"] E["backbuf-held
one display ref kept"] F["auto-destroy flagged"] G["removed / freed"] A -->|"create new entry
dt_dev_pixelpipe_cache_get()
dt_dev_pixelpipe_cache_get_writable()"| B C -->|"rekey reusable entry
dt_dev_pixelpipe_cache_get_writable()
dt_dev_pixelpipe_cache_rekey()"| B B -->|"backend finishes and unlocks
dt_dev_pixelpipe_process_rec()"| C C -->|"exact-hit / reopen / GUI read-only
dt_dev_pixelpipe_cache_peek()
dt_dev_pixelpipe_cache_get_read_only()"| D D -->|"release consumer ref
dt_dev_pixelpipe_cache_unref_hash()
dt_dev_pixelpipe_cache_close_read_only()"| C C -->|"keep final output alive for display
_update_backbuf_cache_reference()"| E E -->|"backbuf changes or invalidates
_update_backbuf_cache_reference()"| C C -->|"mark transient output
dt_dev_pixelpipe_cache_flag_auto_destroy()"| F F -->|"reap when no ref and no lock remain
dt_dev_pixelpipe_cache_auto_destroy_apply()"| G C -->|"explicit remove / GC / LRU
dt_dev_pixelpipe_cache_remove()
dt_dev_pixelpipe_cache_flush_old()
dt_dev_pixel_pipe_cache_remove_lru()"| G C -->|"invalid payload detected
dt_dev_pixelpipe_cache_peek()"| G

Una voce di cache non conosce il tipo di dati che contiene. Conosce solo gli indirizzi dei buffer, la loro dimensione, il loro refcount e il loro stato di blocco. È quindi un’astrazione generica di roba contenuta in memoria.


Translated from English by : Claude. In case of conflict, inconsistency or error, the English version shall prevail.