Concurrency without Actors

Slides:



Advertisements
Similar presentations
7-Jun-14 Lists. Arrays and Lists Arrays are a fixed length and occupy sequential locations in memory This makes random access (for example, getting the.
Advertisements

Intro to Scala Lists. Scala Lists are always immutable. This means that a list in Scala, once created, will remain the same.
Computer Science 320 Clumping in Parallel Java. Sequential vs Parallel Program Initial setup Execute the computation Clean up Initial setup Create a parallel.
CSE 303 Lecture 16 Multi-file (larger) programs
Concurrency The need for speed. Why concurrency? Moore’s law: 1. The number of components on a chip doubles about every 18 months 2. The speed of computation.
Concurrency 101 Shared state. Part 1: General Concepts 2.
29-Jun-15 Java Concurrency. Definitions Parallel processes—two or more Threads are running simultaneously, on different cores (processors), in the same.
Concurrency - 1 Tasking Concurrent Programming Declaration, creation, activation, termination Synchronization and communication Time and delays conditional.
Scala Actors -Terrance Dsilva.  Thankfully, Scala offers a reasonable, flexible approach to concurrency  Actors aren’t a concept unique to Scala.
Scala Parallel Collections Aleksandar Prokopec EPFL.
Threads Concurrency in Java. What is mult-tasking? Doing more than one task.
Scala Parallel Collections Aleksandar Prokopec, Tiark Rompf Scala Team EPFL.
Data structures Abstract data types Java classes for Data structures and ADTs.
Built-in Data Structures in Python An Introduction.
Scala Parallel Collections Aleksandar Prokopec EPFL.
1 G53SRP: Clocks and Time in Java Chris Greenhalgh School of Computer Science.
Concurrency Control 1 Fall 2014 CS7020: Game Design and Development.
Concurrency and Performance Based on slides by Henri Casanova.
Concurrency in Java MD. ANISUR RAHMAN. slide 2 Concurrency  Multiprogramming  Single processor runs several programs at the same time  Each program.
Sequences and for loops. Simple for loops A for loop is used to do something with every element of a sequence scala> for (i
Mergesort example: Merge as we return from recursive calls Merge Divide 1 element 829.
1 Processor design Programming Language Design and Implementation (4th Edition) by T. Pratt and M. Zelkowitz Prentice Hall, 2001 Section 11.3.
Monitor Pattern Read only Collection views Synchronized Collections views Concurrent HashMap The Vehicle Tracker Example.
Chapter 2 Writing Simple Programs
CIS199 Test Review 2 REACH.
Parallel Patterns Reduce & Scan
Computer Programming Fundamentals
Chapter 19 Java Data Structures
Pointers and References
Functional Programming with Java
Java Collections Overview
Subroutines Idea: useful code can be saved and re-used, with different data values Example: Our function to find the largest element of an array might.
Arrays, For loop While loop Do while loop
Synchronization Lecture 23 – Fall 2017.
Introduction to LINQ Chapter 11 10/28/2015 Lect 4 CT1411.
Control Flow Chapter 6.
Functions As Objects.
CS190/295 Programming in Python for Life Sciences: Lecture 6
Java Programming Arrays
More on Thread Safety CSE451 Andrew Whitaker.
Parallel Computation Patterns (Scan)
Introduction to LINQ Chapter 11.
Distributed System Gang Wu Spring,2018.
Arrays and Collections
Parallel Computation Patterns (Reduction)
Java Concurrency 17-Jan-19.
int [] scores = new int [10];
ECE 498AL Lecture 15: Reductions and Their Implementation
Building Java Programs
Concurrency and Immutability
Processor design Programming Language Design and Implementation (4th Edition) by T. Pratt and M. Zelkowitz Prentice Hall, 2001 Section 11.3.
Introduction to Spark.
Fine grained, shared state
Algorithms: Design and Analysis
Data Structures & Algorithms
Java Concurrency.
NETWORK PROGRAMMING CNET 441
Java Concurrency.
Python Review
Parallelism Can we make it faster? 8-May-19.
Fine grained, shared state
Common Lisp II.
Data Parallel Pattern 6c.1
Java Concurrency 29-May-19.
Fine grained, shared state
Processor design Programming Language Design and Implementation (4th Edition) by T. Pratt and M. Zelkowitz Prentice Hall, 2001 Section 11.3.
Chapter 1: Creating a Program.
SPL – PS2 C++ Memory Handling.
Multidisciplinary Optimization
Threads and concurrency / Safety
Presentation transcript:

Concurrency without Actors Easy Concurrency Concurrency without Actors 20-Jul-18

Using the par method scala> val list = (1 to 10).toList list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> list.map(_ + 42) res1: List[Int] = List(43, 44, 45, 46, 47, 48, 49, 50, 51, 52) scala> list.par res2: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> list.par map(_ + 42) res3: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(43, 44, 45, 46, 47, 48, 49, 50, 51, 52) Does not make sense for collections smaller than several thousand.

Parallel collections scala.collection.parallel.mutable.ParArray scala.collection.parallel.mutable.ParHashMap scala.collection.parallel.mutable.ParHashSet scala.collection.parallel.mutable.ParTrieMap scala.collection.parallel.immutable.ParVector scala.collection.parallel.immutable.ParHashMap scala.collection.parallel.immutable.ParHashSet scala.collection.parallel.immutable.ParRange

map scala> val lastNames = List("Smith","Jones","Frankenstein","Bach","Jackson","Rodin").par lastNames: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Frankenstein, Bach, Jackson, Rodin) scala> lastNames.map(_.toUpperCase) res4: scala.collection.parallel.immutable.ParSeq[String] = ParVector(SMITH, JONES, FRANKENSTEIN, BACH, JACKSON, RODIN)

fold scala> val parArray = (1 to 10000).toArray.par parArray: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 2, 3, ... scala> parArray.fold(0)(_ + _) res5: Int = 50005000

filter scala> val lastNames = List("Smith","Jones","Frankenstein","Bach","Jackson", "Rodin").par lastNames: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Frankenstein, Bach, Jackson, Rodin) scala> lastNames.filter(_.head >= 'J') res6: scala.collection.parallel.immutable.ParSeq[String] = ParVector(Smith, Jones, Jackson, Rodin)

Making parallel collections Create a parallel collection directly (requires import) scala> val pv = ParVector(1, 2, 3, 4, 5) pv: scala.collection.parallel.immutable.ParVector[Int] = ParVector(1, 2, 3, 4, 5) Convert from a sequential collection (does not require import) scala> val pv = Vector(1,2,3,4,5).par pv: scala.collection.parallel.immutable.ParVector[Int] = ParVector(1, 2, 3, 4, 5)

Nondeterminism Side-effecting operations can lead to non-determinism scala> val list = (1 to 1000).toList.par list: scala.collection.parallel.immutable.ParSeq[Int] = ParVector(1, 2, 3, ... scala> var sum = 0 sum: Int = 0 scala> sum = 0; list.foreach(sum += _) sum: Int = 462929 scala> sum = 0; list.foreach(sum += _) sum: Int = 405997 scala> sum = 0; list.foreach(sum += _) sum: Int = 472645

More nondeterminism Non-associative operations lead to non-determinism scala> list.reduce(_-_) res12: Int = -28400 scala> list.reduce(_-_) res13: Int = 49740 scala> list.reduce(_-_) res14: Int = -112438

Associative operations are okay Non-commutative (but associative) operations are okay scala> val strings = "abc def ghi jk lmnop qrs tuv wx yz".split(" ").par strings: scala.collection.parallel.mutable.ParArray[String] = ParArray(abc, def, ghi, jk, lmnop, qrs, tuv, wx, yz) scala> val alphabet = strings.reduce(_ ++ _) alphabet: String = abcdefghijklmnopqrstuvwxyz

Parallel arrays def tabulate[A](end: Int)(f: (Int) ⇒ A): Iterator[A] Creates an iterator producing the values of a given function over a range of integer values starting from 0. scala> import scala.collection.parallel.mutable.ParArray scala> val pa = ParArray.range(0, 10) pa: scala.collection.parallel.mutable.ParArray[Int] = ParArray(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) scala> val pa = ParArray.tabulate(10)(x => x + 100) pa: scala.collection.parallel.mutable.ParArray[Int] = ParArray(100, 101, 102, 103, 104, 105, 106, 107, 108, 109) Putting the array together again is a sequential process, and may be the slowest part of the operation

Some array types Array Same as a Java array, fast for most operations. Scala extends this with ArrayOps, which is full of goodies such as map and filter--but these can be slow for arrays of primitives, as they require boxing and unboxing. WrappedArray Same as a Java array of objects; all operations can be used, but boxing/unboxing is unnecessary, as the result is another WrappedArray. ArraySeq Same as a Java array of objects; primitives are boxed going in and unboxed coming out, but remain boxed for all intermediate operations. ArrayBuffer Acts like an array, but elements can be added or removed.Array ParArray An array that can be operated on in parallel. The .seq method on a ParArray returns an ArraySeq.

ParVector scala> val pv = ParVector.range(0, 10) pv: scala.collection.parallel.immutable.ParVector[Int] = ParVector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) scala> val pv = ParVector.tabulate(10)(x => x + 100) pv: scala.collection.parallel.immutable.ParVector[Int] = ParVector(100, 101, 102, 103, 104, 105, 106, 107, 108, 109)

Other types ParVector Fast indexing, fixed size. .seq returns a Vector. ParRange Example: 1 to 10 par .seq returns a Range. ParHashSet Both mutable and immutable varieties. .seq returns a HashSet. ParHashMap .seq returns a HashMap. concurrent.TrieMap A concurrent thread-safe map. Okay to modify during traversal; updates are only visible in the next iteration.

Durations import scala.concurrent.duration._ DurationConversions (included in this package) defines the following methods on numeric types, both integral and real: day days hour hours minute minutes second seconds millisecond milliseconds milli millis microsecond microseconds micro micros nanosecond nanoseconds nano nanos Examples: scala> 3.7 seconds res8: scala.concurrent.duration.FiniteDuration = 3700 milliseconds scala> Math.PI seconds res9: scala.concurrent.duration.FiniteDuration = 3141592654 nanoseconds These methods are optimized for concurrency and are not intended for general use Maximum Duration is about 292 years

Simple Futures import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global A Future is a computation that runs concurrently, in a separate Thread The second import above imports the “default global execution context,” which provides the Threads—it is like a Thread Pool Once you have the above imports, actually creating a Future is dead simple: val f = Future { some long slow computation } This sets f to be a Future object, which will (hopefully) at some later time hold a result

Blocking on Futures One way to get the result out of a Future is to block and wait for it import scala.concurrent.Await val r = Await.result(f, 1 second) Futures have time limits: scala> { val f = Future { | Thread.sleep(50) | 42 | } | val r = Await.result(f, 40 millis) | println(r) | } java.util.concurrent.TimeoutException: Futures timed out after [40 milliseconds]

Blocking is bad The more blocking, the less parallelism, and the less speedup The default execution context only allocates as many threads as you have processors If more threads than that are blocked, computation is deadlocked There are ways to avoid this, but it’s best simply to avoid blocking Only block if nothing more can be done until you have the result of the Future

Getting a result without blocking scala> { val f = Future { | Thread.sleep(50) | 42 | } | println("Before") | f.onComplete { | case Success(value) => println(value) | case Failure(e) => println("Too bad!") | } | println("After") | } Before After scala> 42 The onComplete sets up a callback From this you can see that it also runs in a separate Thread Callbacks can happen in any order, and may not be in the same Thread

Using separate callbacks val f = Future { … } f onSuccess { case result => println(s"Success: $result") } onFailure { case t => println(s"Exception: ${t.getMessage}") } Notice that for all of these (onComplete, onSuccess, onFailure), the result is a partial function This is like the body of a match expression, which is also a partial function

Methods that return Futures def longRunningComputation(i: Int): Future[Int] = Future { sleep(100) i + 1 } // this does not block longRunningComputation(11).onComplete { case Success(result) => println(s"result = $result") case Failure(e) => e.printStackTrace } // important: keep the jvm from shutting down sleep(1000)

Combining Futures scala> val f1 = Future { 42} f1: scala.concurrent.Future[Int] = Success(42) val f2 = Future {10000} f2 : scala.concurrent.Future[Int] = List() val fsum = f1.onSuccess { case x => f2.onSuccess { case y => x + y } }

Reference This entire slide set is pretty much a direct translation from http://docs.scala-lang.org/overviews/parallel-collections/overview.html

The End