5.4. Using Concurrent Haskell — Glasgow Haskell Compiler 9.10.1 User's Guide (2024)

  • 5. Using GHC
  • 5.4. Using Concurrent Haskell
  • View page source

GHC supports Concurrent Haskell by default, without requiring a specialoption or libraries compiled in a certain way. To get access to thesupport libraries for Concurrent Haskell, just importControl.Concurrent. More information on Concurrent Haskell isprovided in the documentation for that module.

Optionally, the program may be linked with the -threaded option (seeOptions affecting linking. This provides two benefits:

  • It enables the -N ⟨x⟩ to be used, which allows threads to run inparallel on a multi-processor or multi-core machine. See Using SMP parallelism.
  • If a thread makes a foreign call (and the call is not markedunsafe), then other Haskell threads in the program will continueto run while the foreign call is in progress. Additionally,foreign exported Haskell functions may be called from multipleOS threads simultaneously. See Multi-threading and the FFI.

The following RTS option(s) affect the behaviour of Concurrent Haskellprograms:

-C ⟨s⟩
Default:20 milliseconds

Sets the context switch interval to ⟨s⟩ seconds.A context switch will occur at the next heap block allocation afterthe timer expires (a heap block allocation occurs every 4k ofallocation). With -C0 or -C, context switches will occur asoften as possible (at every heap block allocation).

GHC supports running Haskell programs in parallel on an SMP (symmetricmultiprocessor).

There’s a fine distinction between concurrency and parallelism:parallelism is all about making your program run faster by making useof multiple processors simultaneously. Concurrency, on the other hand,is a means of abstraction: it is a convenient way to structure a programthat must respond to multiple asynchronous events.

However, the two terms are certainly related. By making use of multipleCPUs it is possible to run concurrent threads in parallel, and this isexactly what GHC’s SMP parallelism support does. But it is also possibleto obtain performance improvements with parallelism on programs that donot use concurrency. This section describes how to use GHC to compileand run parallel programs, in Parallel and Concurrent we describe thelanguage features that affect parallelism.

5.5.1. Compile-time options for SMP parallelism

In order to make use of multiple CPUs, your program must be linked withthe -threaded option (see Options affecting linking). Additionally, thefollowing compiler options affect parallelism:

-feager-blackholing

Blackholing is the act of marking a thunk (lazy computation) asbeing under evaluation. It is useful for three reasons: firstly itlets us detect certain kinds of infinite loop (theNonTermination exception), secondly it avoids certain kinds ofspace leak, and thirdly it avoids repeating a computation in aparallel program, because we can tell when a computation is alreadyin progress.

The option -feager-blackholing causes each thunk to beblackholed as soon as evaluation begins. The default is “lazyblackholing”, whereby thunks are only marked as being underevaluation when a thread is paused for some reason. Lazy blackholingis typically more efficient (by 1-2% or so), because most thunksdon’t need to be blackholed. However, eager blackholing can avoidmore repeated computation in a parallel program, and this oftenturns out to be important for parallelism.

We recommend compiling any code that is intended to be run inparallel with the -feager-blackholing flag.

5.5.2. RTS options for SMP parallelism

There are two ways to run a program on multiple processors: callControl.Concurrent.setNumCapabilities from your program, oruse the RTS -N ⟨x⟩ options.

-N ⟨x⟩
-N
-maxN ⟨x⟩

Use ⟨x⟩ simultaneous threads when running the program.

The runtime manages a set of virtual processors, which we callcapabilities, the number of which is determined by the -Noption. Each capability can run one Haskell thread at a time, so thenumber of capabilities is equal to the number of Haskell threadsthat can run physically in parallel. A capability is animated by oneor more OS threads; the runtime manages a pool of OS threads foreach capability, so that if a Haskell thread makes a foreign call(see Multi-threading and the FFI) another OS thread can take over thatcapability.

Normally ⟨x⟩ should be chosen to match the number of CPU cores onthe machine [1]. For example, on a dual-core machine we wouldprobably use +RTS -N2 -RTS.

Omitting ⟨x⟩, i.e. +RTS -N -RTS, lets the runtime choose thevalue of ⟨x⟩ itself based on how many processors are in yourmachine.

Omitting -N⟨x⟩ entirely means -N1.

With -maxN⟨x⟩, i.e. +RTS -maxN3 -RTS, the runtime will chooseat most (x), also limited by the number of processors on the system.Omitting (x) is an error, if you need a default use option -N.

Be careful when using all the processors in your machine: if some ofyour processors are in use by other programs, this can actually harmperformance rather than improve it. Asking GHC to create more capabilitiesthan you have physical threads is almost always a bad idea.

Setting -N also has the effect of enabling the parallel garbagecollector (see RTS options to control the garbage collector).

The current value of the -N option is available to the Haskellprogram via Control.Concurrent.getNumCapabilities, and it may bechanged while the program is running by callingControl.Concurrent.setNumCapabilities.

The following options affect the way the runtime schedules threads onCPUs:

-qa

Use the OS’s affinity facilities to try to pin OS threads to CPUcores.

When this option is enabled, the OS threads for a capability \(i\) arebound to the CPU core \(i\) using the API provided by the OS for settingthread affinity. e.g. on Linux GHC uses sched_setaffinity().

Depending on your workload and the other activity on the machine,this may or may not result in a performance improvement. Werecommend trying it out and measuring the difference.

-qm

Disable automatic migration for load balancing. Normally the runtimewill automatically try to schedule threads across the available CPUsto make use of idle CPUs; this option disables that behaviour. Notethat migration only applies to threads; sparks created by parare load-balanced separately by work-stealing.

This option is probably only of use for concurrent programs thatexplicitly schedule threads onto CPUs withControl.Concurrent.forkOn.

5.5.3. Hints for using SMP parallelism

Add the -s [⟨file⟩] RTS option when running the program to seetiming stats, which will help to tell you whether your program got faster byusing more CPUs or not. If the user time is greater than the elapsed time, thenthe program used more than one CPU. You should also run the program without-N ⟨x⟩ for comparison.

The output of +RTS -s tells you how many “sparks” were created andexecuted during the run of the program (see RTS options to control the garbage collector),which will give you an idea how well your par annotations areworking.

GHC’s parallelism support has improved in 6.12.1 as a result of muchexperimentation and tuning in the runtime system. We’d still beinterested to hear how well it works for you, and we’re also interestedin collecting parallel programs to add to our benchmarking suite.

[1]Whether hyperthreading cores should be counted or not is an openquestion; please feel free to experiment and let us know what results youfind.
5.4. Using Concurrent Haskell — Glasgow Haskell Compiler 9.10.1 User's Guide (2024)

References

Top Articles
Latest Posts
Article information

Author: Dong Thiel

Last Updated:

Views: 5503

Rating: 4.9 / 5 (59 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Dong Thiel

Birthday: 2001-07-14

Address: 2865 Kasha Unions, West Corrinne, AK 05708-1071

Phone: +3512198379449

Job: Design Planner

Hobby: Graffiti, Foreign language learning, Gambling, Metalworking, Rowing, Sculling, Sewing

Introduction: My name is Dong Thiel, I am a brainy, happy, tasty, lively, splendid, talented, cooperative person who loves writing and wants to share my knowledge and understanding with you.