Parallelism in Haskell allows you to perform multiple computations simultaneously, leveraging multi-core processors to improve performance. This module will cover the basics of parallelism, including key concepts, practical examples, and exercises to help you understand and implement parallelism in Haskell.
Key Concepts
-
Parallelism vs. Concurrency:
- Parallelism: Performing multiple computations at the same time.
- Concurrency: Structuring a program to handle multiple tasks that may not necessarily run simultaneously.
-
Strategies:
- Strategies are a way to control the evaluation of parallel computations in Haskell.
-
Par and pseq:
par
: Sparks a parallel computation.pseq
: Ensures that one computation is evaluated before another.
-
Parallel Libraries:
Control.Parallel
: Basic parallelism constructs.Control.Parallel.Strategies
: Higher-level strategies for parallelism.
Practical Examples
Example 1: Basic Parallelism with par
and pseq
import Control.Parallel (par, pseq) -- A simple function to compute the sum of two lists in parallel parallelSum :: [Int] -> [Int] -> Int parallelSum xs ys = let sum1 = sum xs sum2 = sum ys in sum1 `par` (sum2 `pseq` (sum1 + sum2)) main :: IO () main = do let list1 = [1..1000000] list2 = [1000001..2000000] print $ parallelSum(list1, list2)
Explanation:
par
sparks the evaluation ofsum1
in parallel.pseq
ensures thatsum2
is evaluated before combining the results.
Example 2: Using Strategies
import Control.Parallel.Strategies (rpar, rseq, runEval) -- A function to compute the sum of two lists using strategies parallelSumStrategies :: [Int] -> [Int] -> Int parallelSumStrategies xs ys = runEval $ do sum1 <- rpar (sum xs) sum2 <- rseq (sum ys) rseq sum1 return (sum1 + sum2) main :: IO () main = do let list1 = [1..1000000] list2 = [1000001..2000000] print $ parallelSumStrategies list1 list2
Explanation:
rpar
sparks a parallel computation.rseq
ensures sequential evaluation.runEval
runs the parallel computations and returns the result.
Exercises
Exercise 1: Parallel Map
Implement a parallel version of the map
function.
import Control.Parallel.Strategies (parList, rpar, using) parallelMap :: (a -> b) -> [a] -> [b] parallelMap f xs = map f xs `using` parList rpar main :: IO () main = do let list = [1..1000000] print $ parallelMap (+1) list
Solution:
- Use
parList
andrpar
to parallelize themap
function.
Exercise 2: Parallel Fibonacci
Compute the nth Fibonacci number in parallel.
import Control.Parallel (par, pseq) fib :: Int -> Int fib 0 = 0 fib 1 = 1 fib n = let x = fib (n-1) y = fib (n-2) in x `par` (y `pseq` (x + y)) main :: IO () main = print $ fib 30
Solution:
- Use
par
andpseq
to parallelize the Fibonacci computation.
Common Mistakes and Tips
- Overhead: Parallelism introduces overhead. Ensure that the computations are large enough to benefit from parallelism.
- Granularity: Too fine-grained parallelism can lead to inefficiency. Balance the granularity of tasks.
- Lazy Evaluation: Haskell's lazy evaluation can interfere with parallelism. Use
pseq
to force evaluation order when necessary.
Conclusion
In this section, you learned about parallelism in Haskell, including key concepts, practical examples, and exercises. You now have the tools to implement parallel computations in Haskell, leveraging multi-core processors to improve performance. In the next section, we will explore the Foreign Function Interface (FFI) in Haskell.
Haskell Programming Course
Module 1: Introduction to Haskell
- What is Haskell?
- Setting Up the Haskell Environment
- Basic Syntax and Hello World
- Haskell REPL (GHCi)