Objectives
- Gain an understanding of the importance of concurrency
- Understand how newer languages such as elixir provide features to support concurrent programming
Introduction
Concurrent programming, where a program or programs run multiple threads of execution in parallel, is nothing new. The concepts behind it go back decades, as Operating Systems researchers will atest to. But many programs (and programmers) to this day do not fully exploit the benefits of multithreading due to it's percieved complexity in implementation and debugging.
However, as we approach the end of Moore's law there is a new immpetus to find techniques to keep up with the ever expanding amount of data and performance we expect from modern technology. Languages like Elixir make what is old new again and try to provide tools for programmers to tame concurrency and it's percieved complexity.
Elixir
Work through the Elixir introduction section on processes
Expressions
Open up iex and type the following expressions:
iex> x=spawn(fn -> receive do {pid,x} -> send(pid,x+1) end end) iex> send x,{self(),3} iex> flush() iex> send x,{self(),3} iex> flush()
Lab questions
-
write a module with a
start/0
method that spawns and returns a process that when sent a tuple (as above), sends back the next number, but that remains active so you can send it multiple messages, and get responses. -
write a module with a
start/0
method that spawns and returns a process that when sent a tuple (as above), sends back the cumulative sum, that remains active so you can send it multiple messages, and get responses. If you sent it the messagesend x,{self(),3}
3 times, you would get3
,6
, and9
sent back to you. - This is a parallel map function:
def pmap(collection, fun) do me = self() collection |> Enum.map(fn (elem) -> spawn_link fn -> (send me, { self(), fun.(elem) }) end end) |> Enum.map(fn (pid) -> receive do { ^pid, result } -> result end end) end
Experiment with it versus theEnum.map
function. Define a factorial function and map it over the sequence1..100
. Use Enum.take and Enum.drop to examine portions of the list and check if map and pmap perform the same. - This is a timer function:
def measure(function) do function |> :timer.tc |> elem(0) |> Kernel./(1_000_000) end
You can use it likemeasure fn -> fact 1000 end
. Experiment with timing use of pmap and map (factorials of2000..3000
is probably a reasonable test. Explain any differences between execution time. Can you infer how many processors your computer has?