[Chapter 25 done Bryan O'Sullivan **20080814051018] { hunk ./en/ch05-fp.xml 2232 + + + + Normal form and head normal form + + The seq function evaluates an + expression to what we call head normal + form (HNF): it stops once it reaches the + outermost constructor (the head). This is + distinct from normal form (NF), in which + an expression is completely evaluated. + + You may also hear Haskell programmers refer to + weak head normal form (WHNF). In most cases, + weak head normal form is identical to head normal form. The + difference is abstruse, and only arises in relation to + functions. For normal data, the two are the same. hunk ./en/ch25-concurrent.xml 12 - A concurrent program needs to perform - several possibly unrelated tasks at the same time. Consider the - example of a virtual world: a server is typically composed of - dozens of components, each of which has complicated interactions - with the outside world. One component might handle multi-user - chat; another with evaluating scripts attached to objects; while - another processes monetary transactions. + A concurrent program needs + to perform several possibly unrelated tasks at the same time. + Consider the example of a game server: it is typically composed + of dozens of components, each of which has complicated + interactions with the outside world. One component might handle + multi-user chat; several more will process the inputs of + players, and feed state updates back to them; while another + performs physics calculations. hunk ./en/ch25-concurrent.xml 25 - In contrast, a parallel program solves - a single problem. Consider a financial model that attempts to - predict the next minute of fluctuations in the price of a single - stock. If we want to apply this model to every stock listed on - an exchange, to estimate which ones we should buy and sell, we - hope to get an answer more quickly if we run the model on five - hundred cores than if we use just one. (As this suggests, a - parallel program does not usually depend on the presence of - multiple cores to work correctly.) + In contrast, a parallel + program solves a single problem. Consider a financial model + that attempts to predict the next minute of fluctuations in the + price of a single stock. If we want to apply this model to + every stock listed on an exchange, for example to estimate which + ones we should buy and sell, we hope to get an answer more + quickly if we run the model on five hundred cores than if we use + just one. As this suggests, a parallel program does not usually + depend on the presence of multiple cores to work + correctly. hunk ./en/ch25-concurrent.xml 44 + + Many traditional languages further blur the already + indistinct boundary between concurrent and parallel + programming, because they force programmers to use the same + primitives to construct both kinds of program. + + hunk ./en/ch25-concurrent.xml 73 - thread that created it continues to execute concurrently. + thread that created it continues to execute concurrently. The + thread will stop executing when it reaches the end of its &IO; + action. hunk ./en/ch25-concurrent.xml 105 - The use of flip catch print gives us a cheap + The use of handle print gives us a cheap hunk ./en/ch25-concurrent.xml 133 - use for this purpose. + use to create this capability for ourselves. hunk ./en/ch25-concurrent.xml 463 + + The ! pattern above is simple to use, but it + is not always sufficient to ensure that our data is + evaluated. For a more complete approach, see below. hunk ./en/ch25-concurrent.xml 684 - cores the runtime system can use. + cores the runtime system has been given with the + RTS option. hunk ./en/ch25-concurrent.xml 710 - version 6.8.2 is single threaded: it pauses all other threads + version 6.8.3 is single threaded: it pauses all other threads hunk ./en/ch25-concurrent.xml 713 - cores. (As we write this book, the garbage collector is being - retooled to use multiple cores, but we cannot yet predict its - future effect.) + cores + As we write this book, the garbage collector is being + retooled to use multiple cores, but we cannot yet predict + its future effect. + . hunk ./en/ch25-concurrent.xml 730 - Our purpose here is not to dissuade you from using the - threaded runtime. It is not much more expensive than the - non-threaded runtime: threads remain amazingly cheap compared - to most other programming languages. We merely want to make - it clear that switching to the threaded runtime will not - necessarily result in an automatic win. + Our purpose here is not to dissuade you from + using the threaded runtime. It is not much more expensive + than the non-threaded runtime: threads remain amazingly cheap + compared to the runtimes of most other programming languages. + We merely want to make it clear that switching to the threaded + runtime will not necessarily result in an automatic + win. hunk ./en/ch25-concurrent.xml 763 + + Normal form and head normal form + + The familiar seq function evaluates + an expression to what we call head normal + form (abbreviated HNF). It stops once it reaches + the outermost constructor (the head). This is + distinct from normal form (NF), in which + an expression is completely evaluated. + + You will also hear Haskell programmers refer to + weak head normal form (WHNF). For normal + data, weak head normal form is the same as head normal form. + The difference only arises for functions, and is too abstruse + to concern us here. + + hunk ./en/ch25-concurrent.xml 833 - The par function is provided by the - Control.Parallel module. It serves a similar - purpose to seq: it evaluates its left - argument to weak head normal form (WHNF), and returns its - right. As its name suggests, it can evaluate its left argument - in parallel with whatever other evaluations are - occurring. + The par function is + provided by the Control.Parallel module. It + serves a similar purpose to seq: it + evaluates its left argument to weak head normal form, and + returns its right. As its name suggests, + par can evaluate its left argument in + parallel with whatever other evaluations are occurring. hunk ./en/ch25-concurrent.xml 841 - As for pseq, it is similar to - seq: it evaluates the expression on the - left to WHNF before returning the expression on the right. The - difference between the two is subtle, but important for + As for pseq, it is similar + to seq: it evaluates the expression on + the left to WHNF before returning the expression on the right. + The difference between the two is subtle, but important for hunk ./en/ch25-concurrent.xml 848 - right argument first would improve performance. This lax - guarantee is fine for a program executing on one core, but it - is not strong enough for code running on multiple cores. In - contrast, the compiler guarantees that + right argument first would improve performance. This + flexibility is fine for a program executing on one core, but + it is not strong enough for code running on multiple cores. + In contrast, the compiler guarantees that hunk ./en/ch25-concurrent.xml 914 - Notice that we don't care what's in the list; we just hand - each element off to seq. There is - clearly no magic involved here: we are just using our usual - understanding of Haskell's evaluation model. And because we - will be using force on the left hand side - of par or pseq, we - don't need to return a meaningful value. + Notice that we don't care what's in the list; + we walk down its spine to the end, then use + pseq once. There is clearly no magic + involved here: we are just using our usual understanding of + Haskell's evaluation model. And because we will be using + force on the left hand side of + par or pseq, we + don't need to return a meaningful value. + + Of course, in many cases we will need to force the + evaluation of individual elements of the list, too. Below, + we will discuess a typeclass-based solution to this + problem. hunk ./en/ch25-concurrent.xml 948 - This lax guarantee in turn affects how we write parallel - code. Since par is going to be somewhat + This lax specification in turn affects how we write parallel + code. Since par may be somewhat hunk ./en/ch25-concurrent.xml 1003 - evaluation. This woul evaluate the + evaluation. This wouls evaluate the hunk ./en/ch25-concurrent.xml 1019 - mainabove: you should find that the + main above: you should find that the hunk ./en/ch25-concurrent.xml 1163 - The garbage collector in release 6.8.2 of &GHC; has a - bug that can cause programs using par - to crash. If you want to use par and - you are using 6.8.2, we suggest upgrading to a newer - release. + The garbage collector in release 6.8.2 of + &GHC; has a bug that can cause programs using + par to crash. If you want to use + par and you are using 6.8.2, we + suggest upgrading to at least 6.8.3. hunk ./en/ch25-concurrent.xml 1210 - both huge and plentiful. + both huge and plentiful + The genesis of this idea comes from Tim Bray. + . hunk ./en/ch25-concurrent.xml 1275 - evaluated. + evaluated to WHNF. hunk ./en/ch25-concurrent.xml 1284 - to normal form. The only place that it makes any sense to + to head normal form. The only place that it makes any sense to hunk ./en/ch25-concurrent.xml 1298 - The Control.Parallel.Strategies module turns - this idea into something we can use as a library. It - introduces the idea of an evaluation - strategy. + The Control.Parallel.Strategies + module generalizes this idea into something we can use as a + library. It introduces the idea of an evaluation + strategy. hunk ./en/ch25-concurrent.xml 1451 - Lazy I/O has a few well known risks that we would like - to avoid. + Lazy I/O poses a few well known hazards that + we would like to avoid. hunk ./examples/ch25/Compressor.hs 3 +import Control.Exception (handle) hunk ./examples/ch25/Compressor.hs 11 -main = loop - -loop = do +main = do hunk ./examples/ch25/Compressor.hs 17 - flip catch print $ do + handle print $ do hunk ./examples/ch25/Compressor.hs 21 - loop + main hunk ./examples/ch25/ParMap.hs 7 +parallelMap _ _ = [] hunk ./examples/ch25/ParMap.hs 12 -forceList (x:xs) = x `seq` forceList xs +forceList (x:xs) = x `pseq` forceList xs hunk ./examples/ch25/Sorting.hs 41 -force (x:xs) = x `pseq` force xs -force _ = () +force xs = go xs `pseq` () + where go (_:xs) = go xs + go [] = 1 }