Google I/O è un involucro! Recupera le sessioni di TensorFlow Visualizza le sessioni

Ottimizza le prestazioni della GPU TensorFlow con TensorFlow Profiler

Panoramica

Questa guida ti mostrerà come utilizzare TensorFlow Profiler con TensorBoard per ottenere informazioni dettagliate e ottenere le massime prestazioni dalle tue GPU ed eseguire il debug quando una o più delle tue GPU sono sottoutilizzate.

Se sei nuovo nel Profiler:

Tieni presente che l'offload dei calcoli sulla GPU potrebbe non essere sempre vantaggioso, in particolare per i modelli piccoli. Possono esserci spese generali dovute a:

  • Trasferimento dati tra host (CPU) e dispositivo (GPU); e
  • A causa della latenza coinvolta quando l'host avvia i kernel GPU.

Flusso di lavoro per l'ottimizzazione delle prestazioni

Questa guida illustra come eseguire il debug dei problemi di prestazioni a partire da una singola GPU, passando poi a un singolo host con più GPU.

Si consiglia di eseguire il debug dei problemi di prestazioni nel seguente ordine:

  1. Ottimizza ed esegui il debug delle prestazioni su una GPU:
    1. Verificare se la pipeline di input è un collo di bottiglia.
    2. Eseguire il debug delle prestazioni di una GPU.
    3. Abilitare precisione misto (con fp16 (float16)) ed eventualmente attivare XLA .
  2. Ottimizza ed esegui il debug delle prestazioni sul singolo host multi-GPU.

Ad esempio, se si utilizza un tensorflow strategia di distribuzione per la formazione di un modello su un singolo host con più GPU e preavviso non ottimale utilizzo della GPU, dovresti prima ottimizzare e mettere a punto le prestazioni per una GPU prima di debug del sistema multi-GPU.

Come base per ottenere il codice performante su GPU, questa guida presuppone che si sta già utilizzando tf.function . Il Keras Model.compile e Model.fit API utilizzeranno tf.function automaticamente sotto il cofano. Quando si scrive un ciclo di formazione personalizzato con tf.GradientTape , fare riferimento alla prestazione migliore con tf.function su come attivare tf.function s.

Le sezioni successive illustrano gli approcci suggeriti per ciascuno degli scenari precedenti per aiutare a identificare e correggere i colli di bottiglia delle prestazioni.

1. Ottimizza le prestazioni su una GPU

In un caso ideale, il tuo programma dovrebbe avere un elevato utilizzo della GPU, una comunicazione minima dalla CPU (l'host) alla GPU (il dispositivo) e nessun sovraccarico dalla pipeline di input.

Il primo passaggio nell'analisi delle prestazioni consiste nell'ottenere un profilo per un modello in esecuzione con una GPU.

Di TensorBoard Profiler pagina di riepilogo -che mostra una vista di alto livello di come il vostro modello eseguito durante un profilo di run-in grado di fornire un'idea di quanto lontano il vostro programma è dallo scenario ideale.

TensorFlow Profiler Overview Page

I numeri chiave a cui prestare attenzione alla pagina di panoramica sono:

  1. Quanto del tempo di passaggio deriva dall'esecuzione effettiva del dispositivo?
  2. La percentuale di operazioni effettuate sul dispositivo rispetto all'host
  3. Quante kernel usano fp16

Raggiungere prestazioni ottimali significa massimizzare questi numeri in tutti e tre i casi. Per ottenere una comprensione approfondita del programma, è necessario avere familiarità con Profiler di TensorBoard visualizzatore di analisi . Le sezioni seguenti mostrano alcuni modelli comuni del visualizzatore di tracce da cercare durante la diagnosi dei colli di bottiglia delle prestazioni.

Di seguito è riportata un'immagine di una vista di traccia del modello in esecuzione su una GPU. Dall'ambito tensorflow Nome e tensorflow Ops sezioni, è possibile identificare diverse parti del modello, come il passo in avanti, la funzione di perdita, passaggio all'indietro / calcolo del gradiente, e l'aggiornamento peso ottimizzatore. Si può anche avere i ops in esecuzione sulla GPU successivo a ciascun flusso, che si riferiscono ai flussi CUDA. Ogni flusso viene utilizzato per attività specifiche. In questo tracciato, Stream # 118 viene utilizzato per lanciare i kernel di calcolo e le copie da dispositivo a dispositivo. Streaming # 119 viene utilizzato per la copia host-to-device e Stream # 120 per dispositivo di copiare host.

La traccia seguente mostra le caratteristiche comuni di un modello performante.

image

Ad esempio, la linea temporale GPU computing (Stream # 118) sembra "occupato" con poche lacune. Ci sono copie minimi da host a dispositivo (Stream # 119) e da dispositivo a schiera (Stream # 120), così come le lacune minime tra passaggi. Quando esegui il Profiler per il tuo programma, potresti non essere in grado di identificare queste caratteristiche ideali nella visualizzazione della traccia. Il resto di questa guida copre scenari comuni e come risolverli.

1. Eseguire il debug della pipeline di input

Il primo passaggio nel debug delle prestazioni della GPU consiste nel determinare se il programma è associato all'input. Il modo più semplice per capire questo è quello di utilizzare il Profiler analizzatore Input-gasdotto , su TensorBoard, che offre una panoramica del tempo speso in cantiere di ingresso.

image

Puoi intraprendere le seguenti azioni potenziali se la tua pipeline di input contribuisce in modo significativo al tempo di passaggio:

  • È possibile utilizzare il tf.data SPECIFICI guida per imparare come eseguire il debug la pipeline di ingresso.
  • Un altro modo rapido per verificare se la pipeline di input è il collo di bottiglia consiste nell'utilizzare dati di input generati casualmente che non richiedono alcuna pre-elaborazione. Ecco un esempio di utilizzo di questa tecnica per un modello RESNET. Se la pipeline di input è ottimale, dovresti riscontrare prestazioni simili con dati reali e con dati casuali/sintetici generati. L'unico sovraccarico nel caso dei dati sintetici sarà dovuto alla copia dei dati di input che può essere nuovamente precaricata e ottimizzata.

Inoltre, fare riferimento alle migliori pratiche per l'ottimizzazione della pipeline dei dati in ingresso .

2. Eseguire il debug delle prestazioni di una GPU

Ci sono diversi fattori che possono contribuire a un basso utilizzo della GPU. Di seguito sono riportati alcuni scenari comunemente osservati quando guardando il visualizzatore di analisi e possibili soluzioni.

1. Analizzare gli spazi tra i passaggi

Un'osservazione comune quando il programma non viene eseguito in modo ottimale sono gli intervalli tra le fasi di allenamento. Nell'immagine della vista della traccia di seguito, c'è un ampio divario tra i passaggi 8 e 9, il che significa che la GPU è inattiva durante quel periodo.

image

Se il visualizzatore di tracce mostra grandi spazi tra i passaggi, potrebbe essere un'indicazione che il programma è vincolato all'input. In tal caso dovresti fare riferimento alla sezione precedente sul debug della pipeline di input se non lo hai già fatto.

Tuttavia, anche con una pipeline di input ottimizzata, è ancora possibile avere degli spazi tra la fine di un passaggio e l'inizio di un altro a causa della contesa dei thread della CPU. tf.data fa uso di thread in background per parallelizzare pipeline di elaborazione. Questi thread possono interferire con l'attività lato host della GPU che si verifica all'inizio di ogni passaggio, come la copia dei dati o la pianificazione delle operazioni della GPU.

Se si notano grandi lacune sul lato host, che gli orari di questi PO sulla GPU, è possibile impostare la variabile d'ambiente TF_GPU_THREAD_MODE=gpu_private . Questo assicura che i kernel GPU vengono lanciati dai loro propri thread dedicati, e non vengono messi in coda dietro tf.data lavoro.

I gap tra fasi possono anche essere causate da calcoli metrici, callback KERAS o ops fuori di tf.function quella corsa sull'host. Queste operazioni non hanno le stesse prestazioni delle operazioni all'interno di un grafico TensorFlow. Inoltre, alcune di queste operazioni vengono eseguite sulla CPU e copiano i tensori avanti e indietro dalla GPU.

Se dopo aver ottimizzato la pipeline di input noti ancora degli spazi tra i passaggi nel visualizzatore di traccia, dovresti guardare il codice del modello tra i passaggi e verificare se la disabilitazione di callback/metriche migliora le prestazioni. Alcuni dettagli di queste operazioni si trovano anche nel visualizzatore di tracce (sia lato dispositivo che lato host). La raccomandazione in questo scenario è di ammortizzare il sovraccarico di queste operazioni eseguendole dopo un numero fisso di passaggi anziché ogni passaggio. Quando si utilizza il compile metodo nella tf.keras API, impostando experimental_steps_per_execution bandiera fa automaticamente. Per i cicli di formazione personalizzati, uso tf.while_loop .

2. Ottieni un maggiore utilizzo del dispositivo

1. Piccoli kernel GPU e ritardi nel lancio del kernel host

L'host accoda i kernel per essere eseguiti sulla GPU, ma è coinvolta una latenza (circa 20-40 μs) prima che i kernel vengano effettivamente eseguiti sulla GPU. In un caso ideale, l'host accoda abbastanza kernel sulla GPU in modo tale che la GPU passi la maggior parte del suo tempo in esecuzione, piuttosto che attendere che l'host accoda più kernel.

Il Profiler pagina panoramica sulle esposizioni TensorBoard quanto tempo la GPU è stato inattivo a causa di attesa sul host per i kernel di lancio. Nell'immagine sottostante, la GPU è inattiva per circa il 10% del tempo di attesa in attesa dell'avvio dei kernel.

image

Il visualizzatore di analisi per lo stesso programma spettacoli piccoli spazi tra i kernel dove l'ospite è kernel varo occupato sulla GPU.

image

Lanciando molte piccole operazioni sulla GPU (come un'aggiunta scalare, ad esempio), l'host potrebbe non tenere il passo con la GPU. Il tensorflow Statistiche strumento TensorBoard per le stesse profilo mostra 126,224 operazioni Mul assumono 2,77 secondi. Pertanto, ogni kernel è di circa 21,9 μs, che è molto piccolo (circa lo stesso tempo della latenza di avvio) e può potenzialmente causare ritardi nel lancio del kernel host.

image

Se i vostri traccia spettatori spettacoli tanti piccoli spazi tra ops sulla GPU come nell'immagine sopra, è possibile:

  • Concatena piccoli tensori e usa operazioni vettorializzate o usa una dimensione batch più grande per fare in modo che ogni kernel lanciato faccia più lavoro, il che manterrà la GPU occupata più a lungo.
  • Assicurarsi che si sta utilizzando tf.function per creare grafici tensorflow, in modo che non si esegue ops in un modo ansioso pura. Se si utilizza Model.fit (come opporsi ad un ciclo di formazione personalizzato con tf.GradientTape ), quindi tf.keras.Model.compile verrà automaticamente fare questo per voi.
  • Fuse kernel usando XLA con tf.function(jit_compile=True) o auto-clustering. Per maggiori informazioni, visitare il Abilita precisione misto e XLA sezione sottostante per imparare come attivare XLA per ottenere prestazioni più elevate. Questa funzione può portare a un utilizzo elevato del dispositivo.
2. Posizionamento operativo TensorFlow

Il Profiler pagina Panoramica mostra la percentuale di ops posto sull'host contro il dispositivo (si può anche verificare il posizionamento dei ops specifici guardando il visualizzatore di analisi . Come nell'immagine qui sotto, si desidera che la percentuale di ops sull'host essere molto piccolo rispetto al dispositivo.

image

Idealmente, la maggior parte delle operazioni ad alta intensità di calcolo dovrebbe essere collocata sulla GPU.

Per scoprire quali dispositivi le operazioni e tensori nel modello vengono assegnati a, insieme tf.debugging.set_log_device_placement(True) come la prima dichiarazione del programma.

Si noti che in alcuni casi, anche se si specifica un OP ad essere immessi in un particolare dispositivo, la sua applicazione potrebbe ignorare questa condizione (ad esempio: tf.unique ). Anche per la formazione singola GPU, specificando una strategia di distribuzione, come ad esempio tf.distribute.OneDeviceStrategy , può portare a un posizionamento più deterministica del ops sul dispositivo.

Uno dei motivi per cui la maggior parte delle operazioni viene eseguita sulla GPU è impedire copie di memoria eccessive tra l'host e il dispositivo (sono previste copie di memoria per i dati di input/output del modello tra host e dispositivo). Un esempio di copia eccessiva è dimostrato nella vista traccia in basso sulla GPU flussi # 167, # 168 e # 169.

image

Queste copie a volte possono danneggiare le prestazioni se bloccano l'esecuzione dei kernel GPU. Memoria copiare operazioni nella traccia spettatore hanno più informazioni sui ops che sono la fonte di questi tensori copiati, ma potrebbe non essere sempre facile associare un memCopy con op. In questi casi, è utile guardare le operazioni vicine per verificare se la copia in memoria avviene nella stessa posizione in ogni passaggio.

3. Kernel più efficienti su GPU

Una volta che l'utilizzo della GPU del tuo programma è accettabile, il passaggio successivo consiste nell'aumentare l'efficienza dei kernel GPU utilizzando i Tensor Core o fondendo le operazioni.

1. Utilizza i nuclei tensori

Moderna NVIDIA® GPU si sono specializzati Tensor core che possono migliorare significativamente le prestazioni del kernel ammissibili.

È possibile utilizzare di TensorBoard statistiche kernel GPU di visualizzare che i kernel GPU sono Tensor core-ammissibili e che stanno utilizzando i kernel Tensor Cores. Abilitazione fp16 (vedere Abilitazione sezione mista di precisione sotto) è un modo per rendere il vostro programma generale Matrix Multiply (GEMM) kernel (ops matmul) utilizzare il Tensor Nucleo. Kernel GPU utilizzano i nuclei tensore efficiente quando la precisione è FP16 e dimensioni di ingresso / uscita tensoriali è divisibile per 8 o 16 (per int8 ).

Per le altre raccomandazioni dettagliate su come rendere i kernel efficiente per le GPU, fare riferimento al NVIDIA® profonda imparare le prestazioni di guida.

2. Fusibili

Usa tf.function(jit_compile=True) di fondere ops piccole per formare grandi kernel che portano a significativi miglioramenti delle prestazioni. Per ulteriori informazioni, fare riferimento alla XLA guida.

3. Abilita la precisione mista e XLA

Dopo aver seguito i passaggi precedenti, l'abilitazione della precisione mista e XLA sono due passaggi facoltativi che puoi eseguire per migliorare ulteriormente le prestazioni. L'approccio suggerito consiste nell'abilitarli uno per uno e verificare che i vantaggi in termini di prestazioni siano quelli previsti.

1. Abilita la precisione mista

I tensorflow precisione misto spettacoli di guida come attivare fp16 precisione su GPU. Abilita AMP su GPU NVIDIA® utilizzare Tensor Cores e realizzare fino a 3x incrementi nella velocità complessiva rispetto all'utilizzo solo fp32 (float32) precisione su Volta e più recenti architetture di GPU.

Assicurati che le dimensioni di matrice/tensore soddisfino i requisiti per chiamare i kernel che utilizzano Tensor Core. I kernel GPU utilizzano i Tensor Core in modo efficiente quando la precisione è fp16 e le dimensioni di input/output sono divisibili per 8 o 16 (per int8).

Si noti che con cuDNN v7.6.3 e versioni successive, le dimensioni della convoluzione verranno automaticamente riempite ove necessario per sfruttare i Tensor Core.

Seguire le best practice di seguito per massimizzare i benefici delle prestazioni di fp16 precisione.

1. Usa kernel fp16 ottimali

Con fp16 abilitato, moltiplicazione di matrici del vostro programma (GEMM) kernel, dovrebbero usare il corrispondente fp16 versione che utilizza i nuclei Tensor. Tuttavia, in alcuni casi, questo non accade e non si verifica l'aumento di velocità previsto di attivare fp16 , come programma ricade alla realizzazione inefficiente, invece.

image

Il kernel GPU statistiche spettacoli pagina che ops sono Tensor core ammissibili e che i kernel sono in realtà utilizzando l'efficiente Tensor Nucleo. La guida NVIDIA® sulle prestazioni apprendimento profondo contiene ulteriori suggerimenti su come sfruttare Tensor Cores. Inoltre, i vantaggi di utilizzare fp16 mostrerà anche in kernel che in precedenza erano di memoria legato, come ora i ops avrà la metà del tempo.

2. Ridimensionamento della perdita dinamica e statica

Ridimensionamento perdita è necessario quando si utilizza fp16 per evitare underflow a causa di scarsa precisione. Ci sono due tipi di perdita scalatura, dinamica e statica, entrambi i quali sono spiegati in maggior dettaglio nella guida di precisione mista . È possibile utilizzare la mixed_float16 criterio per attivare automaticamente il ridimensionamento perdita entro l'ottimizzatore Keras.

Quando si tenta di ottimizzare le prestazioni, è importante ricordare che il ridimensionamento della perdita dinamica può introdurre operazioni condizionali aggiuntive che vengono eseguite sull'host e portare a spazi vuoti che saranno visibili tra i passaggi nel visualizzatore di tracce. D'altra parte, il ridimensionamento della perdita statica non ha tali spese generali e può essere un'opzione migliore in termini di prestazioni con il problema che è necessario specificare il valore di scala della perdita statica corretto.

2. Abilita XLA con tf.function(jit_compile=True) o auto-clustering

Come passaggio finale per ottenere le migliori prestazioni con una singola GPU, puoi sperimentare con l'abilitazione di XLA, che fonderà le operazioni e porterà a un migliore utilizzo del dispositivo e a un minore ingombro di memoria. Per i dettagli su come abilitare XLA nel programma con tf.function(jit_compile=True) o auto-clustering, consultare la XLA guida.

È possibile impostare il livello di JIT globale per -1 (off), 1 o 2 . Un livello più alto è più aggressivo e può ridurre il parallelismo e utilizzare più memoria. Impostare il valore su 1 se si dispone di restrizioni di memoria. Si noti che XLA non funziona bene per i modelli con forme di tensore di input variabili poiché il compilatore XLA dovrebbe continuare a compilare i kernel ogni volta che incontra nuove forme.

2. Ottimizza le prestazioni sul singolo host multi-GPU

Il tf.distribute.MirroredStrategy API può essere utilizzato per la formazione modello in scala da una GPU a più GPU su un singolo host. (Per saperne di più su come fare la formazione distribuito con tensorflow, fare riferimento alla formazione distribuita con tensorflow , Usa una GPU , e Usa TPU guide e la formazione distribuita con Keras tutorial.)

Sebbene la transizione da una GPU a più GPU dovrebbe idealmente essere scalabile immediatamente, a volte è possibile riscontrare problemi di prestazioni.

Quando si passa dall'addestramento con una singola GPU a più GPU sullo stesso host, idealmente si dovrebbe sperimentare il ridimensionamento delle prestazioni con solo il sovraccarico aggiuntivo della comunicazione gradiente e un maggiore utilizzo del thread dell'host. A causa di questo sovraccarico, non si avrà un'accelerazione esatta di 2 volte se si passa da 1 a 2 GPU, ad esempio.

La vista della traccia di seguito mostra un esempio dell'overhead di comunicazione aggiuntivo durante l'addestramento su più GPU. C'è un sovraccarico per concatenare i gradienti, comunicarli attraverso le repliche e dividerli prima di eseguire l'aggiornamento del peso.

image

Il seguente elenco di controllo ti aiuterà a ottenere prestazioni migliori durante l'ottimizzazione delle prestazioni nello scenario multi-GPU:

  1. Cerca di massimizzare la dimensione del batch, il che porterà a un maggiore utilizzo dei dispositivi e ad ammortizzare i costi di comunicazione su più GPU. Utilizzando il profiler di memoria aiuta a ottenere un senso di come chiudere il programma è quello di utilizzo della memoria di picco. Si noti che mentre una dimensione del lotto più elevata può influire sulla convergenza, questo è generalmente compensato dai vantaggi in termini di prestazioni.
  2. Quando si passa da una singola GPU a più GPU, lo stesso host ora deve elaborare molti più dati di input. Quindi, dopo (1), si consiglia di ricontrollare le prestazioni della pipeline di input e assicurarsi che non si tratti di un collo di bottiglia.
  3. Controlla la sequenza temporale della GPU nella vista di traccia del tuo programma per eventuali chiamate AllReduce non necessarie, poiché ciò si traduce in una sincronizzazione su tutti i dispositivi. Nella vista traccia mostrato sopra, l'AllReduce avviene tramite il NCCL kernel, e v'è un solo NCCL chiamata su ciascuna GPU per i gradienti di ciascun gradino.
  4. Verificare la presenza di operazioni di copia D2H, H2D e D2D non necessarie che possono essere ridotte a icona.
  5. Controlla il tempo di passaggio per assicurarti che ogni replica stia facendo lo stesso lavoro. Ad esempio, può accadere che una GPU (tipicamente, GPU0 ) viene oversubscribed perché l'host termina erroneamente per mettere più lavoro su di esso.
  6. Infine, controlla il passaggio di addestramento su tutte le GPU nella visualizzazione della traccia per eventuali operazioni in esecuzione in sequenza. Questo di solito accade quando il tuo programma include dipendenze di controllo da una GPU a un'altra. In passato, il debug delle prestazioni in questa situazione veniva risolto caso per caso. Se si osserva questo comportamento nel vostro programma, presentare un problema di GitHub con le immagini della vostra vista traccia.

1. Ottimizza il gradiente AllReduce

Durante l'addestramento con una strategia sincrona, ogni dispositivo riceve una parte dei dati di input.

Dopo aver calcolato i passaggi avanti e indietro attraverso il modello, i gradienti calcolati su ciascun dispositivo devono essere aggregati e ridotti. Questo gradiente AllReduce avviene dopo il calcolo del gradiente su ogni dispositivo, e prima della ottimizzatore aggiorna i pesi modello.

Ciascuna GPU primo concatena i gradienti di tutti i livelli del modello, li comunica attraverso GPU utilizzando tf.distribute.CrossDeviceOps ( tf.distribute.NcclAllReduce è il default), e quindi restituisce i gradienti dopo la riduzione per strato.

L'ottimizzatore utilizzerà questi gradienti ridotti per aggiornare i pesi del modello. Idealmente, questo processo dovrebbe avvenire contemporaneamente su tutte le GPU per evitare qualsiasi sovraccarico.

Il tempo per AllReduce dovrebbe essere approssimativamente lo stesso di:

(number of parameters * 4bytes)/ (communication bandwidth)

Questo calcolo è utile come controllo rapido per capire se le prestazioni ottenute durante l'esecuzione di un processo di formazione distribuito sono quelle previste o se è necessario eseguire un ulteriore debug delle prestazioni. È possibile ottenere il numero di parametri nel modello da Model.summary .

Si noti che ogni parametro del modello è 4 byte di dimensione poiché tensorflow utilizza fp32 (float32) per comunicare gradienti. Anche quando si è fp16 abilitato, NCCL AllReduce utilizza fp32 parametri.

Per ottenere i vantaggi del ridimensionamento, il tempo di passaggio deve essere molto più elevato rispetto a questi costi generali. Un modo per ottenere ciò consiste nell'utilizzare una dimensione batch maggiore poiché la dimensione batch influisce sul tempo di passaggio, ma non influisce sul sovraccarico della comunicazione.

2. Contesa del thread dell'host GPU

Quando si eseguono più GPU, il compito della CPU è mantenere occupati tutti i dispositivi avviando in modo efficiente i kernel GPU sui dispositivi.

Tuttavia, quando ci sono molte operazioni indipendenti che la CPU può pianificare su una GPU, la CPU può decidere di utilizzare molti dei suoi thread host per mantenere occupata una GPU e quindi avviare i kernel su un'altra GPU in un ordine non deterministico . Ciò può causare un'inclinazione o un ridimensionamento negativo, che può influire negativamente sulle prestazioni.

Il visualizzatore di analisi qui sotto mostra l'overhead quando la CPU barcolla lanci kernel GPU modo inefficiente, come GPU1 è inattivo e poi comincia a correre ops dopo GPU2 è iniziata.

image

La vista traccia per gli spettacoli host che l'host lancia noccioli su GPU2 prima di lanciare il GPU1 (nota che il sotto tf_Compute* op non sono indicativi di fili CPU).

image

Se si verifica questo tipo di sfalsamento dei kernel GPU nella vista di traccia del programma, l'azione consigliata è:

  • Impostare la variabile d'ambiente tensorflow TF_GPU_THREAD_MODE a gpu_private . Questa variabile di ambiente dirà all'host di mantenere privati ​​i thread per una GPU.
  • Per default, TF_GPU_THREAD_MODE=gpu_private imposta il numero di fili per 2, che è sufficiente nella maggior parte dei casi. Tuttavia, tale numero può essere modificato impostando la variabile di ambiente tensorflow TF_GPU_THREAD_COUNT al numero desiderato di fili.