FIFO Cache — concetti chiave, algoritmo di coda e come funziona

Autore: IT Sectr Pubblicato: 2026-06-13 Tempo di lettura: 8 min

FIFO Cache (First In First Out Cache) “è un algoritmo di caching che rimuove l'elemento aggiunto per primo, indipendentemente dalla frequenza di accesso”. Viene implementato come una coda: i nuovi elementi vengono aggiunti in coda e, quando si verifica un overflow, l'elemento in testa viene rimosso. Secondo Android Developers (2026), FIFO Cache fornisce O(1) per tutte le operazioni, ma è inferiore a LRU in termini di hit-ratio sotto pattern di accesso ai dati non uniformi.

Punti chiave

  • FIFO Cache “è un algoritmo che rimuove l'elemento più vecchio in base al tempo di aggiunta (First In First Out)”
  • Struttura “coda (Queue), dove l'aggiunta è in coda, la rimozione in testa”
  • Complessità di tutte le operazioni O(1) se implementato tramite buffer circolare o LinkedList
  • Non considera la frequenza di accesso “la rimozione è per tempo di aggiunta, non per popolarità”
  • Applicazione “bufferizzazione di flussi, allocazione equa delle risorse, caching di risposte HTTP”

Cos'è FIFO Cache?

FIFO Cache (First In First Out Cache) è una cache di dimensione fissa che utilizza una coda per gestire gli elementi. Il primo elemento aggiunto viene posto in testa alla coda e sarà il primo ad essere rimosso in caso di overflow. I nuovi elementi vengono sempre aggiunti in coda, garantendo che l'ordine di rimozione corrisponda all'ordine di aggiunta.

A differenza di LRU, che riordina gli elementi ad ogni accesso, FIFO non modifica la posizione degli elementi esistenti nelle richieste get. Questo rende l'algoritmo completamente deterministico: conoscendo l'ordine di aggiunta, si può prevedere con precisione quale elemento sarà rimosso successivamente. Questa prevedibilità è fondamentale per i sistemi in tempo reale dove i dati devono essere elaborati nell'ordine di arrivo.

Un'implementazione di FIFO Cache può essere costruita su diverse strutture dati: un buffer circolare per le massime prestazioni, una lista collegata per la flessibilità, o due pile (coda a due pile) per linguaggi senza coda integrata. Il buffer circolare offre la migliore località di cache e un overhead minimo, ma richiede la pre-allocazione della memoria per maxSize.

Operazioni di base di FIFO Cache

L'operazione enqueue(value) aggiunge un elemento in coda. Se la dimensione raggiunge maxSize, l'elemento in testa viene rimosso prima dell'aggiunta. L'operazione dequeue() rimuove e restituisce l'elemento in testa “per l'estrazione forzata dell'elemento più vecchio. L'operazione peek() restituisce l'elemento in testa senza rimuoverlo “per visualizzare l'elemento più vecchio senza modificare la coda”.

Come funziona FIFO Cache

L'algoritmo FIFO imita il comportamento di una coda normale: primo arrivato, primo servito. Nel contesto del caching, ciò significa che l'elemento che è rimasto più a lungo nella cache verrà rimosso quando sarà necessario spazio “indipendentemente dalla sua popolarità. La politica di rimozione di FIFO ignora la frequenza di accesso, il che è sia un punto di forza che una debolezza dell'algoritmo.

Quando implementato tramite un buffer circolare, vengono utilizzati due puntatori: head (indice della testa della coda) e tail (indice della coda). Durante enqueue, l'elemento viene scritto all'indice tail e tail viene incrementato. Se tail raggiunge la dimensione del buffer, torna all'inizio dell'array. Se tail raggiunge head, la coda è piena e head viene spostato (rimozione). Il buffer circolare non richiede allocazione dinamica della memoria ed evita la frammentazione.

FIFO Cache mostra un hit-ratio dal 40% al 60% per carichi di lavoro tipici, che è superiore a LIFO ma inferiore a LRU. Tuttavia, per scenari in cui l'accesso ai dati è uniforme e non ci sono punti caldi, FIFO può mostrare risultati paragonabili a LRU con una complessità di implementazione significativamente inferiore. La memoria viene utilizzata in modo efficiente: non sono necessari puntatori aggiuntivi per il riordino degli elementi.

Il problema dell'inquinamento della cache

Il principale svantaggio di FIFO è la suscettibilità all'inquinamento della cache. Se una grande quantità di dati che non saranno mai più necessari viene aggiunta alla cache, questi rimuoveranno gradualmente tutti gli elementi utili e l'hit-ratio crollerà. LRU risolve parzialmente questo problema perché gli elementi usati frequentemente vengono costantemente rinfrescati spostandoli in testa, mentre i dati monouso vengono rimossi più rapidamente. In FIFO, i dati monouso rimangono nella cache fino a quando non vengono rimossi naturalmente dall'ordine della coda.

Confronto tra FIFO, LRU e LIFO

La scelta tra FIFO, LRU e LIFO dipende dal pattern di accesso ai dati e dai requisiti di prevedibilità del comportamento. LRU è ottimale per la maggior parte degli scenari, FIFO per i dati in streaming con accesso uniforme e LIFO per le strutture a pila.

ParametroFIFOLRULIFO
Criterio di rimozionePrimo aggiuntoMeno recentemente usatoUltimo aggiunto
StrutturaCodaHashMap + Lista doppiamente collegataPila
PrevedibilitàAltaMediaAlta
Protezione dall'inquinamentoBassaMediaBassa
Dati in streamingEccellenteSoddisfacenteScarso
Risorse (CPU/RAM)MinimoMedioMinimo

FIFO è ideale per scenari in cui l'ordine di elaborazione deve corrispondere all'ordine di arrivo: bufferizzazione dei dati, registrazione, elaborazione di eventi. LRU è migliore per il caching con accesso non uniforme (dati utente). LIFO è applicabile solo per pile e Annulla. Per la maggior parte delle applicazioni mobili, LRU rimane la scelta predefinita, ma FIFO può essere preferibile sotto rigidi vincoli di memoria o requisiti di prevedibilità.

Dove viene utilizzato FIFO Cache

FIFO Cache trova applicazione in scenari in cui la prevedibilità della rimozione o l'ordine di elaborazione dei dati sono importanti. Esaminiamo i principali casi d'uso.

Bufferizzazione di dati in streaming

Durante la riproduzione audio e video, i dati arrivano in un flusso continuo e vengono temporaneamente memorizzati in un buffer. FIFO Cache garantisce che i primi frammenti ricevuti siano i primi ad essere inviati per la decodifica “che garantisce una riproduzione fluida senza ritardi. La dimensione del buffer viene scelta in base al bitrate del flusso e al ritardo accettabile: tipicamente 2–5 secondi per l'audio, 10–30 secondi per il video. FIFO è ideale per tali scenari poiché il riordino dei dati (come in LRU) non ha senso.

Code di richieste di rete

Quando si limita il numero di richieste di rete simultanee, FIFO Cache può essere utilizzato per memorizzare le richieste in attesa. La prima richiesta aggiunta verrà eseguita per prima, garantendo una distribuzione equa delle risorse di rete tra i diversi componenti dell'applicazione. Questo approccio viene utilizzato in OkHttp Dispatcher e librerie simili per la gestione del pool di connessioni.

Caching di risposte HTTP

Le cache semplici di risposte HTTP sui dispositivi mobili utilizzano spesso FIFO. Le risposte alle richieste vengono memorizzate nell'ordine di arrivo e, quando viene raggiunto il limite, quelle più vecchie vengono rimosse. Sebbene LRU fornirebbe un hit-ratio migliore per gli scenari utente, FIFO è più semplice da implementare e non richiede la memorizzazione del momento dell'ultimo accesso per ogni risposta. Per le API con carico uniforme, la differenza di hit-ratio tra FIFO e LRU è minima.

Elaborazione di eventi tattili

Nelle applicazioni mobili, gli eventi tattili vengono bufferizzati in una coda FIFO prima dell'elaborazione dei gesti. Ogni evento deve essere elaborato nell'ordine in cui si è verificato, altrimenti il gesto verrà riconosciuto in modo errato. Un FIFO Cache con limite di dimensione previene l'overflow del buffer durante gli swipe rapidi, scartando gli eventi più vecchi se l'applicazione non riesce a stare al passo.

Esempi di codice FIFO Cache

Esaminiamo un'implementazione di FIFO Cache in Kotlin utilizzando un buffer circolare “l'approccio più performante per i dispositivi mobili.

kotlin
class FifoCache<V>(
    private val maxSize: Int
) {
    private val buffer = arrayOfNulls<V>(maxSize)
    private var head = 0
    private var tail = 0
    private var size = 0

    fun enqueue(value: V) {
        if (size == maxSize) {
            // rimuovere l'elemento più vecchio
            buffer[head] = null
            head = (head + 1) % maxSize
            size--
        }
        buffer[tail] = value
        tail = (tail + 1) % maxSize
        size++
    }

    fun dequeue(): V? {
        if (size == 0) return null
        val result = buffer[head]
        buffer[head] = null
        head = (head + 1) % maxSize
        size--
        return result
    }

    fun peek(): V? {
        return buffer[head]
    }
}

Il buffer circolare utilizza indici head e tail che si incrementano ciclicamente modulo maxSize. Quando size == maxSize, enqueue rimuove prima l'elemento in head (il più vecchio), sposta head e poi scrive il nuovo elemento in tail. L'aritmetica modulare riporta automaticamente i puntatori all'inizio dell'array, eliminando la copia manuale dei dati.

Implementazione in Swift tramite due pile

In Swift, un'alternativa conveniente è una coda FIFO basata su due pile (coda a due pile). Tutte le operazioni enqueue vanno nella prima pila (push) e durante dequeue, gli elementi vengono trasferiti nella seconda pila in ordine inverso “rendendo dequeue O(1) in media.

swift
struct FifoCache<Value> {
    private let maxSize: Int
    private var inStack = [Value]()
    private var outStack = [Value]()

    mutating func enqueue(value: Value) {
        if inStack.count + outStack.count >= maxSize {
            if outStack.isEmpty {
                outStack = inStack.reversed()
                inStack.removeAll()
            }
            outStack.removeLast()
        }
        inStack.append(value)
    }

    mutating func dequeue() -> Value? {
        if outStack.isEmpty {
            outStack = inStack.reversed()
            inStack.removeAll()
        }
        return outStack.popLast()
    }
}

Due pile forniscono complessità ammortizzata O(1) per enqueue e dequeue. outStack.removeLast() durante la rimozione elimina l'elemento più vecchio (il primo aggiunto). Questo approccio non richiede pre-allocazione di memoria ma può creare un carico aggiuntivo sul garbage collector durante frequenti inversioni di pila. Per applicazioni mobili con memoria limitata, il buffer circolare rimane più preferibile.

Domande frequenti

In cosa FIFO Cache si differenzia da una coda?

Una coda è una struttura dati astratta senza limitazione di dimensione. FIFO Cache è una coda con dimensione massima fissa e una politica di rimozione: in caso di overflow, l'elemento in testa viene rimosso automaticamente. Una coda normale blocca l'aggiunta in caso di overflow o si espande dinamicamente, mentre FIFO Cache accetta sempre nuovi dati rimuovendo quelli vecchi.

Quando FIFO Cache è migliore di LRU?

FIFO è migliore di LRU in scenari con accesso uniforme ai dati senza punti caldi. Ad esempio, durante il caching di file di registro o dati in streaming, ogni valore viene utilizzato una volta e LRU non offre vantaggi. FIFO è anche preferibile sotto rigidi vincoli di memoria “non richiede puntatori aggiuntivi per il riordino, risparmiando 16+ byte per elemento.

Come implementare FIFO Cache su Android?

Su Android, puoi utilizzare ArrayDeque dalla libreria standard di Kotlin, che implementa un buffer circolare. Per FIFO Cache, avvolgi ArrayDeque: durante enqueue, controlla la dimensione e se superata, chiama removeFirst(). Per una versione thread-safe, usa ConcurrentLinkedDeque o SynchronizedArrayDeque.

Qual è il problema dell'inquinamento di FIFO Cache?

Se un grande volume di dati monouso viene aggiunto alla cache, rimuoverà tutti gli elementi utili. Ad esempio, caricare 50 immagini per una galleria con maxSize=30 rimuoverà le prime 20 immagini utili, sebbene l'utente probabilmente vi ritornerà. LRU risolve parzialmente questo problema: gli elementi usati frequentemente vengono rinfrescati e rimangono nella cache.

Si può combinare FIFO con LRU?

Sì, esistono algoritmi ibridi. 2Q (Two-Queue) divide la cache in due parti: calda (LRU) e fredda (FIFO). I nuovi elementi vanno prima nella coda FIFO e solo gli accessi ripetuti li spostano nella parte LRU. Questo protegge LRU dall'inquinamento da dati monouso mantenendo un alto hit-ratio per gli elementi usati frequentemente.

Riepilogo

  • FIFO Cache “un algoritmo di caching che rimuove il primo elemento aggiunto in caso di overflow
  • Coda “la struttura di base che fornisce O(1) per enqueue e dequeue
  • Buffer circolare “implementazione ottimale con memoria fissa e senza frammentazione
  • Prevedibilità “conoscendo l'ordine di aggiunta, si può determinare con precisione il prossimo elemento da rimuovere
  • Dati in streaming “scenario ideale per FIFO, dove l'ordine di elaborazione corrisponde all'ordine di arrivo
  • Inquinamento “il principale svantaggio: i dati monouso possono rimuovere elementi usati frequentemente
  • Utilizza FIFO per buffer, code e flussi, LRU per il caching con accesso non uniforme

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche