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:
parsparks the evaluation ofsum1in parallel.pseqensures thatsum2is 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 list2Explanation:
rparsparks a parallel computation.rseqensures sequential evaluation.runEvalruns 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) listSolution:
- Use
parListandrparto parallelize themapfunction.
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 30Solution:
- Use
parandpseqto 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
pseqto 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)
