Post on 02-Jul-2015
Scalable Applications
with Scala
Nimrod Argov
Tikal Knowledge
What is a Scalable
Application?
What is a Scalable
Application?
Scalability is the ability of a system,
network, or process to handle a growing
amount of work in a capable manner or its
ability to be enlarged to accommodate that
growth.
-- wikipedia.org
What is a Scalable
Application?
Administrative - More
People using the
system, or more
programmers writing it
What is a Scalable
Application?
Functional - Adding new
functionality with minimal
effort
What is a Scalable
Application?
Geographical - Maintaining
performance, usefulness or
usability regardless of locale
specific user concentration
What is a Scalable
Application?
Load - The ability of a
system to expand and
contract to accommodate
heavier or lighter loads
Concurrency /
Parallelism
Optimizing CPU Usage Efficiency
Scala
Scala brings together the Functional and Object
Oriented worlds, where until now both sides
considered the other to be totally wrong, or the
work of the devil.
-- Martin Oderski
Fun!
Java
java.util.concurrent
Locking
Mutable Shared State
Java
When is it enough?
Java
When is it enough?
Too Little - Race Conditions
Too Much - Loss of Parallelism / Deadlocks
Immutability
Concurrency requires Immutability
Classes should be immutable unless there's a very good reason to
make them mutable....If a class cannot be made immutable, limit
its mutability as much as possible.
-- Effective
Java (Joshua Bloch)
Immutability
//java
public final class Student
private final int id;
private final String name;
public Student(int id, String name){
this.id = id;
this.name = name;
}
.. getters…
}
Immutability
//java
public final class Student
private final int id;
private final String name;
public Student(int id, String name){
this.id = id;
this.name = name;
}
.. getters…
}
Immutability
//java
List<Student> students = new ArrayList<Student>();
List<Student> immutableStudents = Collections.unmodifiableList(students);
List<Students> ohBother = new CopyOnWriteArrayList<Student>();
Immutability
//java
List<Student> students = new ArrayList<Student>();
List<Student> immutableStudents = Collections.unmodifiableList(students);
students.add(new Student(15, “Harry Potter”));
immutableStudents now shows a different list!
Scala Collections
● Mutable/Immutable variants
● Immutable variant is imported by default
● Operations are Fast
● Cost of object creation is highly exaggerated by
programmers
Scala Collections
● Set
● Map
● List
● Stream
● Vector
● Stack
● Queue
● Tree
Scala Collections
Many Operations:
● ++ (add all)
● collect
● combinations
● contains
● count
● diff
● endsWith
●
● filter
● find
● flatMap
● flatten
● forEach
● intersect
● lift
●
● min
● max
● mkString
● partition
● permutations
● reduce
● reverse
Scala Parallel
Collections
Motivation
“The hope was, and still is, that implicit parallelism behind
a collections abstraction will bring reliable parallel
execution one step closer to the workflow of mainstream
developers.”
--Scala Documentation
Scala Parallel
Collections
Example: Calculate function over list of numbers
Scala Parallel
Collections
Example: Calculate function over list of numbers
Java:
// Somehow cut the list to N parts
Scala Parallel
Collections
Example: Calculate function over list of numbers
Java:
// Somehow cut the list to N parts
// Create ForkJoinTask (or Just a Runnable if pre JDK 6)
Scala Parallel
Collections
Example: Calculate function over list of numbers
Java:
// Somehow cut the list to N parts
// Create ForkJoinTask (or Just a Runnable if pre JDK 6)
// Use ForkJoinPool to run the tasks
// Wait for all to finish (join)
Scala Parallel
Collectionspublic class Operation extends RecursiveAction{
private List<Integer> numbers;
private List<Integer> results;
private int start, end;
public Operation(List<Integer> nums){
numbers = nums;
start = 0;
end = numbers.size();
}
protected void doComputation(){
for(int i = start; i < end; i++)
results.set(i, calculate(number));
}
}
Scala Parallel
Collectionspublic class Operation extends RecursiveAction{
protected static int threshold = 1000;
...
public Operation(List<Integer> nums){ … }
protected void doComputation(){ … }
protected void compute(){
if (numbers.length < threshold)
doComputation();
else
invokeAll(new SumOperation(numbers, 0,
numbers.size() / 2),
new SumOperation(numbers, numbers.size() /
2, numbers.size());
}
}
Scala Parallel
Collections
val myParallelList = someList.par
myParallelList map calculate(_)
Scala Parallel
Collections
val myParallelList = someList.par
myParallelList map calculate(_)
5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95
Scala Parallel
Collections
val myParallelList = someList.par
myParallelList map calculate(_)
5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95
5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95
Scala Parallel
Collections
val myParallelList = someList.par
myParallelList map calculate(_)
5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95
5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95
11 22 66 11 55 22 66 55 99 33 88 88 55 22 77 11
Scala Parallel
Collections
val myParallelList = someList.par
myParallelList map calculate(_)
5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95
5 8 12 7 13 86 14 2 37 23 67 41 1 77 24 95
11 22 66 11 55 22 66 55 99 33 88 88 55 22 77 11
11 22 66 11 55 22 66 55 99 33 88 88 55 22 77 11
Scala Parallel
Collections
• ParArray
• ParVector
• mutable.ParHashMap
• mutable.ParHashSet
• immutable.ParHashMap
• immutable.ParHashSet
• ParRange
• ParTrieMap
Scala Parallel
Collections
Each collection defines:
● Splitter
● Combiner
Default # of threads: how many cores have you got?
Underlying implementation depends on configuration
Scala Parallel
Collections
ForkJoinTaskSupport (JDK 1.6 or higher)
ThreadPoolTaskSupport
ExecutionContextTaskSupport
Scala Parallel
Collections
<<< WARNING… WARNING… >>>
Side Effects or Non-Associative operations will create
non-deterministic results!
Scala Parallel
Collections
var sum = 0;
List(1 to 10000).par.forEach(sum += _)
List(1 to 10000).par.reduce(_-_)
Scala Futures
Futures provide a nice way to reason about performing
many operations in parallel, in an efficient and non-
blocking way.
-- Scala Documentation
Scala Futures
Future Promise
Scala Futures
val p = Promise[T]
val f: Future[T] = p.future
Java Futures
val p = Promise[T]
val f: Future[T] = p.future
// Java-like variant:
doSomeWork()
f.get()
Efficient?
Efficient?
Also, Java Futures are NOT Composable
What We Want
Callbacks
Thread 1
val p = Promise[T]
val f: Future[T] = p.future
Thread 2
f onComplete {
case Success(calcResult) = println(calcResult)
case Failure(t) = println(“Error: “ + t.getMessage)
}
Callbacks
Thread 1
val p = Promise[T]
val f: Future[T] = p.future
Thread 2
f onComplete{
case Success(calcResult) = println(calcResult)
case Failure(t) = println(“Error: “ + t.getMessage)
}
Try
Try[T]
Success[T](value: T)
Failure[T](t: throwable)
Try
Try[T]
Success[T](value: T)
Failure[T](t: throwable)
Not Asynchronous
Try
val result: Try[int] = calcSomeValue
result match{
case Success(n) = println(“The result is “ + n)
case Failure(t) = println(“Error: “ + t.getMessage)
}
Monadic Combinators
val result: Int = calcSomeValue getOrElse -1
calcSomeValue.map(doSomethingWithVal).recover
(handleException)
val rateQuote = future {
service.getCurrentValue(USD)
}
val purchase = rateQuote map { quote =>
if (isProfitable(quote)) service.buy(amount, quote)
else throw new Exception("not profitable")
}
purchase onSuccess {
case _ => println("Purchased " + amount + " USD")
}
Future Composition
val usdQuote = future { service.getCurrentValue(USD) }
val chfQuote = future { service.getCurrentValue(CHF) }
val purchase = for {
usd <- usdQuote
chf <- chfQuote
if isProfitable(usd, chf)
} yield service.buy(amount, chf)
purchase onSuccess {
case _ => println("Purchased " + amount + " CHF")
}
Future Composition
def generate = future { (random, random) }
def calculate(x:(Double, Double)) = future{ pow(x._1, x._2) }
def filter(y:Any) = y.toString.contains("22222")
while (true) {
for {
i <- generate
j <- calculate(i)
if filter(j)
}
println(j)
}
Pipe Example