From 9f7421ad25e3966f04475f56e49280ba69f55764 Mon Sep 17 00:00:00 2001 From: Henry Wright Date: Fri, 10 Mar 2023 11:06:28 +0000 Subject: [PATCH 1/7] Adding a Dask best practices section to the user guide --- .../dask_bags_and_greed.rst | 228 +++++++++++++++++ .../dask_parallel_loop.rst | 169 +++++++++++++ .../dask_best_practices/dask_pp_to_netcdf.rst | 92 +++++++ .../images/grib-bottleneck.png | Bin 0 -> 63951 bytes .../loop_third_party_kapture_results.png | Bin 0 -> 84484 bytes .../dask_best_practices/index.rst | 230 ++++++++++++++++++ docs/src/userguide/index.rst | 1 + docs/src/whatsnew/latest.rst | 4 + 8 files changed, 724 insertions(+) create mode 100644 docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst create mode 100644 docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst create mode 100644 docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst create mode 100644 docs/src/further_topics/dask_best_practices/images/grib-bottleneck.png create mode 100644 docs/src/further_topics/dask_best_practices/images/loop_third_party_kapture_results.png create mode 100644 docs/src/further_topics/dask_best_practices/index.rst diff --git a/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst b/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst new file mode 100644 index 0000000000..465a30c40e --- /dev/null +++ b/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst @@ -0,0 +1,228 @@ +.. _examples_bags_greed: + +3. Dask Bags and Greedy Parallelism +----------------------------------- + +Here is a journey that demonstrates: + +* How to apply dask.bags to an existing script +* The equal importance of optimisation of non-parallel parts of a script +* Protection against multiple softwares trying to manage parallelism + simultaneously + + +3.1 The Problem - Slow Loading +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +We have ~7000 GRIB files spread between 256 dated directories:: + + . + |-- 20180401 + | |-- gfs.t00z.icing.0p25.grb2f006 + | |-- gfs.t00z.icing.0p25.grb2f006.1 + | |-- gfs.t00z.icing.0p25.grb2f012 + | |-- gfs.t00z.icing.0p25.grb2f018 + | |-- gfs.t00z.icing.0p25.grb2f024 + | |-- gfs.t00z.icing.0p25.grb2f030 + | `-- gfs.t00z.icing.0p25.grb2f036 + |-- 20180402 + | `-- gfs.t00z.icing.0p25.grb2f006 + |-- 20180403 + | |-- gfs.t12z.icing.0p25.grb2f006 + | |-- gfs.t12z.icing.0p25.grb2f012 + +With this script, a sample of 11 GRIB files takes ~600secs to load:: + + import iris + import glob + + fpaths=glob.glob('20190416/*t18z*f???') + cubes = iris.load(fpaths, callback=callback) + + def callback(cube, field, fname): + if field.sections[5]['bitsPerValue'] == 0: + raise iris.exceptions.IgnoreCubeException + if field.sections[4]['parameterNumber'] == 20: + raise iris.exceptions.IgnoreCubeException + elif field.sections[4]['parameterNumber'] == 234: + cube.long_name = 'Icing Severity' + +3.2 Parallelisation +^^^^^^^^^^^^^^^^^^^ +We'll try using `dask.bag `_ to +parallelise the function calls. It's important that Dask is given the freedom +to break the task down in an efficient manner - the function that is mapped +across the bag should only load a single file, and the bag itself can +iterate through the list of files. Here's the restructured script:: + + import dask + import dask.bag as db + import iris + import glob + import multiprocessing + import os + + def callback(cube, field, fname): + if field.sections[5]['bitsPerValue'] == 0: + raise iris.exceptions.IgnoreCubeException + if field.sections[4]['parameterNumber'] == 20: + raise iris.exceptions.IgnoreCubeException + elif field.sections[4]['parameterNumber'] == 234: + cube.long_name = 'Icing Severity' + + def func(fname): + return iris.load_cube(fname, callback=callback) + + fpaths = list(glob.glob('20190416/*t18z*f???')) + + # Determine the number of processors visible .. + cpu_count = multiprocessing.cpu_count() + # .. or as given by slurm allocation. + if 'SLURM_NTASKS' in os.environ: + cpu_count = os.environ['SLURM_NTASKS'] + # Do not exceed the number of CPU's available, leaving 1 for the system. + num_workers = cpu_count - 1 + print('Using {} workers from {} CPUs...'.format(num_workers, cpu_count)) + + # Now do the parallel load. + with dask.config.set(num_workers=num_workers): + bag = db.from_sequence(fpaths).map(func) + cubes = iris.cube.CubeList(bag.compute()).merge() + +This achieves approximately a 10-fold improvement if enough CPU's are +available to have one per file. See this benchmarking: + ++---------------+-----------------------+---------------+---------------+ +| Machine | CPU's Available | CPU's Used | Time Taken | ++===============+=======================+===============+===============+ +| vld173 | 4 | 3 | 4m 05s | +| | +---------------+---------------+ +| | | 4 | 3m 22s | ++---------------+-----------------------+---------------+---------------+ +| eld299 | 8 | 1 | 9m 10s | +| | +---------------+---------------+ +| | | 7 | 2m 35s | +| | +---------------+---------------+ +| | | 8 | 2m 20s | ++---------------+-----------------------+---------------+---------------+ + + +.. _examples_bags_greed_profile: + +3.3 Profiling +^^^^^^^^^^^^^ +1m 10s is still a surprisingly long time. When faced with a mystery like +this it is helpful to profile the script to see if there are any steps that +are taking more time than we would expect. For this we use a tool called +`kapture `_ to produce a +flame chart visualising the time spent performing each call: + +.. image:: images/grib-bottleneck.png + :width: 1000 + :align: center + +From this we see that 96% of the runtime is taken by this call:: + + res = gribapi.grib_get_array(self._message_id, key) + +This is the call being used during the ``callback`` function when it uses +GRIB messages to filter out cubes with certain unwanted properties. + +3.4 Improving GRIB Key Handling +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Even with parallelisation, we are still limited by the time it takes to run +a single instance of a function. This is going to become much more important +when running 7000 files instead of 11, since there will be nowhere near +enough CPU's even on a large multi-processing system, meaning each CPU will be running many instances +of the function. **Parallelisation can only go so far to solving speed issues** -- +it's effectively the 'brute force' method. + +:ref:`examples_bags_greed_profile` showed us where the major bottleneck is. To improve efficiency +we can re-write the script to filter on GRIB messages *before* converting +the GRIB file to a cube:: + + import dask + import dask.bag as db + import glob + import iris + import multiprocessing + import os + + def func(fname): + import iris + from iris_grib import load_pairs_from_fields + from iris_grib.message import GribMessage # perform GRIB message level filtering... + filtered_messages = [] + for message in GribMessage.messages_from_filename(fname): + if (message.sections[5]['bitsPerValue'] != 0 and + message.sections[4]['parameterNumber'] == 234): + filtered_messages.append(message) # now convert the messages to cubes... + cubes = [cube for cube, message in load_pairs_from_fields(filtered_messages)] + return iris.cube.CubeList(cubes).merge_cube() + + fpaths = list(glob.glob('/scratch/frcz/ICING/GFS_DATA/20190416/*t18z*f???')) + cpu_count = multiprocessing.cpu_count() + if 'SLURM_NTASKS' in os.environ: + cpu_count = os.environ['SLURM_NTASKS'] + num_workers = cpu_count - 1 + + print('Using {} workers from {} CPUs...'.format(num_workers, cpu_count)) + with dask.config.set(num_workers=num_workers): + bag = db.from_sequence(fpaths).map(func) + cubes = iris.cube.CubeList(bag.compute()) + +This achieves a significant performance improvement - more than twice as +fast as the previous benchmarks: + ++---------------+-----------------------+---------------+---------------+-----------+ +| Machine | CPU's Available | CPU's Used | Previous Time | New Time | ++===============+=======================+===============+===============+===========+ +| Example | 8 | 7 | 2m 35s | 1m 05s | +| | +---------------+---------------+-----------+ +| | | 8 | 2m 20s | 1m 03s | ++---------------+-----------------------+---------------+---------------+-----------+ + +3.5 Managing External Factors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The speed will still need to be further improved before we can process 7000 +files. The main gains we can achieve are by making sure it is **only Dask** +that manages multi-processing - if multi-processing is coming from more +than one place there are predictable clashes. + +First, Numpy must be prevented from performing it's own multi-processing by +adding the following **before** ``import numpy`` is called. You can read more +about this in :ref:`numpy_threads`. + +:: + + import os + + os.environ["OMP_NUM_THREADS"] = "1" + os.environ["OPENBLAS_NUM_THREADS"] = "1" + os.environ["MKL_NUM_THREADS"] = "1" + os.environ["VECLIB_MAXIMUM_THREADS"] = "1" + os.environ["NUMEXPR_NUM_THREADS"] = "1" + +Second, if we are using a multi-processing system then SLURM must also be configured to prevent it +optimising the number of cores necessary for the job. See the SLURM commands +below, to be added before running the python script. It's important that +``ntasks`` matches the number of CPU's specified in the python script. You +can read more about these points in :ref:`multi-pro_slurm`. + +:: + + #SBATCH --ntasks=12 + #SBATCH --ntasks-per-core=1 + +This has all been based on a real example. Once all the above had been set +up correctly, the completion time had dropped from an estimated **55 days** +to **less than 1 day**. + +3.6 Lessons +^^^^^^^^^^^ +* Dask isn't a magic switch - it's important to write your script so that + there is a way to create small sub-tasks. In this case by providing + dask.bag with the file list and the function separated +* Parallelism is not the only performance improvement to try - the script + will still be slow if the individual function is slow +* All multi-processing needs to be managed by Dask. Several other factors + may introduce multi-processing and these need to be configured not to diff --git a/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst b/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst new file mode 100644 index 0000000000..dc7aeffe41 --- /dev/null +++ b/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst @@ -0,0 +1,169 @@ +.. _examples_parallel_loop: + +2. Parallelising a Loop of Multiple Calls to a Third Party Library +------------------------------------------------------------------ + +Whilst Iris does provide extensive functionality for performing statistical and +mathematical operations on your data, it is sometimes necessary to use a third +party library. + +The following example describes a real world use case of how to parallelise +multiple calls to a third party library using dask bags. + +2.1 The Problem - Parallelising a Loop +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In this particular example, the user is calculating a sounding parcel for each +column in their dataset. The cubes that are used are of shape:: + + (model_level_number: 20; grid_latitude: 1536; grid_longitude: 1536) + +As a sounding is calculated for each column, this means there are 1536x1536 +individual calculations. + +In Python, it is common practice to vectorize the calculation of for loops. +Vectorising is done by using numpy to operate on the whole array at once rather +than a single element at a time. Unfortunately, not all operations are +vectorisable, including the calculation in this example, and so we look to +other methods to improve the performance. + +2.2 Original Code with Loop +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +We start out by loading cubes of pressure, temperature, dewpoint temperature and height:: + + import iris + import numpy as np + from skewt import SkewT as sk + + pressure = iris.load_cube('a.press.19981109.pp') + temperature = iris.load_cube('a.temp.19981109.pp') + dewpoint = iris.load_cube('a.dewp.19981109.pp') + height = iris.load_cube('a.height.19981109.pp') + +We set up the numpy arrays we will be filling with the output data:: + + output_arrays = [np.zeros(pressure.shape[0]) for _ in range(6)] + cape, cin, lcl, lfc, el, tpw = output_data + +Now we loop over the columns in the data to calculate the soundings:: + + for y in range(nlim): + for x in range(nlim): + mydata = {'pres': pressure[:, y, x], + 'temp': temperature[:, y, x], + 'dwpt': dewpoint[:, y, x], + 'hght': height[:, y, x]} + + # Calculate the sounding with the selected column of data. + S = sk.Sounding(soundingdata=mydata) + try: + startp, startt, startdp, type_ = S.get_parcel(parcel_def) + P_lcl, P_lfc, P_el, CAPE, CIN = S.get_cape( + startp, startt, startdp, totalcape='tot') + TPW = S.precipitable_water() + except: + P_lcl, P_lfc, P_el, CAPE, CIN, TPW = [ + np.ma.masked for _ in range(6)] + + # Fill the output arrays with the results + cape[y,x] = CAPE + cin[y,x] = CIN + lcl[y,x] = P_lcl + lfc[y,x] = P_lfc + el[y,x] = P_el + tpw[y,x] = TPW + +2.3 Profiling the Code with Kapture +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Kapture is a useful statistical profiler. For more information see `the +Kapture repo `_. + +Results below: + +.. image:: images/loop_third_party_kapture_results.png + :width: 1000 + :align: center + +As we can see above, it spends most of the time in the call to :: + + S.get_parcel(parcel_def) + +As there are over two million columns in the data, we would greatly benefit +from parallelising this work. + +2.4 Parallelising with Dask Bags +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Dask bags are collections of Python objects that you can map a computation over +in a parallel manner. + +For more information about dask bags, see the `Dask Bag Documentation +`_. + +Dask bags work best with lightweight objects, so we will create a collection of +indices into our data arrays. + +First, we put the loop into a function that takes a slice object to index the +appropriate section of the array.:: + + def calculate_sounding(y_slice): + for y in range(y_slice.stop-y_slice.start): + for x in range(nlim): + mydata = {'pres': pressure[:, y_slice][:, y, x], + 'temp': temperature[:, y_slice][:, y, x], + 'dwpt': dewpoint[:, y_slice][:, y, x], + 'hght': height[:, y_slice][:, y, x]} + + # Calculate the sounding with the selected column of data. + S = sk.Sounding(soundingdata=mydata) + try: + startp, startt, startdp, type_ = S.get_parcel(parcel_def) + P_lcl, P_lfc, P_el, CAPE, CIN = S.get_cape( + startp, startt, startdp, totalcape=total_cape) + TPW = S.precipitable_water() + except: + P_lcl, P_lfc, P_el, CAPE, CIN, TPW = [ + np.ma.masked for _ in range(6)] + + # Fill the output arrays with the results + cape[:, y_slice][y,x] = CAPE + cin[:, y_slice][y,x] = CIN + lcl[:, y_slice][y,x] = P_lcl + lfc[:, y_slice][y,x] = P_lfc + el[:, y_slice][y,x] = P_el + tpw[:, y_slice][y,x] = TPW + +Then we create a dask bag of slice objects that will create multiple partitions +along the y axis.:: + + num_of_workers = 4 + len_of_y_axis = pressure.shape[1] + + part_loc = [int(loc) for loc in np.floor(np.linspace(0, len_of_y_axis, + num_of_workers + 1))] + + dask_bag = db.from_sequence( + [slice(part_loc[i], part_loc[i+1]) for i in range(num_of_workers)]) + + with dask.config.set(scheduler='processes'): + dask_bag.map(calculate_sounding).compute() + +When this was run on a VDI, which has 4 workers, a speedup of ~4x was achieved, +as expected. + +Note that if using the processes scheduler this is some extra time spent +serialising the data to pass it between workers. For more information on the +different schedulers available in Dask, see `Dask Scheduler Overview +`_. + +For more speed up, it is possible to run the same code on SPICE where you will +have access to more CPUs. + +In this particular example, we are handling multiple numpy arrays and so we use +dask bags. If working with a single numpy array, it may be more appropriate to +use Dask Arrays (see `Dask Arrays +`_ for more information). + + +2.5 Lessons +^^^^^^^^^^^ +* If possible, dask bags should contain lightweight objects +* Minimise the number of tasks that are created diff --git a/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst b/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst new file mode 100644 index 0000000000..ef5236cd0d --- /dev/null +++ b/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst @@ -0,0 +1,92 @@ +.. _examples_pp_to_ff: + +1. Speed up Converting PP Files to NetCDF +----------------------------------------- + +Here is an example of how dask objects can be tuned for better performance. + +1.1 The problem - Slow Saving +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +We have ~300 PP files which we load as follows: + +.. code-block:: python + + import iris + import glob + + files = glob.glob("/pp_files/*.pp") + cube = iris.load_cube(files, "mass_fraction_of_ozone_in_air") + +Note that loading here may also be parallelised in a similar manner as +described in :ref:`examples_bags_greed`. Either way, the resulting cube looks +as follows: + +.. code-block:: text + + mass_fraction_of_ozone_in_air / (kg kg-1) (time: 276; model_level_number: 85; latitude: 144; longitude: 192) + Dimension coordinates: + time x - - - + model_level_number - x - - + latitude - - x - + longitude - - - x + Auxiliary coordinates: + forecast_period x - - - + level_height - x - - + sigma - x - - + Scalar coordinates: + forecast_reference_time: 1850-01-01 00:00:00 + Attributes: + STASH: m01s34i001 + source: Data from Met Office Unified Model + um_version: 10.9 + Cell methods: + mean: time (1 hour) + +The cube is then immediately saved as a NetCDF file. + +.. code-block:: python + + nc_chunks = [chunk[0] for chunk in cube.lazy_data().chunks] + iris.save(cube, "outfile.nc", nc_chunks) + +This operation was taking longer than expected and we would like to improve +performance. Note that when this cube is being saved, the data is still lazy, +data is both read and written at the saving step and is done so in chunks. +The way this data is divided into chunks can affect performance. By tweaking +the way these chunks are structured it may be possible to improve performance +when saving. + + +.. _dask_rechunking: + +1.2 Rechunking +^^^^^^^^^^^^^^ +We may inspect the cube's lazy data before saving: + +.. code-block:: python + + # We can access the cubes Dask array + lazy_data = cube.lazy_data() + # We can find the shape of the chunks + # note that the chunksize of a Dask array is the shape of the chunk + # as a tuple. + print(lazy_data.chunksize) + +Doing so, we find that the chunks currently have the shape:: + +(1, 1, 144, 192) + +This is significantly smaller than the `size which dask recomends +`_. Bear in mind that the +ideal chunk size depends on the platform you are running on (for this example, +the code is being run on a desktop with 8 CPU's). In this case, we have 23460 +small chunks. We can reduce the number of chunks by rechunking before saving: + +.. code-block:: python + + lazy_data = cube.lazy_data() + lazy_data = lazy_data.rechunk(1, 85, 144, 192) + cube.data = lazy_data + +We now have 276 moderately sized chunks. When we try saving again, we find +that it is approximately 4 times faster, saving in 2m13s rather than 10m33s. diff --git a/docs/src/further_topics/dask_best_practices/images/grib-bottleneck.png b/docs/src/further_topics/dask_best_practices/images/grib-bottleneck.png new file mode 100644 index 0000000000000000000000000000000000000000..c029d57e5e4eff5f182398452cb6ea00ed700d9d GIT binary patch literal 63951 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 GxBmg|n>}Fw literal 0 HcmV?d00001 diff --git a/docs/src/further_topics/dask_best_practices/images/loop_third_party_kapture_results.png b/docs/src/further_topics/dask_best_practices/images/loop_third_party_kapture_results.png new file mode 100644 index 0000000000000000000000000000000000000000..8f388bb89caa781f5457e90d250bad4bdfcf917d GIT binary patch literal 84484 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%0AO+wrS-X33xw0$uZqSW1i!tO$QGb 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

r1 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 is a powerful tool for speeding up data handling +via lazy loading and parallel processing. To get the full benefit of using +Dask, it is important to configure it correctly and supply it with +appropriately structured data. For example, we may need to "chunk" data arrays +into smaller pieces to process, read and write it; getting the "chunking" right +can make a significant different to performance! + +To make sure you get the most out of Dask, check the list of sections on the right +for those that seem relevant to you. + + +.. _numpy_threads: + +Numpy Threads +============= + +In certain scenarios numpy will attempt to perform threading using an +external library - typically OMP, MKL or openBLAS - making use of **every** +CPU available. This interacts badly with Dask: + +* Dask may create multiple instances of numpy, each generating enough + threads to use **all** the available CPU's. The resulting sharing of CPU's + between threads greatly reduces performance. The more cores there are, the + more pronounced this problem is. +* Numpy will generate enough threads to use all available CPU's even + if Dask is deliberately configured to only use a subset of CPU's. The + resulting sharing of CPU's between threads greatly reduces performance. +* `Dask is already designed to parallelise with numpy arrays `_, so adding numpy's 'competing' layer of + parallelisation could cause unpredictable performance. + +Therefore it is best to prevent numpy performing its own parallelisation, `a +suggestion made in Dask's own documentation `_. +The following commands will ensure this in all scenarios: + +in Python... + +:: + + # Must be run before importing numpy. + import os + os.environ["OMP_NUM_THREADS"] = "1" + os.environ["OPENBLAS_NUM_THREADS"] = "1" + os.environ["MKL_NUM_THREADS"] = "1" + os.environ["VECLIB_MAXIMUM_THREADS"] = "1" + os.environ["NUMEXPR_NUM_THREADS"] = "1" + +or in Linux command line... + +:: + + export OMP_NUM_THREADS=1 + export OPENBLAS_NUM_THREADS=1 + export MKL_NUM_THREADS=1 + export VECLIB_MAXIMUM_THREADS=1 + export NUMEXPR_NUM_THREADS=1 + + +.. _multi-pro_systems: + +Dask on Multi-processing systems +================================ + +It is natural to use Dask on systems capable of multi-processing, for example +SPICE within the Met Office, but there are some important factors you must be +aware of. In particular, you will always need to explicitly control parallel +operation, both in Dask and likewise in numpy: see sections below. + + +.. _multi-pro_slurm: + +CPU Allocation +-------------- + +When running on a multi-processing system, unless configured otherwise, Dask will attempt to create +one parallel 'worker' task for each CPU visible. However, within a Slurm allocation, only *some* of +these CPUs are actually accessible -- often, and by default, only one. This leads to a serious +over-commitment unless it is controlled. + +So, **whenever Iris is used on a multi-processing system, you must always control the number +of dask workers to a sensible value**, matching the slurm allocation. You do +this with:: + + dask.config.set(num_workers=N) + +For an example, see :doc:`dask_bags_and_greed`. + +Alternatively, when there is only one CPU allocated, it may actually be more +efficient to use a "synchronous" scheduler instead, with:: + + dask.config.set(scheduler='synchronous') + +See `Single Thread +`_. + + +.. _multi-pro_numpy: + +Numpy Threading +--------------- + +Numpy also interrogates the visible number of CPUs to multi-thread its operations. +The large number of CPU's available in a multi-processing system will thus cause confusion if Numpy +attempts its own parallelisation, so this must be prevented. Refer back to +:ref:`numpy_threads` for more detail. + + +Distributed +----------- + +Even though allocations on a multi-processing system are generally restricted to a single node, there +are still good reasons for using 'dask.distributed' in many cases. See `Single Machine: dask.distributed +`_ in the Dask documentation. + + +Chunking +======== + +Dask breaks down large data arrays into chunks, allowing efficient +parallelisation by processing several smaller chunks simultaneously. For more +information, see the documentation on +`Dask Array `_. + +Iris provides a basic chunking shape to Dask, attempting to set the shape for +best performance. The chunking that is used can depend on the file format that +is being loaded. See below for how chunking is performed for: + + * :ref:`chunking_netcdf` + * :ref:`chunking_pp_ff` + +It can in some cases be beneficial to re-chunk the arrays in Iris cubes. +For information on how to do this, see :ref:`dask_rechunking`. + + +.. _chunking_netcdf: + +NetCDF Files +------------ + +NetCDF files can include their own chunking specification. This is either +specified when creating the file, or is automatically assigned if one or +more of the dimensions is `unlimited `_. +Importantly, netCDF chunk shapes are **not optimised for Dask +performance**. + +Chunking can be set independently for any variable in a netcdf file. +When a netcdf variable uses an unlimited dimension, it is automatically +chunked: the chunking is the shape of the whole variable, but with '1' instead +of the length in any unlimited dimensions. + +When chunking is specified for netcdf data, Iris will set the dask chunking +to an integer multiple or fraction of that shape, such that the data size is +near to but not exceeding the dask array chunk size. + +.. Note:: + Prior to Iris 2.3, Iris would not multiply up a chunksize from a netcdf + variable, which could therefore be quite inefficient. + Iris 2.2. was thus equally inefficient for some files with unlimited + dimensions. Similarly, **Iris 2.2 can often perform badly with StaGE + data**, as this typically has quite small chunks set in the file. + Upgrading to Iris 2.3+ can often solve these problems. + + +.. _chunking_pp_ff: + +PP and Fieldsfiles +------------------ + +PP and Fieldsfiles contain multiple 2D fields of data. When loading PP or +Fieldsfiles into Iris cubes, the chunking will automatically be set to a chunk +per field. + +For example, if a PP file contains 2D lat-lon fields for each of the +85 model level numbers, it will load in a cube that looks as follows:: + + (model_level_number: 85; latitude: 144; longitude: 192) + +The data in this cube will be partitioned with chunks of shape +:code:`(1, 144, 192)`. + +If the file(s) being loaded contain multiple fields, this can lead to an +excessive amount of chunks which will result in poor performance. + +When the default chunking is not appropriate, it is possible to rechunk. +:doc:`dask_pp_to_netcdf` provides a detailed demonstration of how Dask can optimise +that process. + + +Examples +======== + +We have written some examples of use cases for using Dask, that come with advice and +explanations for why and how the tasks are performed the way they are. + +If you feel you have an example of a Dask best practice that you think may be helpful to others, +please share them with us by raising a new `discussion on the Iris repository `_. + + * :doc:`dask_pp_to_netcdf` + * :doc:`dask_parallel_loop` + * :doc:`dask_bags_and_greed` + +.. toctree:: + :hidden: + :maxdepth: 1 + + dask_pp_to_netcdf + dask_parallel_loop + dask_bags_and_greed diff --git a/docs/src/userguide/index.rst b/docs/src/userguide/index.rst index fdd0c4d03e..771aa450a3 100644 --- a/docs/src/userguide/index.rst +++ b/docs/src/userguide/index.rst @@ -45,4 +45,5 @@ they may serve as a useful reference for future exploration. ../further_topics/metadata ../further_topics/lenient_metadata ../further_topics/lenient_maths + ../further_topics/dask_best_practices/index ../further_topics/ugrid/index diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 0e2896b7a1..e0edeb53ad 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -71,6 +71,10 @@ This document explains the changes made to Iris for this release to use it. By default the theme will be based on the users system settings, defaulting to ``light`` if no system setting is found. (:pull:`5299`) +#. `@HGWright`_ added a :doc:`/further_topics/dask_best_practices/index` + section into the user guide, containing advice and use cases to help users + get the best out of Dask. + 💼 Internal =========== From d710d5c4f0c69374db4da7f8441d291a80f0581c Mon Sep 17 00:00:00 2001 From: Henry Wright Date: Thu, 16 Mar 2023 12:00:34 +0000 Subject: [PATCH 2/7] Updated example 2, adjusted internal MO references --- .../dask_best_practices/dask_parallel_loop.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst b/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst index dc7aeffe41..48b233ea4e 100644 --- a/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst +++ b/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst @@ -146,7 +146,7 @@ along the y axis.:: with dask.config.set(scheduler='processes'): dask_bag.map(calculate_sounding).compute() -When this was run on a VDI, which has 4 workers, a speedup of ~4x was achieved, +When this was run on a machine with 4 workers, a speedup of ~4x was achieved, as expected. Note that if using the processes scheduler this is some extra time spent @@ -154,8 +154,8 @@ serialising the data to pass it between workers. For more information on the different schedulers available in Dask, see `Dask Scheduler Overview `_. -For more speed up, it is possible to run the same code on SPICE where you will -have access to more CPUs. +For more speed up, it is possible to run the same code on a multi-processing +system where you will have access to more CPUs. In this particular example, we are handling multiple numpy arrays and so we use dask bags. If working with a single numpy array, it may be more appropriate to From 1c57220d0ca792076d65acfbd6a060e0bceacac5 Mon Sep 17 00:00:00 2001 From: Henry Wright Date: Thu, 23 Mar 2023 16:26:15 +0000 Subject: [PATCH 3/7] making requested changes from review --- .../dask_bags_and_greed.rst | 15 +++--- .../dask_parallel_loop.rst | 2 +- .../dask_best_practices/dask_pp_to_netcdf.rst | 8 +-- .../dask_best_practices/index.rst | 49 ++++++++----------- docs/src/whatsnew/latest.rst | 2 +- 5 files changed, 35 insertions(+), 41 deletions(-) diff --git a/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst b/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst index 465a30c40e..dc0cb938bc 100644 --- a/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst +++ b/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst @@ -54,13 +54,14 @@ to break the task down in an efficient manner - the function that is mapped across the bag should only load a single file, and the bag itself can iterate through the list of files. Here's the restructured script:: - import dask - import dask.bag as db - import iris import glob import multiprocessing import os + import dask + import dask.bag as db + import iris + def callback(cube, field, fname): if field.sections[5]['bitsPerValue'] == 0: raise iris.exceptions.IgnoreCubeException @@ -76,9 +77,11 @@ iterate through the list of files. Here's the restructured script:: # Determine the number of processors visible .. cpu_count = multiprocessing.cpu_count() + # .. or as given by slurm allocation. if 'SLURM_NTASKS' in os.environ: cpu_count = os.environ['SLURM_NTASKS'] + # Do not exceed the number of CPU's available, leaving 1 for the system. num_workers = cpu_count - 1 print('Using {} workers from {} CPUs...'.format(num_workers, cpu_count)) @@ -94,11 +97,11 @@ available to have one per file. See this benchmarking: +---------------+-----------------------+---------------+---------------+ | Machine | CPU's Available | CPU's Used | Time Taken | +===============+=======================+===============+===============+ -| vld173 | 4 | 3 | 4m 05s | +| A | 4 | 3 | 4m 05s | | | +---------------+---------------+ | | | 4 | 3m 22s | +---------------+-----------------------+---------------+---------------+ -| eld299 | 8 | 1 | 9m 10s | +| B | 8 | 1 | 9m 10s | | | +---------------+---------------+ | | | 7 | 2m 35s | | | +---------------+---------------+ @@ -202,7 +205,7 @@ about this in :ref:`numpy_threads`. os.environ["VECLIB_MAXIMUM_THREADS"] = "1" os.environ["NUMEXPR_NUM_THREADS"] = "1" -Second, if we are using a multi-processing system then SLURM must also be configured to prevent it +Lastly, if you are using SLURM on the computing cluster then SLURM must be configured to prevent it optimising the number of cores necessary for the job. See the SLURM commands below, to be added before running the python script. It's important that ``ntasks`` matches the number of CPU's specified in the python script. You diff --git a/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst b/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst index 48b233ea4e..11e6adfb74 100644 --- a/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst +++ b/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst @@ -83,7 +83,7 @@ Results below: :width: 1000 :align: center -As we can see above, it spends most of the time in the call to :: +As we can see above, (looking at the highlighted section of the red bar) it spends most of the time in the call to :: S.get_parcel(parcel_def) diff --git a/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst b/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst index ef5236cd0d..3d6e680268 100644 --- a/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst +++ b/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst @@ -5,7 +5,7 @@ Here is an example of how dask objects can be tuned for better performance. -1.1 The problem - Slow Saving +1.1 The Problem - Slow Saving ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We have ~300 PP files which we load as follows: @@ -14,7 +14,7 @@ We have ~300 PP files which we load as follows: import iris import glob - files = glob.glob("/pp_files/*.pp") + files = glob.glob("pp_files/*.pp") cube = iris.load_cube(files, "mass_fraction_of_ozone_in_air") Note that loading here may also be parallelised in a similar manner as @@ -68,7 +68,7 @@ We may inspect the cube's lazy data before saving: # We can access the cubes Dask array lazy_data = cube.lazy_data() # We can find the shape of the chunks - # note that the chunksize of a Dask array is the shape of the chunk + # Note that the chunksize of a Dask array is the shape of the chunk # as a tuple. print(lazy_data.chunksize) @@ -76,7 +76,7 @@ Doing so, we find that the chunks currently have the shape:: (1, 1, 144, 192) -This is significantly smaller than the `size which dask recomends +This is significantly smaller than the `size which Dask recommends `_. Bear in mind that the ideal chunk size depends on the platform you are running on (for this example, the code is being run on a desktop with 8 CPU's). In this case, we have 23460 diff --git a/docs/src/further_topics/dask_best_practices/index.rst b/docs/src/further_topics/dask_best_practices/index.rst index a2935fbacc..daeb17a837 100644 --- a/docs/src/further_topics/dask_best_practices/index.rst +++ b/docs/src/further_topics/dask_best_practices/index.rst @@ -3,15 +3,18 @@ Dask Best Practices ******************* -This section outlines some of the best practices when using Dask. Some of these practices -involve improving performance through rechunking, making the best use of multi-processing -systems and avoiding conflicts between Dask and numpy. +This section outlines some of the best practices when using Dask with IRIS. Some of these +practices involve improving performance through rechunking, making the best use of +computing clusters and avoiding parallelisation conflicts between Dask and numpy. -.. warning:: - Here, we have collated advice and a handful of examples that we hope will assist - users to make the best start when using Dask. It is *not* a - fully comprehensive guide encompassing all best practices. +.. note:: + + Here, we have collated advice and a handful of examples, from the topics most + relevant when using Dask with IRIS, that we hope will assist users to make + the best start when using Dask. It is *not* a fully comprehensive guide + encompassing all best practices. You can find more general dask information in the + `official Dask Documentation `_. Introduction @@ -24,9 +27,6 @@ appropriately structured data. For example, we may need to "chunk" data arrays into smaller pieces to process, read and write it; getting the "chunking" right can make a significant different to performance! -To make sure you get the most out of Dask, check the list of sections on the right -for those that seem relevant to you. - .. _numpy_threads: @@ -78,13 +78,12 @@ or in Linux command line... .. _multi-pro_systems: -Dask on Multi-processing systems -================================ +Dask on Computing Clusters +========================== -It is natural to use Dask on systems capable of multi-processing, for example -SPICE within the Met Office, but there are some important factors you must be +Dask is well suited for use on computing clusters, but there are some important factors you must be aware of. In particular, you will always need to explicitly control parallel -operation, both in Dask and likewise in numpy: see sections below. +operation, both in Dask and likewise in numpy. .. _multi-pro_slurm: @@ -92,12 +91,12 @@ operation, both in Dask and likewise in numpy: see sections below. CPU Allocation -------------- -When running on a multi-processing system, unless configured otherwise, Dask will attempt to create -one parallel 'worker' task for each CPU visible. However, within a Slurm allocation, only *some* of +When running on a computing cluster, unless configured otherwise, Dask will attempt to create +one parallel 'worker' task for each CPU. However, within a Slurm allocation, only *some* of these CPUs are actually accessible -- often, and by default, only one. This leads to a serious over-commitment unless it is controlled. -So, **whenever Iris is used on a multi-processing system, you must always control the number +So, **whenever Iris is used on a computing cluster, you must always control the number of dask workers to a sensible value**, matching the slurm allocation. You do this with:: @@ -110,7 +109,7 @@ efficient to use a "synchronous" scheduler instead, with:: dask.config.set(scheduler='synchronous') -See `Single Thread +See the Dask documentation on `Single thread synchronous scheduler `_. @@ -120,7 +119,7 @@ Numpy Threading --------------- Numpy also interrogates the visible number of CPUs to multi-thread its operations. -The large number of CPU's available in a multi-processing system will thus cause confusion if Numpy +The large number of CPU's available in a computing cluster will thus cause confusion if Numpy attempts its own parallelisation, so this must be prevented. Refer back to :ref:`numpy_threads` for more detail. @@ -128,7 +127,7 @@ attempts its own parallelisation, so this must be prevented. Refer back to Distributed ----------- -Even though allocations on a multi-processing system are generally restricted to a single node, there +Even though allocations on a computing cluster are generally restricted to a single node, there are still good reasons for using 'dask.distributed' in many cases. See `Single Machine: dask.distributed `_ in the Dask documentation. @@ -173,14 +172,6 @@ When chunking is specified for netcdf data, Iris will set the dask chunking to an integer multiple or fraction of that shape, such that the data size is near to but not exceeding the dask array chunk size. -.. Note:: - Prior to Iris 2.3, Iris would not multiply up a chunksize from a netcdf - variable, which could therefore be quite inefficient. - Iris 2.2. was thus equally inefficient for some files with unlimited - dimensions. Similarly, **Iris 2.2 can often perform badly with StaGE - data**, as this typically has quite small chunks set in the file. - Upgrading to Iris 2.3+ can often solve these problems. - .. _chunking_pp_ff: diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index e0edeb53ad..73cbd93167 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -73,7 +73,7 @@ This document explains the changes made to Iris for this release #. `@HGWright`_ added a :doc:`/further_topics/dask_best_practices/index` section into the user guide, containing advice and use cases to help users - get the best out of Dask. + get the best out of Dask with IRIS. 💼 Internal From 222fc970e0b197bfe53f4ca8ff8a028cc5eb510e Mon Sep 17 00:00:00 2001 From: Henry Wright Date: Fri, 24 Mar 2023 15:16:10 +0000 Subject: [PATCH 4/7] fixing merge conflict and rest of requested changes --- .../dask_bags_and_greed.rst | 18 +++++--- .../dask_parallel_loop.rst | 4 +- .../dask_best_practices/dask_pp_to_netcdf.rst | 4 +- .../dask_best_practices/index.rst | 44 +++++++++---------- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst b/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst index dc0cb938bc..d1c864634e 100644 --- a/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst +++ b/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst @@ -79,10 +79,11 @@ iterate through the list of files. Here's the restructured script:: cpu_count = multiprocessing.cpu_count() # .. or as given by slurm allocation. + # Only relevant when using Slurm for job scheduling if 'SLURM_NTASKS' in os.environ: cpu_count = os.environ['SLURM_NTASKS'] - # Do not exceed the number of CPU's available, leaving 1 for the system. + # Do not exceed the number of CPUs available, leaving 1 for the system. num_workers = cpu_count - 1 print('Using {} workers from {} CPUs...'.format(num_workers, cpu_count)) @@ -91,11 +92,11 @@ iterate through the list of files. Here's the restructured script:: bag = db.from_sequence(fpaths).map(func) cubes = iris.cube.CubeList(bag.compute()).merge() -This achieves approximately a 10-fold improvement if enough CPU's are +This achieves approximately a 10-fold improvement if enough CPUs are available to have one per file. See this benchmarking: +---------------+-----------------------+---------------+---------------+ -| Machine | CPU's Available | CPU's Used | Time Taken | +| Machine | CPUs Available | CPUs Used | Time Taken | +===============+=======================+===============+===============+ | A | 4 | 3 | 4m 05s | | | +---------------+---------------+ @@ -135,7 +136,7 @@ GRIB messages to filter out cubes with certain unwanted properties. Even with parallelisation, we are still limited by the time it takes to run a single instance of a function. This is going to become much more important when running 7000 files instead of 11, since there will be nowhere near -enough CPU's even on a large multi-processing system, meaning each CPU will be running many instances +enough CPUs even on a large multi-processing system, meaning each CPU will be running many instances of the function. **Parallelisation can only go so far to solving speed issues** -- it's effectively the 'brute force' method. @@ -164,8 +165,11 @@ the GRIB file to a cube:: fpaths = list(glob.glob('/scratch/frcz/ICING/GFS_DATA/20190416/*t18z*f???')) cpu_count = multiprocessing.cpu_count() + + # Only relevant when using Slurm for job scheduling if 'SLURM_NTASKS' in os.environ: cpu_count = os.environ['SLURM_NTASKS'] + num_workers = cpu_count - 1 print('Using {} workers from {} CPUs...'.format(num_workers, cpu_count)) @@ -177,7 +181,7 @@ This achieves a significant performance improvement - more than twice as fast as the previous benchmarks: +---------------+-----------------------+---------------+---------------+-----------+ -| Machine | CPU's Available | CPU's Used | Previous Time | New Time | +| Machine | CPUs Available | CPUs Used | Previous Time | New Time | +===============+=======================+===============+===============+===========+ | Example | 8 | 7 | 2m 35s | 1m 05s | | | +---------------+---------------+-----------+ @@ -191,7 +195,7 @@ files. The main gains we can achieve are by making sure it is **only Dask** that manages multi-processing - if multi-processing is coming from more than one place there are predictable clashes. -First, Numpy must be prevented from performing it's own multi-processing by +First, NumPy must be prevented from performing it's own multi-processing by adding the following **before** ``import numpy`` is called. You can read more about this in :ref:`numpy_threads`. @@ -208,7 +212,7 @@ about this in :ref:`numpy_threads`. Lastly, if you are using SLURM on the computing cluster then SLURM must be configured to prevent it optimising the number of cores necessary for the job. See the SLURM commands below, to be added before running the python script. It's important that -``ntasks`` matches the number of CPU's specified in the python script. You +``ntasks`` matches the number of CPUs specified in the python script. You can read more about these points in :ref:`multi-pro_slurm`. :: diff --git a/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst b/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst index 11e6adfb74..836503314c 100644 --- a/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst +++ b/docs/src/further_topics/dask_best_practices/dask_parallel_loop.rst @@ -21,7 +21,7 @@ As a sounding is calculated for each column, this means there are 1536x1536 individual calculations. In Python, it is common practice to vectorize the calculation of for loops. -Vectorising is done by using numpy to operate on the whole array at once rather +Vectorising is done by using NumPy to operate on the whole array at once rather than a single element at a time. Unfortunately, not all operations are vectorisable, including the calculation in this example, and so we look to other methods to improve the performance. @@ -39,7 +39,7 @@ We start out by loading cubes of pressure, temperature, dewpoint temperature and dewpoint = iris.load_cube('a.dewp.19981109.pp') height = iris.load_cube('a.height.19981109.pp') -We set up the numpy arrays we will be filling with the output data:: +We set up the NumPy arrays we will be filling with the output data:: output_arrays = [np.zeros(pressure.shape[0]) for _ in range(6)] cape, cin, lcl, lfc, el, tpw = output_data diff --git a/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst b/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst index 3d6e680268..28784154b4 100644 --- a/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst +++ b/docs/src/further_topics/dask_best_practices/dask_pp_to_netcdf.rst @@ -42,7 +42,7 @@ as follows: Cell methods: mean: time (1 hour) -The cube is then immediately saved as a NetCDF file. +The cube is then immediately saved as a netCDF file. .. code-block:: python @@ -79,7 +79,7 @@ Doing so, we find that the chunks currently have the shape:: This is significantly smaller than the `size which Dask recommends `_. Bear in mind that the ideal chunk size depends on the platform you are running on (for this example, -the code is being run on a desktop with 8 CPU's). In this case, we have 23460 +the code is being run on a desktop with 8 CPUs). In this case, we have 23460 small chunks. We can reduce the number of chunks by rechunking before saving: .. code-block:: python diff --git a/docs/src/further_topics/dask_best_practices/index.rst b/docs/src/further_topics/dask_best_practices/index.rst index daeb17a837..d5bb5701ec 100644 --- a/docs/src/further_topics/dask_best_practices/index.rst +++ b/docs/src/further_topics/dask_best_practices/index.rst @@ -3,15 +3,15 @@ Dask Best Practices ******************* -This section outlines some of the best practices when using Dask with IRIS. Some of these +This section outlines some of the best practices when using Dask with Iris. These practices involve improving performance through rechunking, making the best use of -computing clusters and avoiding parallelisation conflicts between Dask and numpy. +computing clusters and avoiding parallelisation conflicts between Dask and NumPy. .. note:: Here, we have collated advice and a handful of examples, from the topics most - relevant when using Dask with IRIS, that we hope will assist users to make + relevant when using Dask with Iris, that we hope will assist users to make the best start when using Dask. It is *not* a fully comprehensive guide encompassing all best practices. You can find more general dask information in the `official Dask Documentation `_. @@ -30,25 +30,25 @@ can make a significant different to performance! .. _numpy_threads: -Numpy Threads +NumPy Threads ============= -In certain scenarios numpy will attempt to perform threading using an +In certain scenarios NumPy will attempt to perform threading using an external library - typically OMP, MKL or openBLAS - making use of **every** CPU available. This interacts badly with Dask: -* Dask may create multiple instances of numpy, each generating enough - threads to use **all** the available CPU's. The resulting sharing of CPU's +* Dask may create multiple instances of NumPy, each generating enough + threads to use **all** the available CPUs. The resulting sharing of CPUs between threads greatly reduces performance. The more cores there are, the more pronounced this problem is. -* Numpy will generate enough threads to use all available CPU's even - if Dask is deliberately configured to only use a subset of CPU's. The - resulting sharing of CPU's between threads greatly reduces performance. -* `Dask is already designed to parallelise with numpy arrays `_, so adding numpy's 'competing' layer of +* NumPy will generate enough threads to use all available CPUs even + if Dask is deliberately configured to only use a subset of CPUs. The + resulting sharing of CPUs between threads greatly reduces performance. +* `Dask is already designed to parallelise with NumPy arrays `_, so adding NumPy's 'competing' layer of parallelisation could cause unpredictable performance. -Therefore it is best to prevent numpy performing its own parallelisation, `a +Therefore it is best to prevent NumPy performing its own parallelisation, `a suggestion made in Dask's own documentation `_. The following commands will ensure this in all scenarios: @@ -57,7 +57,7 @@ in Python... :: - # Must be run before importing numpy. + # Must be run before importing NumPy. import os os.environ["OMP_NUM_THREADS"] = "1" os.environ["OPENBLAS_NUM_THREADS"] = "1" @@ -83,7 +83,7 @@ Dask on Computing Clusters Dask is well suited for use on computing clusters, but there are some important factors you must be aware of. In particular, you will always need to explicitly control parallel -operation, both in Dask and likewise in numpy. +operation, both in Dask and likewise in NumPy. .. _multi-pro_slurm: @@ -92,7 +92,7 @@ CPU Allocation -------------- When running on a computing cluster, unless configured otherwise, Dask will attempt to create -one parallel 'worker' task for each CPU. However, within a Slurm allocation, only *some* of +one parallel 'worker' task for each CPU. However, when using a job scheduler such as Slurm, only *some* of these CPUs are actually accessible -- often, and by default, only one. This leads to a serious over-commitment unless it is controlled. @@ -115,11 +115,11 @@ See the Dask documentation on `Single thread synchronous scheduler .. _multi-pro_numpy: -Numpy Threading +NumPy Threading --------------- -Numpy also interrogates the visible number of CPUs to multi-thread its operations. -The large number of CPU's available in a computing cluster will thus cause confusion if Numpy +NumPy also interrogates the visible number of CPUs to multi-thread its operations. +The large number of CPUs available in a computing cluster will thus cause confusion if NumPy attempts its own parallelisation, so this must be prevented. Refer back to :ref:`numpy_threads` for more detail. @@ -163,12 +163,12 @@ more of the dimensions is `unlimited Date: Fri, 2 Jun 2023 11:01:37 +0100 Subject: [PATCH 5/7] finishing requested changes? --- .../dask_best_practices/dask_bags_and_greed.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst b/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst index d1c864634e..007a58d400 100644 --- a/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst +++ b/docs/src/further_topics/dask_best_practices/dask_bags_and_greed.rst @@ -96,7 +96,7 @@ This achieves approximately a 10-fold improvement if enough CPUs are available to have one per file. See this benchmarking: +---------------+-----------------------+---------------+---------------+ -| Machine | CPUs Available | CPUs Used | Time Taken | +| Machine | CPUs Available | CPUs Used | Time Taken | +===============+=======================+===============+===============+ | A | 4 | 3 | 4m 05s | | | +---------------+---------------+ @@ -181,7 +181,7 @@ This achieves a significant performance improvement - more than twice as fast as the previous benchmarks: +---------------+-----------------------+---------------+---------------+-----------+ -| Machine | CPUs Available | CPUs Used | Previous Time | New Time | +| Machine | CPUs Available | CPUs Used | Previous Time | New Time | +===============+=======================+===============+===============+===========+ | Example | 8 | 7 | 2m 35s | 1m 05s | | | +---------------+---------------+-----------+ From aac31cdc70d9cf2b5db0cb7d710e6eeedd98085e Mon Sep 17 00:00:00 2001 From: Henry Wright Date: Fri, 2 Jun 2023 12:06:22 +0100 Subject: [PATCH 6/7] fixing dask docs link for linkcheck --- docs/src/further_topics/dask_best_practices/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/further_topics/dask_best_practices/index.rst b/docs/src/further_topics/dask_best_practices/index.rst index d5bb5701ec..eb3321345b 100644 --- a/docs/src/further_topics/dask_best_practices/index.rst +++ b/docs/src/further_topics/dask_best_practices/index.rst @@ -14,7 +14,7 @@ computing clusters and avoiding parallelisation conflicts between Dask and NumPy relevant when using Dask with Iris, that we hope will assist users to make the best start when using Dask. It is *not* a fully comprehensive guide encompassing all best practices. You can find more general dask information in the - `official Dask Documentation `_. + `official Dask Documentation `_. Introduction From 95540f3d7559447e32377e4850cdc1379b37d4a4 Mon Sep 17 00:00:00 2001 From: Henry Wright <84939917+HGWright@users.noreply.github.com> Date: Mon, 12 Jun 2023 10:56:18 +0100 Subject: [PATCH 7/7] Update docs/src/whatsnew/latest.rst Co-authored-by: lbdreyer --- docs/src/whatsnew/latest.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index 73cbd93167..9b62715be6 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -73,7 +73,7 @@ This document explains the changes made to Iris for this release #. `@HGWright`_ added a :doc:`/further_topics/dask_best_practices/index` section into the user guide, containing advice and use cases to help users - get the best out of Dask with IRIS. + get the best out of Dask with Iris. 💼 Internal