čtvrtek 31. října 2013

Klasika versus LINQ


To si takhle něco programuji a najednou si říkám: "nešlo by to lépe?". Šlo!

Preambule

Drahnou dobu, od roku 2005, je s námi LINQ. Občas jej použiji, ale že bych na něj byl nějaký odborník, se říct nedá... Jsem tedy rád za každý, byť malý, pokrok.

Vo co tedy GO?

V rámci svého zaměstnání jsem řešil tabulku s intervaly platností nějakých číselníků a součástí byla i metoda pro převod této tabulky do lidské řečí. Tedy něco, co mi napíše, že Palivo SVÍTIPLYN je platné do roku 1990, pak v letech 2001 až 2003, 2010 až 2020 a od roku 2030 platí furt dál.

Ve zdrojáku je tato funkce napsaná pomocí LINQ pod názvem ToStringA(). Funkce ToStringB() je napsaná klasicky. Zbytek je omáčka. Výsledek obou funkcí je identický a je k vidění na obrázku v záhlaví.

A jak je to s rychlostí?

Jednoduchých 5 000 000 cyklů je v případě LINQ hotovo za 23 sekund, klasika je za 16 sekund. Dalším experimentům jsem se nevěnoval, zajímavý by mohl být vliv velikosti zpracovávaného seznamu intervalu a pod. Nicméně, pokud nejde o kritickou část aplikace, upřednostňuji jednoduchost a čitelnost. Tedy LINQ.

Zdroják

using System;
using System.Collections.Generic;
using System.Linq;

namespace ArtYearInterval
{
    class Interval
    {
        /// 
        /// Počátek intervalu.
        /// 
        public Nullable<int> From = null;
        
        /// 
        /// Konec intervalu.
        /// 
        public Nullable<int> To = null;

        /// 
        /// Řetězcová reprezentace objektu.
        /// 
        /// Počátek a konec oddělen pomlčkou.
        public override string ToString()
        {
            return String.Format("{0}-{1}", From, To);
        }
    }

    class IntervalList : List<Interval>
    {
        /// 
        /// Řetězcová prezentace objektu, seřazené intervaly jsou odděleny středníky.
        /// 
        /// 
        /// Řešení s Linq.
        /// 
        /// 
        public string ToStringA()
        {
            return String.Join("; ", this.Select(it => it.ToString()).OrderBy(it => it).ToArray());
        }

        /// 
        /// Řetězcová prezentace objektu, seřazené intervaly jsou odděleny středníky.
        /// 
        /// 
        /// Klasické řešení.
        /// 
        /// 
        public string ToStringB()
        {
            //pomocné pole pro realizaci řazení
            string[] temp = new string[this.Count];

            //naplníme
            for (int index = 0; index < this.Count; index++)
                temp[index] = this[index].ToString();

            //seřadíme
            Array.Sort(temp);

            return String.Join("; ", temp);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IntervalList intervalList = new IntervalList();

            intervalList.Add(new Interval() { From = 2030, To = null });
            intervalList.Add(new Interval() { From = 2010, To = 2020 });
            intervalList.Add(new Interval() { From = null, To = 1990 });
            intervalList.Add(new Interval() { From = 2001, To = 2003 });

            Console.WriteLine(intervalList.ToStringA());
            Console.WriteLine(intervalList.ToStringB());
            
            Console.ReadKey();
        }
    }
}




2 komentáře:

  1. Zajímavé zamyšlení. Možná by nebylo od věci podrobit LINQ důkladnějším testům, jak zmiňujete v posledním odstavci...23 a 16 sekund je vlastně žádný rozdíl..
    Zajímavé by také bylo porovnat LINQ při použití s Entity Frameworkem oproti klasickému SQL.
    Asi dost bude záležet na typu dotazu.

    OdpovědětSmazat
    Odpovědi
    1. LINQ&EF verzus PURE SQL> Tak zrovna v tomhle už pár let jedeme. LINQ zpravidla vytvoří skutečně velký SQL dotaz, který často taky dá serveru pokouřit. Tak jsme zatím 'průzkumem bojem' došli k řešení, že pro aplikace typu 'správce číselníků', 'importér' a pod. používáme EF spolu s LINQ. Exportéry řešíme napřed přes EF a LINQ a až si náš jediný zákazník ujasní, co vlastně chce a navíc se ozve se, že je to pomalé, tak to přepíšeme do čistého SQL. Jinak čisté SQL nám způsobuje problémy, protože máme tzv. permanentně polymorfní datový model, který se mění každým okamžikem a neplatné SQL dotazy nelze odchytit při kompilaci.

      Jinak používáme Oracle v kombinaci s (mini) drivery od Devartu.

      Smazat