[Multiple cores Bryan O'Sullivan **20080528065230] { addfile ./examples/ch25/NumCapabilities.hs hunk ./en/ch25-concurrent.xml 474 - programming in other languages apply equally to Haskell. + programming in other languages apply equally to Haskell. Two of + the better known problems are deadlock and + starvation. hunk ./en/ch25-concurrent.xml 480 - A common problem with concurrent code is - deadlock, in which two or more threads - get stuck forever due to a clash over access to shared + + In a deadlock situation, two or more + threads get stuck forever in a clash over access to shared hunk ./en/ch25-concurrent.xml 507 - In real code, these kinds of inversion problems are much - more difficult to spot. The taking of MVars is - often spread across several functions in different files, - making visual inspection more tricky. Worse, these problems - are often intermittent, which makes them - tricky to even reproduce, never mind isolate and fix. + Across languages, the usual way to solve an order + inversion problem is to always follow a consistent order when + acquiring resources. Since this approach requires manual + adherence to a coding convention, it is easy to miss in + practice. + + To make matters more complicated, these kinds of inversion + problems can be difficult to spot in real code. The taking of + MVars is often spread across several functions in + different files, making visual inspection more tricky. Worse, + these problems are often intermittent, + which makes them tricky to even reproduce, never mind isolate + and fix. hunk ./en/ch25-concurrent.xml 602 + + Using multiple cores with GHC + + By default, &GHC; generates programs that use just one core, + even when we write explicitly concurrent code. To use multiple + cores, we must explicitly choose to do so. We make this choice + at link time, when we are generating an + executable program. + + + + The non-threaded runtime library runs all + Haskell threads in a single operating system thread. This + runtime is highly efficient for creating threads and passing + data around in MVars. + + + The threaded runtime library uses + multiple operating system threads to run Haskell threads. + It has somewhat more overhead for creating threads and using + MVars. + + + + If we pass the option to the + compiler, it will link our program against the threaded runtime + library. We do not need to use when + we are compiling libraries or source files, only when we are + finally generating an executable. + + Even when we select the threaded runtime for our program, + it will still default to using only one core when we run it. We + must explicitly tell the runtime how many cores to use. + + + Runtime options + + We can pass options to &GHC;'s runtime system on the + command line of our program. Before handing control to our + code, the runtime scans the program's arguments for the + special command line option . It + interprets everything that follows, until the special option + , as an option for the runtime system, + not our program. It hides all of these options from our code. + When we use the System.Environment module's + getArgs function to obtain our command + line arguments, we will not find any runtime options in the + list. + + The threaded runtime accepts an option + + The non-threaded runtime does not understand this + option, and will reject it with an error message. + . This takes one argument, which specifies the + number of cores that &GHC;'s runtime system should use. The + option parser is picky: there must be no spaces between + and the number that follows it. The option + is acceptable, but + is not. + + + + Finding the number of available cores from Haskell + + The module GHC.Conc exports a variable, + numCapabilities, that tells us how many + cores the runtime system can use. + + &NumCapabilities.hs:main; + + If we compile and run the above program, we can see that + the options to the runtime system are not visible to the + program, but that it can see how many cores it can run + on. + + $ ghc -c NumCapabilities.hs +$ ghc -threaded -o NumCapabilities NumCapabilities.o +$ ./NumCapabilities +RTS -N4 -RTS foo +command line arguments: ["foo"] +number of cores: 4 + + + + Choosing the right runtime + + The decision of which runtime to use is not completely + clear cut. While the threaded runtime can use multiple cores, + it has a cost: threads and sharing data between them are more + expensive than with the non-threaded runtime. + + Furthermore, the garbage collector used by &GHC; as of + version 6.8.2 is single threaded: it pauses all other threads + while it runs, and executes on one core. This limits the + performance improvement we can hope to see from using multiple + cores. (As we write this book, the garbage collector is being + retooled to use multiple cores, but we cannot yet predict its + future effect.) + + In many real world concurrent programs, an individual + thread will spend most of its time waiting for a network + request or response. In these cases, if a single Haskell + program serves tens of thousands of concurrent clients, the + lower overhead of the non-threaded runtime may be helpful. For + example, instead of having a single server program use the + threaded runtime on four cores, we might see better + performance if we design our server so that we can run four + copies of it simultaneously, and use the non-threaded + runtime. + + 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. + + + + + Parallel programming in Haskell + + Foo. + hunk ./examples/ch25/NumCapabilities.hs 1 +{-- snippet main --} +import GHC.Conc (numCapabilities) +import System.Environment (getArgs) + +main = do + args <- getArgs + putStrLn $ "command line arguments: " ++ show args + putStrLn $ "number of cores: " ++ show numCapabilities +{-- /snippet main --} }