martedì 8 marzo 2011

Vi presento GridGain

Come promesso, vi presento GridGain. Si tratta di un framework per permettervi di fare programmazione distribuita in modo molto semplice. Ciò che lo rende veramente potente è il supporto per Scala, infatti Scala dà il meglio di sé proprio in questi contesti. Supponete di avere una rete con 5 nodi e la vostra applicazione suddivide un certo carico di un lavoro su tutti i nodi. Se il sistema scala orizzontalmente e passiamo a 50 nodi, dobbiamo rivedere tutto il programma? Non se usate GridGain. Se inoltre siete stati bravi, e avete applicato i bei paradigmi funzionali alla vostra applicazione, il vostro codice è già scalabile.
Ad esempio, avete una funzione in Scala per calcolare la costante di Nepero:
object Program {

  def invFact(i : Int) : BigDecimal = i match {
    case 0 => BigDecimal(1)
    case n if n > 0 => (BigDecimal(1) / BigDecimal(n)) * invFact(n - 1)
  }

  def neper(iterations : Int) = {
    (BigDecimal(0) /: ((1 to iterations) map invFact))( _ + _)
  }

  ...
}
Dove avete utilizzato la formula:
All'aumentare di iterations aumenta la precisione. È ovvio che può essere molto migliorato, è solo un esempio. Supponiamo adesso che volete farlo andare in parallelo. Con GridGain è semplicissimo:
  1. accedete i vostri nodi (quanti ne volete/potete) lanciando ggstart. Ogni nodo si mette in attesa:
  2. importate le librerie di gridgain nel progetto e modificate il codice in modo da utilizzare gridgain per il calcolo:
      def neper(iterations : Int) = {
        grid !*~ (
              for (w <- 1 to iterations) yield () => {
                val ret = invFact(w)
                println("Ricevuto: " + w + " - Restituito: " + ret)
                ret
              },
              (s: Seq[BigDecimal]) => (BigDecimal(0) /: s)(_ + _)
            )
      }
      
      def main(args : Array[String]) : Unit = {
        scalar {
            val n = neper(100);
            println("e = " + n)
        }
      }
    
Se, per esempio, accendiamo un solo nodo, a parte quello da cui fate partire l'applicazione, quindi 2 nodi in tutto, vedrete che il carico si è suddiviso grosso modo a metà (il balancing viene fatto dinamicamente). L'output del nodo pricinpale è:
Ricevuto: 37 - Restituito: 7.265460179153071315382745030722890E-44
Ricevuto: 71 - Restituito: 1.175808554667930839412331276025168E-102
Ricevuto: 15 - Restituito: 7.647163731819816475901131985788074E-13
Ricevuto: 57 - Restituito: 2.467495709560789306494418417905353E-77
Ricevuto: 27 - Restituito: 9.183689863795546148425716836473919E-29
Ricevuto: 47 - Restituito: 3.866628513960593886829197697871059E-60
Ricevuto: 51 - Restituito: 6.446959640457172680454177834252131E-67
Ricevuto: 21 - Restituito: 1.957294106339126123084757437350544E-20
Ricevuto: 17 - Restituito: 2.811457254345520763198945583010321E-15
Ricevuto: 61 - Restituito: 1.970131956802168311835039121583386E-84
Ricevuto: 59 - Restituito: 7.210682961895936021316243184995189E-81
Ricevuto: 63 - Restituito: 5.043860616493006430709265544248299E-88
Ricevuto: 9 - Restituito: 0.000002755731922398589065255731922398590
Ricevuto: 31 - Restituito: 1.216125041553517949629974685692294E-34
Ricevuto: 19 - Restituito: 8.220635246624329716955981236872284E-18
Ricevuto: 53 - Restituito: 2.339245152560657721500064526216304E-70
Ricevuto: 35 - Restituito: 9.677592958631890992089816380922888E-41
Ricevuto: 77 - Restituito: 6.887854405285506994393217621555398E-114
Ricevuto: 67 - Restituito: 2.741896188035459954765761198513713E-95
Ricevuto: 85 - Restituito: 3.549744558233655586243013385073271E-129
Ricevuto: 81 - Restituito: 1.724992688482351758285854365654940E-121
Ricevuto: 99 - Restituito: 1.071510288125466923183546759519195E-156
Ricevuto: 97 - Restituito: 1.039579281539328008872677066085523E-152
Ricevuto: 83 - Restituito: 2.534517614578830088577511556942316E-125
Ricevuto: 13 - Restituito: 1.605904383682161459939237717015495E-10
Ricevuto: 25 - Restituito: 6.446950284384473396194853219204692E-26
Ricevuto: 55 - Restituito: 7.876246304918039466330183589953885E-74
Ricevuto: 7 - Restituito: 0.0001984126984126984126984126984126985
Ricevuto: 3 - Restituito: 0.1666666666666666666666666666666666
Ricevuto: 87 - Restituito: 4.744379254522394528525813131613567E-133
Ricevuto: 29 - Restituito: 1.130996288644771693155876457693833E-31
Ricevuto: 11 - Restituito: 2.505210838544171877505210838544173E-8
Ricevuto: 93 - Restituito: 8.644742106836940619829775373573826E-145
Ricevuto: 89 - Restituito: 6.057685462873333156953285408086779E-137
Ricevuto: 73 - Restituito: 2.237078680875058674680995578434491E-106
Ricevuto: 23 - Restituito: 3.868170170630684037716911931522814E-23
Ricevuto: 49 - Restituito: 1.643974708316579033515815347734293E-63
Ricevuto: 91 - Restituito: 7.396441346609686394326355809629767E-141
Ricevuto: 33 - Restituito: 1.151633562077195028058688149329824E-37
Ricevuto: 69 - Restituito: 5.843768516699616271879286441845085E-99
Ricevuto: 45 - Restituito: 8.359650847182803983324725422797232E-57
Ricevuto: 5 - Restituito: 0.008333333333333333333333333333333330
Ricevuto: 65 - Restituito: 1.212466494349280391997419601982764E-91
Ricevuto: 43 - Restituito: 1.655210867742195188698295633713852E-53
Ricevuto: 95 - Restituito: 9.680562269694222418622368839388382E-149
Ricevuto: 75 - Restituito: 4.030772397973078693118910952134216E-110
Ricevuto: 1 - Restituito: 1
Ricevuto: 39 - Restituito: 4.902469756513543397694159939759036E-47
Ricevuto: 41 - Restituito: 2.989310827142404510789121914487217E-50
Ricevuto: 79 - Restituito: 1.117795262136563939369233628944401E-117
Sum = 1.71828182845904523536028747135266241129844606162999606421881338461047508297046530851272129739288024500806666138654505778398426211369899599762685530664760916526029476793035635790672605174468695
[18:09:56] GridGain stopped OK [uptime=00:00:01:898]
cioè ha eseguito 55 iterazioni. Le altre sono state eseguite dall'altro nodo (vi risparmio l'output, che vedreste nella sua console). Se a runtime avessi lanciato ancora un terzo nodo, si sarebbe preso il proprio carico di lavoro e tutto il sistema avrebbe scalato di conseguenza, automaticamente!

Notare che sono stato comunque costretto a modificare il codice originario. Perché? Perché non ho fatto il bravo, cioé non ho utilizzato né un monad writer né un monad state. Li vedremo in un prossimo post. :)

onof

Nessun commento: