martedì 29 agosto 2023

DMVCFramework Gotcha #1: Object Pool explained

Uno degli aspetti relativi alle performance di un applicativo che spesso non viene attenzionato, è l'overhead nell'esecuzione di specifici task.
L'overhead è influenzato da diversi fattori. Uno di questi, in ambito OOP,  è il tempo di creazione di un oggetto: quando creiamo un oggetto, il processo di creazione e inizializzazione può risultare più o meno lungo in funzione della complessità dell'oggetto e del processo di inizializzazione dei suoi attributi. La ripetuta creazione e distruzione di questo oggetto, influenza negativamente il tempo di runtime di tutto il singolo processo e dell'intera applicazione nel suo complesso.

Una possibile soluzione a questo problema, sarebbe quella di avere a disposizione oggetti già creati e per i quali non dobbiamo preoccuparci del loro ciclo di vita.

Uno dei Design Patterns appartenente alla categoria dei Creational Design Pattern, è l'Object Pool.
Dalla sua definizione:

l'Object Pool è un creational design pattern, ovvero uno di quei pattern applicabile per la risoluzione di problematiche di performance legate al processo di inizializzazione delle componenti (classi) di cui il nostro sistema si compone.

Il concetto alla base dell'Object Pool si può sintetizzare descrivendo un processo durante il quale un client, che necessita una istanza di una classe, chiede al Pool di fornirgliela. Il Pool, alla richiesta, 'verifica' se ha una istanza di detta classe disponibile per soddisfare la richiesta. Se l'istanza è disponibile nel pool, viene restituita; se l'istanza non è disponibile, l'Object Pool, che conosce il metodo utile alla creazione dell'istanza, ne crea una nuova e la restituisce.
Quando il client termina l'utilizzo dell'istanza ottenuta, la restituisce al Pool che non la distrugge ma la rende disponibile per le future richieste.

Questo processo è ciclico e permette, nel migliore dei casi, di abbattere l'overhead derivato dalla creazione dell'istanza quando questa è già presente nel pool.

DMVCFramework, il Framework RESTfulJSON-RPC e ActiveRecord per Delphi mantenuto da Daniele Teti e il team di DMVCFramework,  ha tante piccole, ma importanti, features forse poco conosciute. Una di questa è l'implementazione dell'ObjectPool.

La unit MVCFramework.ObjectPool.pas presente in DMVCFramework definisce una classe che implementa l'Object Pool così come il pattern lo definisce:

TObjectPool<T: class, constructor> = class(TInterfacedObject, IObjectPool<T>)

La classe (generica) TObjectPool, implementa l'interfaccia IObjectPool<T: class, constructor>
IObjectPool richiede l'implementazione di due specifici metodi per il pooling:

function GetFromPool(const RaiseExceptionIfNotAvailable: Boolean = False): T;
procedure ReleaseToPool(const Obj: T);

GetFromPool() è invocata per ottenere un oggetto dal Pool; ReleaseToPool() è invocata per rilasciare e restituire l'istanza dell'oggetto al Pool.

L'istanza della classe TObjectPool è ottenuta invocando la class function CreatePool() della classe TPoolFactory definita così:

Da notare il parametro Factory : TFunc<T> della CreatePool(): esso rappresenta il costruttore della classe che specializza il pool. Esso sarà utilizzato dal pool quando riceverà una richiesta e non vi saranno istanze disponibili.

A questo punto, mostro nel codice che segue, l'applicazione di quanto descritto con un esempio:

1) Definiamo un Pool per la classe TClientDataset:

           2) Utilizziamo l'istanza:

Con queste semplici istruzioni, abbiamo delegato al Pool la creazione e la gestione del ciclo di vita dei nostri oggetti.

A completamento di questa introduzione, pongo l'attenzione su due caratteristiche dell'Object Pool presente in DMVCFramework:
 - Il Pool ha una dimensione: POOL_SIZE indica il numero massimo di istanze mantenute nel pool (le istanze, comunque, occupano memoria!)
 - E' implementato un thread che periodicamente interviene per ripulire il pool da istanze in funzione dei valori di Shrink impostati.

Per completare, è utile sapere che esiste anche la unit MVCFramework.IntfObjectPool che in maniera analoga implementa un ObjectPool per le Interfacce.

#codinglikeacoder



2 commenti:

Embedding "Flavio", my name

 Hi, I'm { "embedding": [ 0.02614186331629753, 0.4381698966026306, -0.21378979086875916, 0.2108100950717926, ...