.. _readme-tasks:

============
Chapel Tasks
============

.. contents::

--------
Overview
--------

Chapel programs create new tasks via the begin, cobegin, and coforall
statements.  Tasks are computations that can conceptually execute
concurrently, though they may or may not do so in practice.

An implementation of Chapel must include at least one tasking layer.
A tasking layer will in turn implement *threads* which are a mechanism
for executing work in parallel.

All tasking layers support configuration constants to control system
resources such as the number of threads that are available to execute
tasks and the amount of call stack space reserved for each task.  Generally
speaking, the Chapel programmer can make no assumptions about the
scheduling of threads or the mapping of tasks to threads other than
those semantics defined by the language specification.

This document describes the currently-supported tasking options in more
detail.  The rest of this document includes:

* an overview of the different tasking options
* a detailed description of each tasking option
* a discussion of the number of threads used by each tasking option
* a discussion of call stack sizes and overflow handling
* a list of tasking-related methods on the locale type
* a brief description of future directions for the tasking layer


--------------------------
Task Implementation Layers
--------------------------

This release contains two distinct tasking layers for Chapel tasks.
The user can select between these options by setting the ``CHPL_TASKS``
environment variable to one of the following values:

:qthreads:
  best performance; default for most targets

:fifo:
  most portable, but heavyweight; default for NetBSD and Cygwin

Each tasking layer is described in more detail below:


CHPL_TASKS == qthreads
----------------------

Chapel's default tasking layer implementation for most targets is based
on the Qthreads user-level threading package from Sandia National Labs.
This provides a lightweight implementation of Chapel tasking as well as
an optimized implementation of sync variables.  To use qthreads tasking,
please take the following steps:

1) Ensure that the environment variable ``CHPL_HOME`` points to the
   top-level Chapel directory.

2) Set up your environment to use Qthreads:

   ensure ``CHPL_TASKS`` is not set (if qthreads is the default)

   -- or --

   .. code-block:: sh

     export CHPL_TASKS=qthreads

3) Follow the instructions in :ref:`readme-chplenv` to set up,
   compile and run your Chapel programs.

Please report any apparent bugs in Qthreads tasking to the Chapel team.


Stack overflow detection
========================

The qthreads tasking implementation can arrange to halt programs when
any task overflows its call stack (see `Task Call Stacks`_).
It does this by placing a guard page, which cannot be referenced, at the
end of each task stack.  When a task tries to extend its stack onto a
guard page, it fails with a segfault.

Normally guard pages for stack overflow detection are configured and
enabled.  There is a performance cost for this, however.  We do not have
a quantitative estimate for this cost, but it is a fixed overhead (a
couple of system calls) added to the time needed to run every task, so
qualitatively speaking it will have a greater effect on programs which
create more or shorter-lived tasks than on programs which create fewer
or longer-lived ones.

As described in `Task Call Stacks`_, the execution-time default for
stack overflow checking can be set by using the ``--[no-]stack-checks``
compiler option.  But whatever the default is, at execution time stack
overflow detection can be turned off by setting the environment variable
``QT_GUARD_PAGES`` to any of the values "0", "no", or "false", or on by
setting it to any of "1", "yes", or "true".  When it is off the execution
overhead is negligible (just a couple of scalar tests).  Developers
who wish to remove even this small cost can disable guard pages by
building qthreads with guard pages entirely configured out, as follows:

.. code-block:: sh

  cd $CHPL_HOME/third-party/qthread
  make CHPL_QTHREAD_NO_GUARD_PAGES=yes ... clean all

As noted, running without guard pages can improve performance and thus
may be desirable for production work.  However, if this is done, test
runs at similar scale with guard pages turned on to check for stack
overflow should be done beforehand if possible, because undetected stack
overflows can cause subtle and intermittent errors in execution.


Environment variables
=====================

Qthreads provides a number of environment variables that can be used to
configure its behavior at execution time.  An introduction to these can
be found in the ENVIRONMENT section of the qthread_init man page.  (Note
that although this man page documents variables named ``QTHREAD_*``, each
variable is actually present in both ``QT_*`` and ``QTHREAD_*`` forms,
with the former superseding the latter.)  The qthreads man pages are
available by means of the man -M option, for example:

.. code-block:: sh

  man -M $CHPL_HOME/third-party/qthread/qthread-src/man qthread_init

Note that in some cases there are Chapel environment variables that
override Qthreads counterparts. ``CHPL_RT_NUM_THREADS_PER_LOCALE`` overrides
``QT_HWPAR``, for example.  Whenever a Chapel variable overrides a Qthreads
variable, you should use the Chapel one.


Worker affinity and number
==========================

Simplistically, there are two kinds of threads in Qthreads: shepherds
that manage work distribution, and workers that host qthreads (Chapel
tasks, for our purposes).  The execution-time environment variable
``QT_WORKER_UNIT`` controls how worker threads are distributed on hardware
processors.  The default is "core" to distribute workers across CPU
cores (physical processors).  An alternative is "pu", which distributes
workers across processing units.  These are instances of the processor
architecture, or hardware threads if the cores have those.  Note that
"pu" will be automatically selected if ``CHPL_RT_NUM_THREADS_PER_LOCALE``
is set to anything larger than the number of cores, so it usually isn't
necessary to set ``QT_WORKER_UNIT``.


.. _overloading-with-qthreads:

Overloading system nodes
========================

By default the qthreads tasking implementation is set up to assume that
its process is not competing with anything else for system resources
(CPUs and memory) on its system node.  In this mode, qthreads optimizes
its internal behavior to favor performance over load balancing.  This
works out well for Chapel programs, because normally Chapel runs with
one process (locale) per system node.  However, with ``CHPL_COMM=gasnet``
or ``CHPL_COMM=ofi`` one can run multiple Chapel locales on a single
system node, say for doing multilocale functional correctness testing
with limited system resources.  (See :ref:`readme-multilocale` for more
details.)  When this is done qthreads' optimization for performance can
greatly reduce performance, due to resource starvation among multiple
Chapel processes.  If you need qthreads to share system resources more
cooperatively with other processes set ``CHPL_RT_OVERSUBSCRIBED=yes`` at
execution time (see :ref:`oversubscribed-execution`).


Hwloc
=====

When ``CHPL_TASKS=qthreads``, the default for ``CHPL_HWLOC`` becomes "bundled",
and the hwloc third-party package will be built.  Qthreads depends on
this package to provide it with a description of the locale hardware, to
support locality and affinity operations.


Further information
===================

For more information on Qthreads, see $CHPL_HOME/third-party/README.


CHPL_TASKS == fifo
------------------

FIFO tasking over POSIX threads (or pthreads) works on all
platforms and is the default for Cygwin and NetBSD. It is
attractive in its portability, though on most platforms it will
tend to be heavier weight than Chapel strictly requires.  FIFO
tasking is also used when Chapel is configured in 'Quick Start'
mode (see :ref:`chapelhome-quickstart`).  To use FIFO tasking,
please take the following steps:

1) Ensure that the environment variable ``CHPL_HOME`` points to the
   top-level Chapel directory.

2) Set up your environment to use FIFO tasking:

   .. code-block:: sh

     export CHPL_TASKS=fifo

3) Follow the instructions in :ref:`readme-chplenv` to set up,
   compile and run your Chapel programs.

In the FIFO tasking implementation, Chapel tasks are mapped to threads
such that each task is executed by a single thread and is run to
completion before giving up that thread.  As a result, a program can
have no more tasks active (that is, created and started) at any given
time than it has threads on which to run those tasks.  It can create
more tasks than threads, but no more tasks will be run at any time
than there are threads.  Excess tasks are placed in a pool where they
will be picked up and started by threads as they complete their tasks.

The threading implementation uses POSIX threads (pthreads) to run Chapel
tasks.  Because pthreads are relatively expensive to create, it does not
destroy them when there are no tasks for them to execute.  Instead they
stay around and continue to check the task pool for tasks to execute.
Setting the number of pthreads is described in `Controlling the Number of Threads`_.


Stack overflow detection
========================

The fifo tasking implementation can arrange to halt programs when any
task overflows its call stack (see `Task Call Stacks`_).  It does
this by placing a guard page, which cannot be referenced, at the end of
each task stack.  When a task tries to extend its stack onto a guard
page, it fails with a segfault.

This feature is enabled in fifo tasking and cannot currently be turned
off.  There is a performance cost for it, which we expect to be small in
most cases.  We do not have a quantitative estimate for this cost, but
it is a fixed overhead (a couple of system calls) added to the time
needed to start each pthread.  Since the pthreads in fifo tasking are
long-lived and can host many tasks over their lifespan, on a per-task
basis we don't expect stack overflow detection to be expensive.


---------------------------------
Controlling the Number of Threads
---------------------------------

The number of threads per compute node used to implement a Chapel
program can be controlled by the ``CHPL_RT_NUM_THREADS_PER_LOCALE``
environment variable.  This may be set to either an explicit number
or one of the following symbolic strings:

  :'MAX_PHYSICAL': number of physical CPUs (cores) on the node
  :'MAX_LOGICAL':  number of logical CPUs (hyperthreads) on the node

If ``CHPL_RT_NUM_THREADS_PER_LOCALE`` is not set, the number of threads is
left up to the tasking layer.  See the case-by-case discussions below
for more details.

The Chapel program will generate an error if the requested number of
threads per locale is too large.  For example, when running multi-locale
programs, the GASNet communication layer typically places an upper bound
of 127 or 255 on the number of threads per locale (There are ways to
work around this assumption on certain platforms -- please contact us if
you need to do so, or peruse the GASNet documentation).

CHPL_TASKS == fifo
------------------
  The value of ``CHPL_RT_NUM_THREADS_PER_LOCALE`` indicates the maximum
  number of threads that the fifo tasking layer can create on each
  locale to execute tasks.  These threads are created on a demand-driven
  basis, so a program with a small number of concurrent tasks may never
  create the specified number.  If the value is zero, then the number of
  threads will be limited by system resources and other constraints
  (such as GASNet's configuration-time limit).

  The value of ``CHPL_RT_NUM_THREADS_PER_LOCALE`` can have a major impact on
  performance for fifo tasking.  For programs with few inter-task
  dependences and high computational intensity, setting it roughly equal
  to the number of physical CPUs on each locale can lead to near-optimal
  performance.  However, for programs with lots of fine-grained
  synchronization in which tasks frequently block on sync or single
  variables, ``CHPL_RT_NUM_THREADS_PER_LOCALE`` can often exceed the number
  of physical CPUs without an adverse effect on performance since
  blocked threads will not consume the CPU's cycles.

  Note that setting ``CHPL_RT_NUM_THREADS_PER_LOCALE`` too low can result in
  program deadlock for fifo tasking.  For example, for programs written
  with an assumption that some minimum number of tasks are executing
  concurrently, setting ``CHPL_RT_NUM_THREADS_PER_LOCALE`` lower than this
  can result in deadlock if there are not enough threads to implement
  all of the required tasks.

  When ``CHPL_RT_NUM_THREADS_PER_LOCALE`` is set, a warning is issued like:

  .. code-block:: text

    warning: Setting number of threads in CHPL_TASKS=fifo can lead to deadlock

  and can be suppressed with ``CHPL_RT_NUM_THREADS_PER_LOCALE_QUIET=yes``

CHPL_TASKS == qthreads
----------------------
  In the Qthreads tasking layer, ``CHPL_RT_NUM_THREADS_PER_LOCALE``
  specifies the number of system threads used to execute tasks.  The
  default is to use a number of threads equal to the number of physical
  CPUs on the locale.

----------------
Task Call Stacks
----------------

Each task including the main Chapel program has an associated call
stack.  As documented in :ref:`readme-executing`, the ``CHPL_RT_CALL_STACK_SIZE``
environment variable can be used to specify how big these call stacks
will be during execution.  See there for a full description of this
environment variable and the values it can take.

When a task's call chain becomes so deep that it needs more space than
the size of its call stack, stack overflow occurs.  Whether or not a
program checks for stack overflow checking at execution time can be
specified when it is compiled, via the ``--[no-]stack-checks`` compilation
option.  The compile-time default is ``--stack-checks``; ``--no-stack-checks``
can be given directly, and is also implied by ``--no-checks``, which in turn
is implied by ``--fast``.  By default stack overflow checks are enabled.

Chapel does not yet have a consistent, implementation-independent way to
deal with call stack overflow.  Each tasking layer implementation
handles stacks and stack overflow in its own way, as described below.

CHPL_TASKS == fifo
------------------
  In fifo tasking, Chapel tasks use their host pthreads' stacks when
  executing.  If stack checks are enabled, these stacks are created with
  an additional memory page called a "guard page" beyond their end, that
  is marked so that it cannot be referenced.  When stack overflow occurs
  the task's attempt to reference the guard page will cause the OS to
  react as it usually does when bad memory references are done.  On
  Linux, for example, it will kill the program with this message:

    Segmentation fault

  Unfortunately, many other things that cause improper memory references
  result in this same kind of program termination, so as a diagnostic it
  is ambiguous.  However, it does at least prevent the program from
  continuing on in an erroneous state.

CHPL_TASKS == qthreads
----------------------
  Like fifo tasks (see above), qthreads tasking can place guard pages
  beyond the ends of task stacks.  Stack overflow then results in the
  system's usual response to referencing memory that cannot be reached.
  With qthreads tasking, the compiler ``--stack-checks`` setting specifies
  the default setting for execution-time stack overflow checking.  Final
  control over stack overflow checks is provided by the ``QT_GUARD_PAGE``
  environment variable.  See the qthreads subsection of `Task
  Implementation Layers`_ for more information.

----------------------------
Quantifying Tasks on Locales
----------------------------

The locale type has a method available to query the number of tasks that
are running on a given locale.

  runningTasks()
    returns the number of tasks that have been created
    but have not yet finished.  Note that this number can exceed the
    number of threads because tasking layers may be capable of switching
    among multiple Chapel tasks running on a single hosting thread.

In order to use this method, you have to specify the locale you wish to
query, as in here.runningTasks(), where 'here' is the current locale.


-------------------------
Future Tasking Directions
-------------------------

As Chapel's task parallel implementation matures, we expect to have
multiple task->thread scheduling policies, from literally creating and
destroying new threads with each task (for programmers who want full
control over a thread's lifetime) to automated work stealing and load
balancing at the other end of the spectrum (for programmers who would
prefer not to manage threads or whose programs cannot trivially be
load balanced manually).  Our hope is to leverage existing open source
threading and task management software and to collaborate with others
in these areas, so please contact us if you'd
like to work with us in this area.
