lunedì 14 ottobre 2024

Parallel Linq "troppo" veloce ?!?

 PLINQ (parallel linq)  permette la gestione di operazioni su insiemi di oggetti su più core  con un notevole vantaggio nei tempi di esecuzione , qui un introduzione alla tecnologia PLinq , ho fatto qualche prova ed i risultati portano a credere che il test stesso andrebbe condotto in altro modo , ho caricato 1000000 di record nella tabella Products e poi ho fatto una select sulle righe prima con Linq "semplice" e poi con ParallelLinq .La macchina utilizzata ha un processore i7-11370H , 4 cpu cores ed 8 "cpu logiche" ,disco ssd .

Ho  aggiunto 1 milione di record nella tabella Products di un db Northwind , ho creato un progetto Asp .net Mvc , ed ho utilizzato la possibilità "Database First" (con il comando Scaffold-DbContext) per creare nel progetto Asp .net la struttura di oggetti che rispecchi quella tabellare di Northwind per poter utilizzare Linq e ParallelLinq .Segue lo snippet per caricare la tabella :

public class Util
{        
    public Util() { }

    public void CaricaDB() {
        SqlConnection sqlConnection = null;
        string str = "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=Northwind;"
             + "Integrated Security=SSPI";
        sqlConnection = new SqlConnection(str);
        sqlConnection.Open();
        int k= 1000000;
        int j= 2000000;
        while(k<j)
        {
            string sql = "INSERT INTO Products (ProductName) values ('ProductName" + k.ToString() + "'" + ")";
            SqlCommand cmd = new SqlCommand(sql, sqlConnection);
            cmd.ExecuteNonQuery();                
            k++;
        }
        sqlConnection.Close();
    }    
}

lo snippet può essere chiamato in un app console :

internal class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("inizio caricamento");
        Util u=new Util();
        u.CaricaDB();
        Console.WriteLine("fine caricamento");
    }
    
}

a questo punto è possibile provare le query sulle collezioni di oggetti , creiamo un controller Products ed aggiungiamo un metodo (un action) di test ,di nome Test ( :-) ) 

public async Task<IActionResult> Test()
{
    DateTime dt1 = DateTime.Now;
    //var list = from p in _context.Products.AsParallel().WithDegreeOfParallelism(2) where p.ProductName.Contains("%0%") select p;
    var list = from p in _context.Products.AsParallel() where p.ProductName.Contains("%0%") select p;
    //var list = from p in _context.Products where p.ProductName.Contains("%0%") select p;
    DateTime dt2 = DateTime.Now;
    TimeSpan span = dt2.Subtract(dt1);
    ViewData["Milliseconds"] = span.TotalMilliseconds;
    return View("Test");
}

segue la view Test.cshtml :

@{
    ViewData["Title"] = "Test";
}
<h1>Test</h1>
Milliseconds: @ViewData["Milliseconds"];


 
i due Datetime.Now racchiudono le chiamate che caricano i dati , la prima query permette di specificare il numero di processori utilizzati passando il numero stesso al metodo WithDegreeOfParallelism , in questo caso 2 .
Ho testato le altre due modalità di usare linq e plinq ,
la seconda è from p in _context.Products.AsParallel() where p.ProductName.Contains("%0%") select p;
in questo modo PLinq cerca di usare tutti i core della macchina , 

la terza "query" utilizza Linq (non parallel) :
from p in _context.Products where p.ProductName.Contains("%0%") select p;

ho eseguito 10 volte la seconda query (Plinq) in visual studio eseguendo il progetto (non in debug) ed ho ottenuto :

0,8038
1,2417
1,7415
0,653
0,4739
0,5141
1,0046
0,6074
0,5183
0,9088

i risultati sono in millisecondi




ho eseguito 10 volte la seconda query (Linq) in visual studio eseguendo il progetto (non in debug) ed ho ottenuto :

836,0055 
978,9648
450,3928
977,5208 
435,4028
516,4756
535,6739
855,7413
648,2701
488,9144


Plinq sembrerebbe centinaia di volte più veloce , evidentemente qualcosa non và nei test... , se avete suggerimenti , critiche ,segnalazione di errori, potete scrivere a gianmarco.castagna@gmail.com


Parallel Linq "troppo" veloce ?!?

 PLINQ (parallel linq)  permette la gestione di operazioni su insiemi di oggetti su più core  con un notevole vantaggio nei tempi di esecuzi...