Don't forget to also checkout my second blog containing articles to all other related ICT topics!!

Friday, May 25, 2012

Importance of immutability for concurrency

Let's take a look at how much performance we may gain when applying immutability to our code. I will let the code snippets speak for themselves. Below a generic trait with 3 methods of which two are important: the find and save method.
package robbypelssers.concurrency

trait Store[K,V] {
    def find(k: K): Option[V]
    def save(v: V): Unit
    def getName(): String
}

Now we create a mutable and immutable store (both insert key-value pairs (index mapped to fibonacci number) in respectively a mutable and immutable hashmap.
package robbypelssers.concurrency

import collection.mutable.HashMap

class MutableFibonacciStore extends Store[Int, Int] with Fibonacci {
  
  val store = new HashMap[Int, Int]

  def find(n: Int): Option[Int] = synchronized(store.get(n))

  def save(n: Int): Unit = synchronized(store.put(n, fibonacci(n)))
  
  def getName() = "MutableFibonacciStore"

}

package robbypelssers.concurrency

import collection.immutable.HashMap

class ImmutableFibonacciStore extends Store[Int, Int] with Fibonacci {

  var store = new HashMap[Int,Int]
  
  def find(n: Int): Option[Int] = store.get(n)

  def save(n: Int): Unit = synchronized(store = store + ((n, fibonacci(n))))
  
  def getName() = "ImmutableFibonacciStore"

}

package robbypelssers.concurrency

trait Fibonacci {

    def fibonacci(n: Int): Int = n match {
        case 0 => 0
        case 1 => 1
        case _ => fibonacci(n-2) + fibonacci(n-1)
    }  
  
}

Below a very simple profiler which will give some idea of how long both stores will take to do a lot of calls to find and save.
package robbypelssers.concurrency

import java.util.Date

object Profiler {

  /**
   * profile method works like an Around Advice in AspectOriented programming
   */
  def profile[T](body: => T) = {
      val start = new Date()
      val result = body
      val end = new Date()
      println("Execution took " + (end.getTime() - start.getTime()) / 1000 + " seconds") 
      result
  }
  
}

package robbypelssers.concurrency

import java.util.Date
import Profiler._
object StoreConcurrencyTest extends App {

  val immutableStore = new ImmutableFibonacciStore()
  val mutableStore = new MutableFibonacciStore()
  val numberOfOperations = 40
  
  def useStore(store: Store[Int,Int], n: Int) {
        println("using " + store.getName())
        for (x:Int <- 1 to n) {
              store.save(x)
        }
        for (x:Int <- 1 to n) {
              store.find(x)
        }
  }
  
  profile(useStore(immutableStore, numberOfOperations))
  profile(useStore(mutableStore, numberOfOperations))
  
}

using ImmutableFibonacciStore
Execution took 1 seconds
using MutableFibonacciStore
Execution took 4 seconds

No comments:

Post a Comment