<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>https://blog.dask.org</id>
  <title>Dask Working Notes - Posted in 2020</title>
  <updated>2026-03-05T15:05:21.504990+00:00</updated>
  <link href="https://blog.dask.org"/>
  <link href="https://blog.dask.org/blog/2020/atom.xml" rel="self"/>
  <generator uri="https://ablog.readthedocs.io/" version="0.11.12">ABlog</generator>
  <entry>
    <id>https://blog.dask.org/2020/11/12/deconvolution/</id>
    <title>Image Analysis Redux</title>
    <updated>2020-11-12T00:00:00+00:00</updated>
    <author>
      <name>John Kirkham (NVIDIA) and Ben Zaitlen (NVIDIA)</name>
    </author>
    <content type="html">&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/11/12/deconvolution.md&lt;/span&gt;, line 9)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="summary"&gt;

&lt;p&gt;&lt;a class="reference external" href="https://blog.dask.org/2019/08/09/image-itk"&gt;Last year&lt;/a&gt; we experimented with
Dask/ITK/Scikit-Image to perform large scale image analysis on a stack of 3D
images. Specifically, we looked at deconvolution, a common method to &lt;em&gt;deblur&lt;/em&gt;
images. Now, a year later, we return to these experiments with a better
understanding of how Dask and CuPy can interact, enhanced serialization
methods, and support from the open-source community. This post looks at the
following:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Implementing a common deconvolution method for CPU + GPU&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leveraging Dask to perform deconvolution on a larger dataset&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exploring the results with the Napari image viewer&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/11/12/deconvolution.md&lt;/span&gt;, line 23)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="image-analysis-redux"&gt;
&lt;h1&gt;Image Analysis Redux&lt;/h1&gt;
&lt;p&gt;Previously we used the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Richardson%E2%80%93Lucy_deconvolution"&gt;Richardson Lucy
(RL)&lt;/a&gt;
deconvolution algorithm from ITK and
&lt;a class="reference external" href="https://github.com/scikit-image/scikit-image/blob/master/skimage/restoration/deconvolution.py#L329"&gt;Scikit-Image&lt;/a&gt;.
We left off at theorizing how GPUs could potentially help accelerate these
workflows. Starting with Scikit-Image’s implementation, we naively tried
replacing &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;scipy.signal.convolve&lt;/span&gt;&lt;/code&gt; calls with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;cupyx.scipy.ndimage.convolve&lt;/span&gt;&lt;/code&gt;,
and while performance improved, it did not improve &lt;em&gt;significantly&lt;/em&gt; – that is,
we did not get the 100X speed we were looking for.&lt;/p&gt;
&lt;p&gt;As it often turns out in mathematics a problem that can be inefficient to solve
in one representation can often be made more efficent by transforming the data
beforehand. In this new representation we can solve the same problem
(convolution in this case) more easily before transforming the result back into
a more familiar representation. When it comes to convolution, the
transformation we apply is called &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Fast_Fourier_transform"&gt;Fast-Fourier Transform
(FFT)&lt;/a&gt;. Once this is
applied we are able to convolve data using a simple multiplication.&lt;/p&gt;
&lt;p&gt;As it turns out this FFT transformation is extremely fast on both CPUs and
GPUs. Similarly the algorithm we can write with FFTs is accelerated. This is a
commonly used technique in the image processing field to speed up convolutions.
Despite the added step of doing FFTs, the cost of transformation + the cost of
the algorithm is still lower than performing the original algorithm in real
space. We (and others before us) found this was the case for Richardson Lucy
(on both CPUs and GPUs) and performance continued increasing when we
parallelized with Dask over multiple GPUs.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/11/12/deconvolution.md&lt;/span&gt;, line 53)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="help-from-open-source"&gt;
&lt;h1&gt;Help from Open-Source&lt;/h1&gt;
&lt;p&gt;An FFT RL equivalent has been around for some time and the good folks at the
&lt;a class="reference external" href="https://sdo.gsfc.nasa.gov/mission/instruments.php"&gt;Solar Dynamics Observatory&lt;/a&gt;
built and shared a NumPy/CuPy implementation as part the &lt;a class="reference external" href="https://aiapy.readthedocs.io/en/v0.2.0/_modules/aiapy/psf/deconvolve.html"&gt;Atmospheric Imaging
Assembly&lt;/a&gt;
Python package (aiapy). We slightly modified their implementation to handle 3D
as well as 2D &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Point_spread_function"&gt;Point Spread
Functions&lt;/a&gt; and to take
advantage of
&lt;a class="reference external" href="https://numpy.org/neps/nep-0018-array-function-protocol.html"&gt;NEP-18&lt;/a&gt; for
convenient dispatching of NumPy and CuPy to NumPy and CuPy functions:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;deconvolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Pad PSF with zeros to match image shape&lt;/span&gt;
    &lt;span class="n"&gt;pad_l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pad_r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;divmod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pad_r&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;pad_l&lt;/span&gt;
    &lt;span class="n"&gt;psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pad_l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pad_r&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;constant&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;constant_values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Recenter PSF at the origin&lt;/span&gt;
    &lt;span class="c1"&gt;# Needed to ensure PSF doesn&amp;#39;t introduce an offset when&lt;/span&gt;
    &lt;span class="c1"&gt;# convolving with image&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndim&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;roll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Convolution requires FFT of the PSF&lt;/span&gt;
    &lt;span class="n"&gt;psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rfftn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Perform deconvolution in-place on a copy of the image&lt;/span&gt;
    &lt;span class="c1"&gt;# (avoids changing the original)&lt;/span&gt;
    &lt;span class="n"&gt;img_decon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;irfftn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rfftn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img_decon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;img_decon&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;irfftn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fft&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rfftn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conj&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conj&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;img_decon&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For a 1.3 GB image we measured the following:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;CuPy ~3 seconds for 20 iterations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NumPy ~36 seconds for 2 iterations&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We see 10x increase in speed for 10 times the number of iterations – very
close to our desired 100x speedup! Let’s explore how this implementation
performs with real biological data and Dask…&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/11/12/deconvolution.md&lt;/span&gt;, line 100)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="define-a-dask-cluster-and-load-the-data"&gt;
&lt;h1&gt;Define a Dask Cluster and Load the Data&lt;/h1&gt;
&lt;p&gt;We were provided sample data from &lt;a class="reference external" href="https://www.nibib.nih.gov/about-nibib/staff/hari-shroff"&gt;Prof.
Shroff’s&lt;/a&gt; lab at the
NIH. The data originally was provided as a 3D TIFF file which we subsequently
converted to Zarr with a shape of (950, 2048, 2048).&lt;/p&gt;
&lt;p&gt;We start by creating a Dask cluster on a DGX2 (16 GPUs in a single node) and
loading the image stored in Zarr :&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://gist.github.com/quasiben/3a638bb9a4f075ac9041bf66974ebb45"&gt;Example Notebook&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_cuda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LocalCUDACluster&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;rmm&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cupy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cp&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LocalCUDACluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;local_directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/tmp/bzaitlen&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;enable_nvlink&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rmm_pool_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;26GB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_allocator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rmm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rmm_cupy_allocator&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;imgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_zarr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/public/NVMICROSCOPY/y1z1_C1_A.zarr/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;th&gt; Array &lt;/th&gt;&lt;th&gt; Chunk &lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;th&gt; Bytes &lt;/th&gt;&lt;td&gt; 7.97 GB &lt;/td&gt; &lt;td&gt; 8.39 MB &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Shape &lt;/th&gt;&lt;td&gt; (950, 2048, 2048) &lt;/td&gt; &lt;td&gt; (1, 2048, 2048) &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Count &lt;/th&gt;&lt;td&gt; 951 Tasks &lt;/td&gt;&lt;td&gt; 950 Chunks &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Type &lt;/th&gt;&lt;td&gt; uint16 &lt;/td&gt;&lt;td&gt; numpy.ndarray &lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;svg width="212" height="202" style="stroke:rgb(0,0,0);stroke-width:1" &gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="10" y1="0" x2="42" y2="32" style="stroke-width:2" /&gt;
  &lt;line x1="10" y1="120" x2="42" y2="152" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="10" y1="0" x2="10" y2="120" style="stroke-width:2" /&gt;
  &lt;line x1="12" y1="2" x2="12" y2="122" /&gt;
  &lt;line x1="14" y1="4" x2="14" y2="124" /&gt;
  &lt;line x1="16" y1="6" x2="16" y2="126" /&gt;
  &lt;line x1="18" y1="8" x2="18" y2="128" /&gt;
  &lt;line x1="20" y1="10" x2="20" y2="130" /&gt;
  &lt;line x1="22" y1="12" x2="22" y2="132" /&gt;
  &lt;line x1="24" y1="14" x2="24" y2="134" /&gt;
  &lt;line x1="26" y1="16" x2="26" y2="136" /&gt;
  &lt;line x1="28" y1="18" x2="28" y2="138" /&gt;
  &lt;line x1="30" y1="20" x2="30" y2="140" /&gt;
  &lt;line x1="32" y1="22" x2="32" y2="142" /&gt;
  &lt;line x1="34" y1="24" x2="34" y2="144" /&gt;
  &lt;line x1="36" y1="26" x2="36" y2="146" /&gt;
  &lt;line x1="38" y1="28" x2="38" y2="148" /&gt;
  &lt;line x1="41" y1="31" x2="41" y2="151" /&gt;
  &lt;line x1="42" y1="32" x2="42" y2="152" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="10.000000,0.000000 42.743566,32.743566 42.743566,152.743566 10.000000,120.000000" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="10" y1="0" x2="130" y2="0" style="stroke-width:2" /&gt;
  &lt;line x1="12" y1="2" x2="132" y2="2" /&gt;
  &lt;line x1="14" y1="4" x2="134" y2="4" /&gt;
  &lt;line x1="16" y1="6" x2="136" y2="6" /&gt;
  &lt;line x1="18" y1="8" x2="138" y2="8" /&gt;
  &lt;line x1="20" y1="10" x2="140" y2="10" /&gt;
  &lt;line x1="22" y1="12" x2="142" y2="12" /&gt;
  &lt;line x1="24" y1="14" x2="144" y2="14" /&gt;
  &lt;line x1="26" y1="16" x2="146" y2="16" /&gt;
  &lt;line x1="28" y1="18" x2="148" y2="18" /&gt;
  &lt;line x1="30" y1="20" x2="150" y2="20" /&gt;
  &lt;line x1="32" y1="22" x2="152" y2="22" /&gt;
  &lt;line x1="34" y1="24" x2="154" y2="24" /&gt;
  &lt;line x1="36" y1="26" x2="156" y2="26" /&gt;
  &lt;line x1="38" y1="28" x2="158" y2="28" /&gt;
  &lt;line x1="41" y1="31" x2="161" y2="31" /&gt;
  &lt;line x1="42" y1="32" x2="162" y2="32" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="10" y1="0" x2="42" y2="32" style="stroke-width:2" /&gt;
  &lt;line x1="130" y1="0" x2="162" y2="32" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="10.000000,0.000000 130.000000,0.000000 162.743566,32.743566 42.743566,32.743566" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="42" y1="32" x2="162" y2="32" style="stroke-width:2" /&gt;
  &lt;line x1="42" y1="152" x2="162" y2="152" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="42" y1="32" x2="42" y2="152" style="stroke-width:2" /&gt;
  &lt;line x1="162" y1="32" x2="162" y2="152" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="42.743566,32.743566 162.743566,32.743566 162.743566,152.743566 42.743566,152.743566" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Text --&gt;
&lt;p&gt;&lt;text x="102.743566" y="172.743566" font-size="1.0rem" font-weight="100" text-anchor="middle" &gt;2048&lt;/text&gt;
&lt;text x="182.743566" y="92.743566" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(-90,182.743566,92.743566)"&gt;2048&lt;/text&gt;
&lt;text x="16.371783" y="156.371783" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(45,16.371783,156.371783)"&gt;950&lt;/text&gt;
&lt;/svg&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;From the Dask output above you can see the data is a z-stack of 950 images
where each slice is 2048x2048. For this data set, we can improve GPU
performance if we operate on larger chunks. Additionally, we need to ensure
the chunks are are least as big as the PSF which in this case is, (128, 128,
128). As we did our work on a DGX2, which has 16 GPUs, we can comfortably fit
the data and perform deconvolution on each GPU if we &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;rechunk&lt;/span&gt;&lt;/code&gt; the data
accordingly:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# chunk with respect to PSF shape (128, 128, 128)&lt;/span&gt;
&lt;span class="n"&gt;imgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rechunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;190&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;imgs&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;td&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;th&gt; Array &lt;/th&gt;&lt;th&gt; Chunk &lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;th&gt; Bytes &lt;/th&gt;&lt;td&gt; 7.97 GB &lt;/td&gt; &lt;td&gt; 99.61 MB &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Shape &lt;/th&gt;&lt;td&gt; (950, 2048, 2048) &lt;/td&gt; &lt;td&gt; (190, 512, 512) &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Count &lt;/th&gt;&lt;td&gt; 967 Tasks &lt;/td&gt;&lt;td&gt; 80 Chunks &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Type &lt;/th&gt;&lt;td&gt; uint16 &lt;/td&gt;&lt;td&gt; numpy.ndarray &lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/td&gt;
&lt;p&gt;Next, we convert to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;float32&lt;/span&gt;&lt;/code&gt; as the data may not already be of floating point
type. Also 32-bit is a bit faster than 64-bit when computing and saves a bit on
memory. Below we map &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;cupy.asarray&lt;/span&gt;&lt;/code&gt; onto each block of data. &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;cupy.asarray&lt;/span&gt;&lt;/code&gt;
moves the data from host memory (NumPy) to the device/GPU (CuPy).&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;imgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;c_imgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asarray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;th&gt; Array &lt;/th&gt;&lt;th&gt; Chunk &lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;th&gt; Bytes &lt;/th&gt;&lt;td&gt; 15.94 GB &lt;/td&gt; &lt;td&gt; 199.23 MB &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Shape &lt;/th&gt;&lt;td&gt; (950, 2048, 2048) &lt;/td&gt; &lt;td&gt; (190, 512, 512) &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Count &lt;/th&gt;&lt;td&gt; 80 Tasks &lt;/td&gt;&lt;td&gt; 80 Chunks &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Type &lt;/th&gt;&lt;td&gt; float32 &lt;/td&gt;&lt;td&gt; cupy.ndarray &lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;svg width="212" height="202" style="stroke:rgb(0,0,0);stroke-width:1" &gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="10" y1="0" x2="42" y2="32" style="stroke-width:2" /&gt;
  &lt;line x1="10" y1="30" x2="42" y2="62" /&gt;
  &lt;line x1="10" y1="60" x2="42" y2="92" /&gt;
  &lt;line x1="10" y1="90" x2="42" y2="122" /&gt;
  &lt;line x1="10" y1="120" x2="42" y2="152" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="10" y1="0" x2="10" y2="120" style="stroke-width:2" /&gt;
  &lt;line x1="16" y1="6" x2="16" y2="126" /&gt;
  &lt;line x1="23" y1="13" x2="23" y2="133" /&gt;
  &lt;line x1="29" y1="19" x2="29" y2="139" /&gt;
  &lt;line x1="36" y1="26" x2="36" y2="146" /&gt;
  &lt;line x1="42" y1="32" x2="42" y2="152" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="10.0,0.0 42.74356617647059,32.74356617647059 42.74356617647059,152.74356617647058 10.0,120.0" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="10" y1="0" x2="130" y2="0" style="stroke-width:2" /&gt;
  &lt;line x1="16" y1="6" x2="136" y2="6" /&gt;
  &lt;line x1="23" y1="13" x2="143" y2="13" /&gt;
  &lt;line x1="29" y1="19" x2="149" y2="19" /&gt;
  &lt;line x1="36" y1="26" x2="156" y2="26" /&gt;
  &lt;line x1="42" y1="32" x2="162" y2="32" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="10" y1="0" x2="42" y2="32" style="stroke-width:2" /&gt;
  &lt;line x1="40" y1="0" x2="72" y2="32" /&gt;
  &lt;line x1="70" y1="0" x2="102" y2="32" /&gt;
  &lt;line x1="100" y1="0" x2="132" y2="32" /&gt;
  &lt;line x1="130" y1="0" x2="162" y2="32" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="10.0,0.0 130.0,0.0 162.74356617647058,32.74356617647059 42.74356617647059,32.74356617647059" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="42" y1="32" x2="162" y2="32" style="stroke-width:2" /&gt;
  &lt;line x1="42" y1="62" x2="162" y2="62" /&gt;
  &lt;line x1="42" y1="92" x2="162" y2="92" /&gt;
  &lt;line x1="42" y1="122" x2="162" y2="122" /&gt;
  &lt;line x1="42" y1="152" x2="162" y2="152" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="42" y1="32" x2="42" y2="152" style="stroke-width:2" /&gt;
  &lt;line x1="72" y1="32" x2="72" y2="152" /&gt;
  &lt;line x1="102" y1="32" x2="102" y2="152" /&gt;
  &lt;line x1="132" y1="32" x2="132" y2="152" /&gt;
  &lt;line x1="162" y1="32" x2="162" y2="152" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="42.74356617647059,32.74356617647059 162.74356617647058,32.74356617647059 162.74356617647058,152.74356617647058 42.74356617647059,152.74356617647058" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Text --&gt;
&lt;p&gt;&lt;text x="102.743566" y="172.743566" font-size="1.0rem" font-weight="100" text-anchor="middle" &gt;2048&lt;/text&gt;
&lt;text x="182.743566" y="92.743566" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(-90,182.743566,92.743566)"&gt;2048&lt;/text&gt;
&lt;text x="16.371783" y="156.371783" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(45,16.371783,156.371783)"&gt;950&lt;/text&gt;
&lt;/svg&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;What we now have is a Dask array composed of 16 CuPy blocks of data. Notice
how Dask provides nice typing information in the SVG output. When we moved
from NumPy to CuPy, the block diagram above displays &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Type:&lt;/span&gt; &lt;span class="pre"&gt;cupy.ndarray&lt;/span&gt;&lt;/code&gt; –
this is a nice sanity check.&lt;/p&gt;
&lt;p&gt;The last piece we need before running the deconvolution is the PSF which should
also be loaded onto the GPU:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;skimage.io&lt;/span&gt;

&lt;span class="n"&gt;psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;skimage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/public/NVMICROSCOPY/PSF.tif&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;c_psf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Lastly, we call &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_overlap&lt;/span&gt;&lt;/code&gt; with the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;deconvolve&lt;/span&gt;&lt;/code&gt; function across the Dask
array:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_overlap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;deconvolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;c_imgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;psf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;c_psf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;c_imgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_psf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;boundary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;periodic&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;out&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;a href="/images/deconvolve.png"&gt;
    &lt;img src="/images/deconvolve.png" width="100%"&gt;&lt;/a&gt;
&lt;p&gt;The image above is taken from a mouse intestine.&lt;/p&gt;
&lt;p&gt;With Dask and multiple GPUs, we measured deconvolution of an 16GB image in ~30
seconds! But this is just the first step towards accelerated image science.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/11/12/deconvolution.md&lt;/span&gt;, line 386)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="napari"&gt;
&lt;h1&gt;Napari&lt;/h1&gt;
&lt;p&gt;Deconvolution is just one operation and one tool, an image scientist or
microscopist will need. They will need other tools as they study the
underlying biology. Before getting to those next steps, they will need tools
to visualize the data. &lt;a class="reference external" href="https://napari.org/"&gt;Napari&lt;/a&gt;, a multi-dimensional image
viewer used in the PyData Bio ecosystem, is a good tool for visualizing this
data. As an experiment, we ran the same workflow on a local workstation with 2
Quadro RTX 8000 GPUs connected with NVLink. &lt;a class="reference external" href="https://gist.github.com/quasiben/02b3dabba8fb3415e40e685b3cb2ca4a"&gt;Example
Notebook&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;By adding a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_blocks&lt;/span&gt;&lt;/code&gt; call to our array, we can move our data &lt;em&gt;back&lt;/em&gt; from
GPU to CPU (device to host).&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;cupy_to_numpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cupy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cp&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;asnumpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;np_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cupy_to_numpy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;a href="/images/napari-deconv.png"&gt;
    &lt;img src="/images/napari-deconv.png" width="100%"&gt;&lt;/a&gt;
&lt;p&gt;When the user moves the slider on the Napari UI, we are instructing dask to the
following:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Load the data from disk onto the GPU (CuPy)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compute the deconvolution&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Move back to the host (NumPy)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Render with Napari&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This has about a second latency which is great for a naive implementation! We
can improve this by adding caching, improving communications with
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map_overlap&lt;/span&gt;&lt;/code&gt;, and optimizing the deconvolution kernel.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/11/12/deconvolution.md&lt;/span&gt;, line 423)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;We have now shown with Dask + CuPy how one can perform Richardson-Lucy
Deconvolution. This required a minimal amount of code. Combining this with an
image viewer (Napari), we were able to inspect the data and our result. All of
this performed reasonably well by assembling PyData libraries: Dask, CuPy,
Zarr, and Napari with a new deconvolution kernel. Hopefully this provides you
a good template to get started analyzing your own data and demonstrates the
richness and easy expression of custom workflows. If you run into any
challenges, please reach out on &lt;a class="reference external" href="https://github.com/dask/dask/issues"&gt;the Dask issue
tracker&lt;/a&gt; and we would be happy to engage
with you :)&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/11/12/deconvolution/"/>
    <summary>Document headings start at H2, not H1 [myst.header]</summary>
    <published>2020-11-12T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/09/22/user_survey/</id>
    <title>2020 Dask User Survey</title>
    <updated>2020-09-22T00:00:00+00:00</updated>
    <author>
      <name>Tom Augspurger</name>
    </author>
    <content type="html">&lt;style type="text/css"&gt;
table td {
    background: none;
}

table tr.even td {
    background: none;
}

table {
    text-shadow: none;
}

table tr:hover td {
    background: none;
}

&lt;/style&gt;
&lt;p&gt;This post presents the results of the 2020 Dask User Survey,
which ran earlier this summer. Thanks to everyone who took the time to fill out the survey!
These results help us better understand the Dask community and will guide future development efforts.&lt;/p&gt;
&lt;p&gt;The raw data, as well as the start of an analysis, can be found in this binder:&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://mybinder.org/v2/gh/dask/dask-examples/main?urlpath=%2Ftree%2Fsurveys%2F2020.ipynb"&gt;&lt;img alt="Binder" src="https://mybinder.org/badge_logo.svg" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let us know if you find anything in the data.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/09/22/user_survey.md&lt;/span&gt;, line 38)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="highlights"&gt;

&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;We had 240 responses to the survey (slightly fewer than last year, which had about 260).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Overall, results look mostly similar to last year’s.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Our documentation has probably improved relative to last year&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Respondents care more about performance relative to last year.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/09/22/user_survey.md&lt;/span&gt;, line 45)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="new-questions"&gt;
&lt;h1&gt;New Questions&lt;/h1&gt;
&lt;p&gt;Most of the questions are the same as in 2019. We added a couple questions about deployment and dashboard usage. Let’s look at those first.&lt;/p&gt;
&lt;p&gt;Among respondents who use a Dask package to deploy a cluster (about 53% of respondents), there’s a wide spread of methods.&lt;/p&gt;
&lt;img src="/images/2020_survey/2020_3_0.png"&gt;
&lt;p&gt;Most people access the dashboard through a web browser. Those not using the dashboard are likely (hopefully) just using Dask on a single machine with the threaded scheduler (though the dashboard works fine on a single machine as well).&lt;/p&gt;
&lt;img src="/images/2020_survey/2020_5_0.png"&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/09/22/user_survey.md&lt;/span&gt;, line 57)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="learning-resources"&gt;
&lt;h1&gt;Learning Resources&lt;/h1&gt;
&lt;p&gt;Respondents’ learning material usage is farily similar to last year. The most notable differences are from
our survey form providing more options (our &lt;a class="reference external" href="https://www.youtube.com/channel/UCj9eavqmvwaCyKhIlu2GaoA"&gt;YouTube channel&lt;/a&gt; and “Gitter chat”). Other than that, &lt;a class="reference external" href="https://examples.dask.org"&gt;examples.dask.org&lt;/a&gt; might be relatively more popular.&lt;/p&gt;
&lt;img src="/images/2020_survey/2020_7_0.png"&gt;
&lt;p&gt;Just like last year, we’ll look at resource usage grouped by how often they use Dask.&lt;/p&gt;
&lt;img src="/images/2020_survey/2020_10_0.png"&gt;
&lt;p&gt;A few observations&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;GitHub issues are becoming relatively less popular, which perhaps reflects better documentation or stability (assuming people go to the issue tracker when they can’t find the answer in the docs or they hit a bug).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://examples.dask.org"&gt;https://examples.dask.org&lt;/a&gt; is notably now more popular among occasinal users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In response to last year’s survey, we invested time in making &lt;a class="reference external" href="https://tutorial.dask.org"&gt;https://tutorial.dask.org&lt;/a&gt; better, which we previously felt was lacking. Its usage is still about the same as last year’s (pretty popular), so it’s unclear whether we should dedicate additional focus there.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/09/22/user_survey.md&lt;/span&gt;, line 74)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how-do-you-use-dask"&gt;
&lt;h1&gt;How do you use Dask?&lt;/h1&gt;
&lt;p&gt;API usage remains about the same as last year (recall that about 20 fewer people took the survey and people can select multiple, so relative differences are most interesting). We added new choices for RAPIDS, Prefect, and XGBoost, each of which are somewhat popular (in the neighborhood of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask.Bag&lt;/span&gt;&lt;/code&gt;).&lt;/p&gt;
&lt;img src="/images/2020_survey/2020_12_0.png"&gt;
&lt;p&gt;About 65% of our users are using Dask on a cluster at least some of the time, which is similar to last year.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/09/22/user_survey.md&lt;/span&gt;, line 82)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how-can-dask-improve"&gt;
&lt;h1&gt;How can Dask improve?&lt;/h1&gt;
&lt;p&gt;Respondents continue to say that more documentation and examples would be the most valuable improvements to the project.&lt;/p&gt;
&lt;p&gt;One interesting change comes from looking at “Which would help you most right now?” split by API group (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask.dataframe&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask.array&lt;/span&gt;&lt;/code&gt;, etc.). Last year showed that “More examples” in my field was the most important for all API groups (first table below). But in 2020 there are some differences (second table below).&lt;/p&gt;
&lt;style  type="text/css" &gt;
    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col0 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col1 {
            background-color:  #cacee5;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col3 {
            background-color:  #f1ebf4;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col4 {
            background-color:  #c4cbe3;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col0 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col1 {
            background-color:  #3b92c1;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col3 {
            background-color:  #62a2cb;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col4 {
            background-color:  #bdc8e1;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col0 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col1 {
            background-color:  #c2cbe2;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col3 {
            background-color:  #94b6d7;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col4 {
            background-color:  #e0dded;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col0 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col1 {
            background-color:  #e6e2ef;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col3 {
            background-color:  #ced0e6;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col4 {
            background-color:  #c5cce3;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col0 {
            background-color:  #dedcec;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col1 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col3 {
            background-color:  #1c7fb8;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col4 {
            background-color:  #73a9cf;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col0 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col1 {
            background-color:  #b4c4df;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col3 {
            background-color:  #b4c4df;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col4 {
            background-color:  #eee9f3;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col0 {
            background-color:  #faf2f8;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col1 {
            background-color:  #e7e3f0;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col3 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col4 {
            background-color:  #f4eef6;
            color:  #000000;
        }&lt;/style&gt;&lt;table id="T_0a8701b8_e96b_11ea_9e95_186590cd1c87" &gt;&lt;caption&gt;2019 normalized by row. Darker means that a higher proporiton of users of that API prefer that priority.&lt;/caption&gt;&lt;thead&gt;    &lt;tr&gt;        &lt;th class="index_name level0" &gt;Which would help you most right now?&lt;/th&gt;        &lt;th class="col_heading level0 col0" &gt;Bug fixes&lt;/th&gt;        &lt;th class="col_heading level0 col1" &gt;More documentation&lt;/th&gt;        &lt;th class="col_heading level0 col2" &gt;More examples in my field&lt;/th&gt;        &lt;th class="col_heading level0 col3" &gt;New features&lt;/th&gt;        &lt;th class="col_heading level0 col4" &gt;Performance improvements&lt;/th&gt;    &lt;/tr&gt;    &lt;tr&gt;        &lt;th class="index_name level0" &gt;Dask APIs&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;    &lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;div class="highlight-none notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;            &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87level0_row0&amp;quot; class=&amp;quot;row_heading level0 row0&amp;quot; &amp;gt;Array&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col0&amp;quot; class=&amp;quot;data row0 col0&amp;quot; &amp;gt;10&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col1&amp;quot; class=&amp;quot;data row0 col1&amp;quot; &amp;gt;24&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col2&amp;quot; class=&amp;quot;data row0 col2&amp;quot; &amp;gt;62&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col3&amp;quot; class=&amp;quot;data row0 col3&amp;quot; &amp;gt;15&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row0_col4&amp;quot; class=&amp;quot;data row0 col4&amp;quot; &amp;gt;25&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87level0_row1&amp;quot; class=&amp;quot;row_heading level0 row1&amp;quot; &amp;gt;Bag&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col0&amp;quot; class=&amp;quot;data row1 col0&amp;quot; &amp;gt;3&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col1&amp;quot; class=&amp;quot;data row1 col1&amp;quot; &amp;gt;11&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col2&amp;quot; class=&amp;quot;data row1 col2&amp;quot; &amp;gt;16&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col3&amp;quot; class=&amp;quot;data row1 col3&amp;quot; &amp;gt;10&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row1_col4&amp;quot; class=&amp;quot;data row1 col4&amp;quot; &amp;gt;7&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87level0_row2&amp;quot; class=&amp;quot;row_heading level0 row2&amp;quot; &amp;gt;DataFrame&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col0&amp;quot; class=&amp;quot;data row2 col0&amp;quot; &amp;gt;16&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col1&amp;quot; class=&amp;quot;data row2 col1&amp;quot; &amp;gt;32&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col2&amp;quot; class=&amp;quot;data row2 col2&amp;quot; &amp;gt;71&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col3&amp;quot; class=&amp;quot;data row2 col3&amp;quot; &amp;gt;39&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row2_col4&amp;quot; class=&amp;quot;data row2 col4&amp;quot; &amp;gt;26&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87level0_row3&amp;quot; class=&amp;quot;row_heading level0 row3&amp;quot; &amp;gt;Delayed&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col0&amp;quot; class=&amp;quot;data row3 col0&amp;quot; &amp;gt;16&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col1&amp;quot; class=&amp;quot;data row3 col1&amp;quot; &amp;gt;22&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col2&amp;quot; class=&amp;quot;data row3 col2&amp;quot; &amp;gt;55&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col3&amp;quot; class=&amp;quot;data row3 col3&amp;quot; &amp;gt;26&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row3_col4&amp;quot; class=&amp;quot;data row3 col4&amp;quot; &amp;gt;27&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87level0_row4&amp;quot; class=&amp;quot;row_heading level0 row4&amp;quot; &amp;gt;Futures&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col0&amp;quot; class=&amp;quot;data row4 col0&amp;quot; &amp;gt;12&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col1&amp;quot; class=&amp;quot;data row4 col1&amp;quot; &amp;gt;9&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col2&amp;quot; class=&amp;quot;data row4 col2&amp;quot; &amp;gt;25&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col3&amp;quot; class=&amp;quot;data row4 col3&amp;quot; &amp;gt;20&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row4_col4&amp;quot; class=&amp;quot;data row4 col4&amp;quot; &amp;gt;17&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87level0_row5&amp;quot; class=&amp;quot;row_heading level0 row5&amp;quot; &amp;gt;ML&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col0&amp;quot; class=&amp;quot;data row5 col0&amp;quot; &amp;gt;5&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col1&amp;quot; class=&amp;quot;data row5 col1&amp;quot; &amp;gt;11&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col2&amp;quot; class=&amp;quot;data row5 col2&amp;quot; &amp;gt;23&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col3&amp;quot; class=&amp;quot;data row5 col3&amp;quot; &amp;gt;11&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row5_col4&amp;quot; class=&amp;quot;data row5 col4&amp;quot; &amp;gt;7&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87level0_row6&amp;quot; class=&amp;quot;row_heading level0 row6&amp;quot; &amp;gt;Xarray&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col0&amp;quot; class=&amp;quot;data row6 col0&amp;quot; &amp;gt;8&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col1&amp;quot; class=&amp;quot;data row6 col1&amp;quot; &amp;gt;11&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col2&amp;quot; class=&amp;quot;data row6 col2&amp;quot; &amp;gt;34&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col3&amp;quot; class=&amp;quot;data row6 col3&amp;quot; &amp;gt;7&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8701b8_e96b_11ea_9e95_186590cd1c87row6_col4&amp;quot; class=&amp;quot;data row6 col4&amp;quot; &amp;gt;9&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;style  type="text/css" &gt;
    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col0 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col1 {
            background-color:  #f1ebf5;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col3 {
            background-color:  #f5eef6;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col4 {
            background-color:  #d0d1e6;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col0 {
            background-color:  #f0eaf4;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col1 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col3 {
            background-color:  #f0eaf4;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col4 {
            background-color:  #4c99c5;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col0 {
            background-color:  #f5eff6;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col1 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col3 {
            background-color:  #fcf4fa;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col4 {
            background-color:  #8eb3d5;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col0 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col1 {
            background-color:  #ebe6f2;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col3 {
            background-color:  #f5eff6;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col4 {
            background-color:  #3d93c2;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col0 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col1 {
            background-color:  #f5eef6;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col2 {
            background-color:  #0567a2;
            color:  #f1f1f1;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col3 {
            background-color:  #cacee5;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col4 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col0 {
            background-color:  #ede8f3;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col1 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col3 {
            background-color:  #c1cae2;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col4 {
            background-color:  #80aed2;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col0 {
            background-color:  #fff7fb;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col1 {
            background-color:  #f8f1f8;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col2 {
            background-color:  #023858;
            color:  #f1f1f1;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col3 {
            background-color:  #c9cee4;
            color:  #000000;
        }    #T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col4 {
            background-color:  #86b0d3;
            color:  #000000;
        }&lt;/style&gt;&lt;table id="T_0a8d3eac_e96b_11ea_9e95_186590cd1c87" &gt;&lt;caption&gt;2020 normalized by row. Darker means that a higher proporiton of users of that API prefer that priority.&lt;/caption&gt;&lt;thead&gt;    &lt;tr&gt;        &lt;th class="index_name level0" &gt;Which would help you most right now?&lt;/th&gt;        &lt;th class="col_heading level0 col0" &gt;Bug fixes&lt;/th&gt;        &lt;th class="col_heading level0 col1" &gt;More documentation&lt;/th&gt;        &lt;th class="col_heading level0 col2" &gt;More examples in my field&lt;/th&gt;        &lt;th class="col_heading level0 col3" &gt;New features&lt;/th&gt;        &lt;th class="col_heading level0 col4" &gt;Performance improvements&lt;/th&gt;    &lt;/tr&gt;    &lt;tr&gt;        &lt;th class="index_name level0" &gt;Dask APIs&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;        &lt;th class="blank" &gt;&lt;/th&gt;    &lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;div class="highlight-none notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;            &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87level0_row0&amp;quot; class=&amp;quot;row_heading level0 row0&amp;quot; &amp;gt;Array&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col0&amp;quot; class=&amp;quot;data row0 col0&amp;quot; &amp;gt;12&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col1&amp;quot; class=&amp;quot;data row0 col1&amp;quot; &amp;gt;16&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col2&amp;quot; class=&amp;quot;data row0 col2&amp;quot; &amp;gt;56&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col3&amp;quot; class=&amp;quot;data row0 col3&amp;quot; &amp;gt;15&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row0_col4&amp;quot; class=&amp;quot;data row0 col4&amp;quot; &amp;gt;23&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87level0_row1&amp;quot; class=&amp;quot;row_heading level0 row1&amp;quot; &amp;gt;Bag&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col0&amp;quot; class=&amp;quot;data row1 col0&amp;quot; &amp;gt;7&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col1&amp;quot; class=&amp;quot;data row1 col1&amp;quot; &amp;gt;5&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col2&amp;quot; class=&amp;quot;data row1 col2&amp;quot; &amp;gt;24&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col3&amp;quot; class=&amp;quot;data row1 col3&amp;quot; &amp;gt;7&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row1_col4&amp;quot; class=&amp;quot;data row1 col4&amp;quot; &amp;gt;16&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87level0_row2&amp;quot; class=&amp;quot;row_heading level0 row2&amp;quot; &amp;gt;DataFrame&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col0&amp;quot; class=&amp;quot;data row2 col0&amp;quot; &amp;gt;24&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col1&amp;quot; class=&amp;quot;data row2 col1&amp;quot; &amp;gt;21&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col2&amp;quot; class=&amp;quot;data row2 col2&amp;quot; &amp;gt;67&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col3&amp;quot; class=&amp;quot;data row2 col3&amp;quot; &amp;gt;22&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row2_col4&amp;quot; class=&amp;quot;data row2 col4&amp;quot; &amp;gt;41&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87level0_row3&amp;quot; class=&amp;quot;row_heading level0 row3&amp;quot; &amp;gt;Delayed&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col0&amp;quot; class=&amp;quot;data row3 col0&amp;quot; &amp;gt;15&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col1&amp;quot; class=&amp;quot;data row3 col1&amp;quot; &amp;gt;19&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col2&amp;quot; class=&amp;quot;data row3 col2&amp;quot; &amp;gt;46&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col3&amp;quot; class=&amp;quot;data row3 col3&amp;quot; &amp;gt;17&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row3_col4&amp;quot; class=&amp;quot;data row3 col4&amp;quot; &amp;gt;34&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87level0_row4&amp;quot; class=&amp;quot;row_heading level0 row4&amp;quot; &amp;gt;Futures&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col0&amp;quot; class=&amp;quot;data row4 col0&amp;quot; &amp;gt;9&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col1&amp;quot; class=&amp;quot;data row4 col1&amp;quot; &amp;gt;10&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col2&amp;quot; class=&amp;quot;data row4 col2&amp;quot; &amp;gt;21&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col3&amp;quot; class=&amp;quot;data row4 col3&amp;quot; &amp;gt;13&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row4_col4&amp;quot; class=&amp;quot;data row4 col4&amp;quot; &amp;gt;24&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87level0_row5&amp;quot; class=&amp;quot;row_heading level0 row5&amp;quot; &amp;gt;ML&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col0&amp;quot; class=&amp;quot;data row5 col0&amp;quot; &amp;gt;6&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col1&amp;quot; class=&amp;quot;data row5 col1&amp;quot; &amp;gt;4&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col2&amp;quot; class=&amp;quot;data row5 col2&amp;quot; &amp;gt;21&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col3&amp;quot; class=&amp;quot;data row5 col3&amp;quot; &amp;gt;9&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row5_col4&amp;quot; class=&amp;quot;data row5 col4&amp;quot; &amp;gt;12&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
                    &amp;lt;th id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87level0_row6&amp;quot; class=&amp;quot;row_heading level0 row6&amp;quot; &amp;gt;Xarray&amp;lt;/th&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col0&amp;quot; class=&amp;quot;data row6 col0&amp;quot; &amp;gt;3&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col1&amp;quot; class=&amp;quot;data row6 col1&amp;quot; &amp;gt;4&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col2&amp;quot; class=&amp;quot;data row6 col2&amp;quot; &amp;gt;25&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col3&amp;quot; class=&amp;quot;data row6 col3&amp;quot; &amp;gt;9&amp;lt;/td&amp;gt;
                    &amp;lt;td id=&amp;quot;T_0a8d3eac_e96b_11ea_9e95_186590cd1c87row6_col4&amp;quot; class=&amp;quot;data row6 col4&amp;quot; &amp;gt;13&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Examples are again the most important (for all API groups except &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Futures&lt;/span&gt;&lt;/code&gt;). But “Performance improvements” is now the second-most important improvement (except for &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Futures&lt;/span&gt;&lt;/code&gt; where it’s most important). How should we interpret this? A charitable interpretation is that Dask’s users are scaling to larger problems and are running into new scaling challenges. A less charitable interpretation is that our user’s workflows are the same but Dask is getting slower!&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/09/22/user_survey.md&lt;/span&gt;, line 422)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="what-other-systems-do-you-use"&gt;
&lt;h1&gt;What other systems do you use?&lt;/h1&gt;
&lt;p&gt;SSH continues to be the most popular “cluster resource mananger”. This was the big surprise last year, so we put in some work to make it nicer. Aside from that, not much has changed.&lt;/p&gt;
&lt;img src="/images/2020_survey/2020_25_0.png"&gt;
&lt;p&gt;And Dask users are about as happy with its stability as last year.&lt;/p&gt;
&lt;img src="/images/2020_survey/2020_27_0.png"&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/09/22/user_survey.md&lt;/span&gt;, line 432)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="takeaways"&gt;
&lt;h1&gt;Takeaways&lt;/h1&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Overall, most things are similar to last year.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Documentation, especially domain-specific examples, continues to be important. That said, our documentation is probably better than it was last year.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;More users are pushing Dask further. Investing in performance is likely to be valuable.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thanks again to all the respondents!&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/09/22/user_survey/"/>
    <summary>This post presents the results of the 2020 Dask User Survey,
which ran earlier this summer. Thanks to everyone who took the time to fill out the survey!
These results help us better understand the Dask community and will guide future development efforts.</summary>
    <category term="UserSurvey" label="User Survey"/>
    <published>2020-09-22T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/08/31/helm_daskhub/</id>
    <title>Announcing the DaskHub Helm Chart</title>
    <updated>2020-08-31T00:00:00+00:00</updated>
    <author>
      <name>Tom Augspurger</name>
    </author>
    <content type="html">&lt;p&gt;Today we’re announcing the release of the
&lt;a class="reference external" href="https://github.com/dask/helm-chart/blob/master/daskhub/README.md"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;daskhub&lt;/span&gt;&lt;/code&gt;&lt;/a&gt;
helm chart. This is a &lt;a class="reference external" href="https://helm.sh/"&gt;Helm&lt;/a&gt; chart to easily install
&lt;a class="reference external" href="https://jupyter.org/hub"&gt;JupyterHub&lt;/a&gt; and Dask for multiple users on a
Kubernetes Cluster. If you’re managing deployment for many people that needs
interactive, scalable computing (say for a class of students, a data science
team, or a research lab) then &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask/daskhub&lt;/span&gt;&lt;/code&gt; might be right for you.&lt;/p&gt;
&lt;p&gt;You can install &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask/daskhub&lt;/span&gt;&lt;/code&gt; on a Kubernetes cluster today with&lt;/p&gt;
&lt;div class="highlight-console notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="go"&gt;helm repo add dask https://helm.dask.org/&lt;/span&gt;
&lt;span class="go"&gt;helm repo update&lt;/span&gt;
&lt;span class="go"&gt;helm upgrade --install dhub dask/daskhub&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/31/helm_daskhub.md&lt;/span&gt;, line 26)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="history"&gt;

&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask/daskhub&lt;/span&gt;&lt;/code&gt; helm chart is an evolution of the &lt;a class="reference external" href="http://pangeo.io/"&gt;Pangeo&lt;/a&gt;
helm chart, which came out of that community’s attempts to do big data
geoscience on the cloud. We’re very grateful to have years of experience using
Dask and JupyterHub together. Pangeo was always aware that there wasn’t anything
geoscience-specific to their Helm chart and so were eager to contribute it to
Dask to share the maintenance burden. In the process of moving it over to Dask’s
chart repository we took the opportunity to clean up a few rough edges.&lt;/p&gt;
&lt;p&gt;It’s interesting to read the &lt;a class="reference external" href="https://blog.dask.org/2018/01/22/pangeo-2"&gt;original
announcement&lt;/a&gt; of Pangeo’s JupyterHub
deployment. A lot has improved, and we hope that this helm chart assists more
groups in deploying JupyterHubs capable of scalable computations with Dask.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/31/helm_daskhub.md&lt;/span&gt;, line 41)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="details"&gt;
&lt;h1&gt;Details&lt;/h1&gt;
&lt;p&gt;Internally, the DaskHub helm chart is relatively simple combination of the
&lt;a class="reference external" href="https://github.com/jupyterhub/zero-to-jupyterhub-k8s"&gt;JupyterHub&lt;/a&gt; and &lt;a class="reference external" href="https://github.com/dask/dask-gateway/"&gt;Dask
Gateway&lt;/a&gt; helm charts. The only additional
magic is some configuration to&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Register Dask Gateway as a &lt;a class="reference external" href="https://jupyterhub.readthedocs.io/en/stable/reference/services.html"&gt;JupyterHub
service&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set environment variables to make using Dask Gateway easy for your users.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With the default configuration, your users will be able to create and connect to
Dask Clusters, including their dashboards, with a simple&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_gateway&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GatewayCluster&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GatewayCluster&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Check out the
&lt;a class="reference external" href="https://docs.dask.org/en/latest/setup/kubernetes-helm.html"&gt;documentation&lt;/a&gt; for
details and let us know if you run into any difficulties.&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/08/31/helm_daskhub/"/>
    <summary>Today we’re announcing the release of the
daskhub
helm chart. This is a Helm chart to easily install
JupyterHub and Dask for multiple users on a
Kubernetes Cluster. If you’re managing deployment for many people that needs
interactive, scalable computing (say for a class of students, a data science
team, or a research lab) then dask/daskhub might be right for you.</summary>
    <category term="DaskGateway" label="Dask Gateway"/>
    <category term="Deployment" label="Deployment"/>
    <category term="Helm" label="Helm"/>
    <published>2020-08-31T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/08/21/running-tutorials/</id>
    <title>Running tutorials</title>
    <updated>2020-08-21T00:00:00+00:00</updated>
    <author>
      <name>Jacob Tomlinson (NVIDIA)</name>
    </author>
    <content type="html">&lt;p&gt;For the last couple of months we’ve been running community tutorials every three weeks or so. The response from the community has been great and we’ve had 50-100 people at each 90 minute session.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/21/running-tutorials.md&lt;/span&gt;, line 12)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="why-should-open-source-projects-run-tutorials"&gt;

&lt;p&gt;The Dask team has historically run tutorials at conferences such as SciPy. With 2020 turning out the way that it has much of this content is being presented virtually this year. As more people are becoming accustomed to participating in virtual tutorials we felt it would be a good service to our community to start running regular virtual tutorials independent of conferences we may be attending or speaking at.&lt;/p&gt;
&lt;p&gt;Tutorials are great for open source projects as they appeal to multiple types of learner.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;The tutorial material provides a great foundation for &lt;em&gt;written and visual learners&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using an interactive tool like Jupyter Notebooks allows &lt;em&gt;kinesthetic learners&lt;/em&gt; to follow along and take their own paths.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Having an instructor run through the material in real time provides a spoken source for &lt;em&gt;auditory learners&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It’s also just fun to have a bunch of people from around the world participate in a live event. There is a greater sense of community.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many open source projects provide documentation, some also make instructional videos on YouTube, but you really can’t beat a tutorial for producing a single set of content that is valuable to many users.&lt;/p&gt;
&lt;p&gt;The more users can share knowledge, information and skills with the more they are going to use and engage with the project. Having a great source of learning material is critical for converting interested newcomers to users and users to contributors.&lt;/p&gt;
&lt;p&gt;It is also great for the maintainers too. Dask is a large project made up of many open source repositories all with different functions. Each maintainer tends to participate in their specialist areas, but do not engage with everything on a day-to-day basis. Having maintainers run tutorials encourages them to increase their knowledge of areas they rarely touch in order to deliver the material, and this benefits the project as a whole.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/21/running-tutorials.md&lt;/span&gt;, line 29)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how"&gt;
&lt;h1&gt;How&lt;/h1&gt;
&lt;p&gt;For the rest of this post we will discuss the preparation and logistics we have undertaken to provide our tutorials. Hopefully this will provide a blueprint for others waning to run similar activities.&lt;/p&gt;
&lt;section id="writing-the-material"&gt;
&lt;h2&gt;Writing the material&lt;/h2&gt;
&lt;p&gt;When starting to compile material is it important to consider a few questions; “Who is this for?”, “How long should it be?” and “What already exists today?”.&lt;/p&gt;
&lt;p&gt;For the Dask tutorial we were targeting users who were either new to Dask, or had been using it for a while but wanted to learn more about the wider project. Dask is a large project after all and there are many features that you may not discover when trying to solve your specific challenges with it.&lt;/p&gt;
&lt;p&gt;At large conferences is it quite normal to run a three hour tutorial, however when trying to schedule a tutorial as part of a person’s normal working day that is probably too much to ask of them. Folks are accustomed to scheduling in work meetings that are typically 30-60 minutes, but that may not be enough to run a tutorial. So we settled on 90 minutes, enough to get through a good amount of content, but not too long that folks will be put off.&lt;/p&gt;
&lt;p&gt;We already have an &lt;a class="reference external" href="https://github.com/dask/dask-tutorial"&gt;“official” tutorial&lt;/a&gt; which is designed to fill the three hours of a SciPy tutorial. This tutorial is also designed as a “Dask from first principals” style tutorial where we explore how Dask works and eventually scale up to how Dask implements familiar APIs like Numpy and Pandas. This is great for giving folks a thorough understanding of Dask but given that we decided on 90 minutes we may not want to start with low level code as we may run out of time before getting to general usage.&lt;/p&gt;
&lt;p&gt;While researching what already exists I was pointed to the &lt;a class="reference external" href="https://github.com/adbreind/dask-mini-2019"&gt;Mini Dask 2019 tutorial&lt;/a&gt; which was created for an &lt;a class="reference external" href="https://www.oreilly.com/live-training/courses/scale-your-python-processing-with-dask/0636920319573/"&gt;O’Reilly event&lt;/a&gt;. This tutorial starts with familiar APIs such as dataframes and arrays and eventually digs down into Dask fundamentals. As tutorial content like this is often licensed as open source and made available on GitHub it’s great to be able to build upon the work of others.&lt;/p&gt;
&lt;p&gt;The result of combining the two tutorials was the &lt;a class="reference external" href="https://github.com/jacobtomlinson/dask-video-tutorial-2020"&gt;Dask Video Tutorial 2020&lt;/a&gt;. It follows the same structure as the mini tutorial starting with high level APIs and digging further down. It also includes some new content on deployment and distributed methods.&lt;/p&gt;
&lt;section id="structuring-content"&gt;
&lt;h3&gt;Structuring content&lt;/h3&gt;
&lt;p&gt;To ensure this content targets the different learner types that we discussed earlier we need to ensure our content has a few things.&lt;/p&gt;
&lt;p&gt;As a foundation we should put together a series of pages/documents with a written version of the information we are trying to communicate for &lt;em&gt;written learners&lt;/em&gt;. We should also endeavor to include diagrams and pictures to illustrate this information for &lt;em&gt;visual learners&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;As we are sharing knowledge on an open source software project we should also make things as interactive as possible. Using Jupyter Notebooks as our document format means we can include many code examples which both provide written examples but are also editable and executable to empower &lt;em&gt;kinesthetic learners&lt;/em&gt; to feel how things work in practice.&lt;/p&gt;
&lt;p&gt;When the content is being delivered the instructor will be running through the content at the same time and narrating what they are doing for &lt;em&gt;auditory learners&lt;/em&gt;. It is important to try and structure things in a way where you explain each section of the content out loud, but without directly reading the text from the screen as that can be off-putting.&lt;/p&gt;
&lt;p&gt;We also want to ensure folks are taking things in, and labs are a great way to include small tests in the content. Having a section at the end of an example which is incomplete means that you can give the audience some time to try and figure things our for themselves. Some folks will be able to fill things in with no problems. For others they will hit errors or make mistakes, this is good for teaching how to debug and troubleshoot your project. And for those who are having awful flashbacks to pop-quizzes they can simply skip it without worrying that someone will check up on them.&lt;/p&gt;
&lt;p&gt;For each section of content you want to include in your tutorial I recommend you create a notebook with an explanation, an example and some things for the audience to figure out. Doing this for each section (in the Dask tutorial we had 9 sections) the audience will quickly become familiar with the process and be able to anticipate what is coming next. This will make them feel comfortable.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="hosting-the-material"&gt;
&lt;h2&gt;Hosting the material&lt;/h2&gt;
&lt;p&gt;Once you have put your material together you need to share it with your attendees.&lt;/p&gt;
&lt;p&gt;GitHub is a great place to put things, especially if you include an open license with it. For narrative tutorial content a creative commons license if often used which requires modifications to also be shared.&lt;/p&gt;
&lt;p&gt;As we have put our content together as Jupyter Notebooks we can use &lt;a class="reference external" href="https://mybinder.org/"&gt;Binder&lt;/a&gt; to make it possible for folks to run the material without having to download it locally or ensure their Python environment is set up correctly.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="choosing-a-video-platform"&gt;
&lt;h2&gt;Choosing a video platform&lt;/h2&gt;
&lt;p&gt;Next we have to decide how we will present the material. As this is a virtual tutorial we will want to use some kind of video conferences or streaming software.&lt;/p&gt;
&lt;p&gt;These tools tend to fall into two categories; private meetings with a tool like Zoom, Hangouts or Teams and public broadcasts on websites like YouTube or Twitch.&lt;/p&gt;
&lt;p&gt;Any of these options will likely be a good choice, they allow the presenter to share their video, audio and screen with participants and participants can communicate back with a range of tools.&lt;/p&gt;
&lt;p&gt;The main decision you will have to make is around whether you want to restrict numbers or not. The more interactivity you want to have in the tutorial the more you will need a handle on numbers. For our initial tutorials we wanted to enable participants to ask questions at any time and get a quick response, so we opted to use Zoom and limit our numbers to allow us to not get overwhelmed with questions. However if you want to present to as many people as possible and accept that you may not be able to address them all individually you may want to use a streaming platform instead.&lt;/p&gt;
&lt;p&gt;It is also possible to do both at the same time. Zoom can stream directly to YouTube for example. This can be useful if you want to open things to as many folks as possible, but also limit the interactivity to a select group (probably on a first-come-first-served basis). For the Dask tutorials we decided to not livestream and instead run multiple tutorials so that everyone gets an interactive experience, but we are fortunate to have the resources to do that.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="registering-attendees"&gt;
&lt;h2&gt;Registering attendees&lt;/h2&gt;
&lt;p&gt;There are a couple of reasons why you may wish to register attendees ahead of time.&lt;/p&gt;
&lt;p&gt;If you want to limit numbers you will certainly need some way to register people and put a cap on that number. But even if you are streaming generally you may want to get folks to register ahead of time as that allows you to send them reminder emails in the run up to the event, which likely will add more certainty to the attendance numbers.&lt;/p&gt;
&lt;p&gt;As our event was private we registered folks with &lt;a class="reference external" href="https://www.eventbrite.com/"&gt;Eventbrite&lt;/a&gt;. This allowed us to cap numbers and also schedule automated emails to act as a reminder but also share the details of the private Zoom meeting.&lt;/p&gt;
&lt;p&gt;When running the Dask tutorials we found about 50% of the folks who registered actually turned up, so we accounted for this an set out limit to around double the number we wanted.&lt;/p&gt;
&lt;p&gt;Here’s an example of the event details what we created:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;&lt;strong&gt;Event Title&lt;/strong&gt;: Dask Tutorial&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Organizer&lt;/strong&gt;: Presenter’s name&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Event Type&lt;/strong&gt;: Seminar or talk, Science and Technology, Online event&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tags&lt;/strong&gt;: dask, pydata, python, tutorial&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Location&lt;/strong&gt;: Online event&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date and time&lt;/strong&gt;: Single Event, add times&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Details&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;Come learn about Dask at this online free tutorial provided by the Dask maintainers.&lt;/p&gt;
&lt;p&gt;This ninety minute course will mix overview discussion and demonstration by a leader in the Dask community, as well as interactive exercises in live notebook sessions for attendees. The computing environment will be provided.&lt;/p&gt;
&lt;p&gt;If you want to get a sample of similar content, take a look at https://tutorial.dask.org (although this tutorial will cover different material appropriate for this shorter session).&lt;/p&gt;
&lt;p&gt;We look forward to seeing you there!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Image&lt;/strong&gt;: https://i.imgur.com/2i1tMNG.png&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live video content&lt;/strong&gt;: NA&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Text and media&lt;/strong&gt;: NA&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Links to resources&lt;/strong&gt;:
Tutorial Content (Online Jupyter Notebooks)
https://github.com/jacobtomlinson/dask-video-tutorial-2020&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ticket Cost&lt;/strong&gt;: Free&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ticket Attendee limit&lt;/strong&gt;: 150 people&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="count-down-to-the-tutorial"&gt;
&lt;h2&gt;Count down to the tutorial&lt;/h2&gt;
&lt;p&gt;We also set up a series of automated emails. You can find this under &lt;strong&gt;Manage Attendees &amp;gt; Emails to Attendees&lt;/strong&gt; in the event management page.&lt;/p&gt;
&lt;p&gt;We scheduled emails for two days before, two hours before and 10 minutes before to let folks know where to go and another a few hours after to gather feedback. &lt;em&gt;We will discuss the feedback email shortly&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You’ll need to ensure you have links to the materials and meeting location ready for this. In our case we pushed the content to GitHub and scheduled the Zoom call ahead of time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Two days and two hours before&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Hi Everyone!&lt;/p&gt;
&lt;p&gt;We look forward to seeing you &amp;lt;tomorrow|soon&amp;gt;. We wanted to share some important links with you to help you connect to the meeting.&lt;/p&gt;
&lt;p&gt;The materials for the course are available on GitHub here at the link below:&lt;/p&gt;
&lt;p&gt;&amp;lt;Link to materials&amp;gt;&lt;/p&gt;
&lt;p&gt;This repository contains Jupyter notebooks that we’ll go through together as a group. You do not need to install anything before the tutorial. We will run the notebooks on the online service, mybinder.org . All you need is a web connection.&lt;/p&gt;
&lt;p&gt;The meeting itself will be held by video call at the following Zoom link:&lt;/p&gt;
&lt;p&gt;&amp;lt;Zoom link and pin&amp;gt;&lt;/p&gt;
&lt;p&gt;We look forward to seeing you soon!&lt;/p&gt;
&lt;p&gt;&amp;lt;Organisers names&amp;gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ten minutes before&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Hi Everyone!&lt;/p&gt;
&lt;p&gt;We are about to get started. Here’s a final reminder of the meeting details.&lt;/p&gt;
&lt;p&gt;&amp;lt;Zoom link and pin&amp;gt;&lt;/p&gt;
&lt;p&gt;See you in a minute!&lt;/p&gt;
&lt;p&gt;&amp;lt;Organisers names&amp;gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Few hours after&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Hi Everyone!&lt;/p&gt;
&lt;p&gt;Thank you so much for attending the Dask tutorial. We really hope you found it valuable.&lt;/p&gt;
&lt;p&gt;We would really appreciate it if you could answer a couple of quick feedback questions to help us improve things for next time.&lt;/p&gt;
&lt;p&gt;&amp;lt;Google form link &amp;gt;&lt;/p&gt;
&lt;p&gt;Also we want to remind you that the tutorial materials are always available on GitHub and you can run through them any time or share them with others.&lt;/p&gt;
&lt;p&gt;&amp;lt;Link to materials&amp;gt;&lt;/p&gt;
&lt;p&gt;Thanks,&lt;/p&gt;
&lt;p&gt;&amp;lt;Organisers names&amp;gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/section&gt;
&lt;section id="getting-the-word-out"&gt;
&lt;h2&gt;Getting the word out&lt;/h2&gt;
&lt;p&gt;Now that we have an Eventbrite page we need to tell people about it.&lt;/p&gt;
&lt;p&gt;You may already have existing channels where you can contact your community. For Dask we have an active twitter account with a good number of followers, so tweeting out the link to the event a couple of times the week running up to the tutorial was enough to fill the spaces.&lt;/p&gt;
&lt;p&gt;If you have a mailing list, or any other platform you will probably want to share it there.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="setting-up-the-call"&gt;
&lt;h2&gt;Setting up the call&lt;/h2&gt;
&lt;p&gt;Be sure to join the call ahead of the attendees. I would make sure this is at least before the final reminder email goes out. Personally I join 20 minutes or so before hand. This allows you to ensure the call is being recorded and that attendees were muted when they join.&lt;/p&gt;
&lt;p&gt;Consider the experience of the user’s here. They will have signed up for an event online, received a few emails with Zoom call details and then they will join the call. If there is no indication that they are in the right place within a few seconds they may become anxious.&lt;/p&gt;
&lt;p&gt;To combat this I tend to show some graphic which lets people know they are in the right place. You could either use a tool like &lt;a class="reference external" href="https://jacobtomlinson.dev/posts/2020/how-to-use-obs-studio-with-zoom-hangouts-teams-and-more-on-macos/"&gt;OBS with Zoom&lt;/a&gt; to create a custom scene or just share your screen with a simple slide saying something like “The Dask tutorial will start soon”.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The only downside to sharing your screen is you can’t continue to use your computer in the run up to the tutorial.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When we ran our first few tutorials we were also running our Dask user survey so also included a link to that on the waiting screen to give folks something to do.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="greeting-and-getting-folks-set-up"&gt;
&lt;h2&gt;Greeting and getting folks set up&lt;/h2&gt;
&lt;p&gt;Say hi on the hour and welcome everyone to the tutorial. But as the event is virtual folks will be late, so don’t kick off until around five minutes in, otherwise you’ll just get a flood of questions asking what’s going on.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="interactivity"&gt;
&lt;h2&gt;Interactivity&lt;/h2&gt;
&lt;p&gt;A fun thing to do during this waiting period is get everyone to introduce themselves in the chat. Say something like “Please say hi in that chat and give your name and where you are joining from”.&lt;/p&gt;
&lt;p&gt;This is nice feedback for you as the instructor to see where folks are joining from, but it also gives the attendees a sense of being in a room full of people. One of the benefits of an event like this is that it is interactive, so be sure to say hi back to people.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I’m awful at pronouncing names correctly so I tend to list the places they said they are from instead. It still makes them feel like their message has been seen.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once you’re ready to start introduce yourself and a general overview of the tutorial content. Then make use of any interaction tools you may have in your chat application. In zoom there are buttons that participants can click with labels like “go faster”, “go slower”, “yes” and “no”. These are great for getting feedback from the audience when running the tutorial, but it’s good to make sure everyone knows where they are and has a go at using them. I tend to explain where the buttons are and then ask questions like “have you managed to launch the binder?”, “have you used Dask before?” or “are you a Pandas user?”. You learn a little about your audience and they get familiar with the controls.&lt;/p&gt;
&lt;p&gt;Being interactive means you can also respond to user questions. In Dask tutorials we mute everyone by default and encourage folks to type in the text chat. We also have an additional instructor who is not delivering the material who is able to watch the chat and answer questions in real time. If they feel like a question/answer would be beneficial to the whole group they can unmute and interrupt the presenter in order to bubble it up. Be prepared for a wide range of questions from the chat, including topics that are not being actively covered in the tutorial. This is often the only time that attendees have real-time access to core maintainers.&lt;/p&gt;
&lt;p&gt;You may not have the resources to have two instructors for every tutorial, Dask is fortunate to have a strong maintainer team, so instead you may want to allocate breaks at the end of each section to answer questions. During the labs can be a good time to go back and review any questions.&lt;/p&gt;
&lt;p&gt;Interactivity is one of the big benefits a live tutorial has over a video.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="run-through-the-material"&gt;
&lt;h2&gt;Run through the material&lt;/h2&gt;
&lt;p&gt;Once you’re all set up and everyone is in it’s time to run through the material. Given the amount of preparation we did before hand to construct the material this is relatively straight forward. Everything is laid out in front of us and we just need to go through the motions of talking through it.&lt;/p&gt;
&lt;p&gt;I find it very helpful to have a list of the sections with timings written down that I can refer to in order to pace things.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Overview of Dask with Dask Dataframe (10 mins)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Introductory Lab (10 mins) and results (5 mins)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dask GUI and dashboards (10 mins)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dask Array (10 mins)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dask ML with lab (10 mins) and results (5 mins)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bags and Futures (10 mins)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Distributed (10 mins)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wrapup and close (5 mins)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;As we have another instructor answering questions I tend to ignore the chat and run through each section as slowly as I can without going over time. Personally my default is to go too fast, so forcing myself to be slow but having some timings to keep me on track seems to work well. But you should do whatever works for you.&lt;/p&gt;
&lt;p&gt;During the labs I tend to mute my microphone and join in with answering questions on the chat.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="wrapping-things-up"&gt;
&lt;h2&gt;Wrapping things up&lt;/h2&gt;
&lt;p&gt;When you’re nearing the end it’s good to have some time for any final questions. People may want to ask things that they didn’t get a chance to earlier or have questions which haven’t fit in with any particular area.&lt;/p&gt;
&lt;p&gt;If you get complex questions or want to go in to depth you may want to offer to stay after and continue talking, but your attendees will appreciate you finishing at the scheduled time as they may have other things booked immediately after.&lt;/p&gt;
&lt;p&gt;It’s always good to leave folks with some extra resources, whether that is links to the documentation, community places they can learn more like a Gitter chat, etc.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="sharing-the-content-later"&gt;
&lt;h2&gt;Sharing the content later&lt;/h2&gt;
&lt;p&gt;Once you’re done it is also beneficial to upload a recording of the tutorial to YouTube. If you’ve livestreamed then this may happen automatically. If you used a tool like Zoom you’ll need to upload it to yourself.&lt;/p&gt;
&lt;p&gt;Anyone watching in the future won’t get the benefit of the interactivity, but should still be able to get much of the benefit from following through the material.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="gathering-feedback-and-planning-for-next-time"&gt;
&lt;h2&gt;Gathering feedback and planning for next time&lt;/h2&gt;
&lt;p&gt;The last thing for you to do is plan for next time. The Dask team have decided to run tutorials every month or so but rotate around timezones to try and cover as many users as possible. We’ve also discussed having special deep dive tutorials which follow the same length and format but dive into one topic in particular.&lt;/p&gt;
&lt;p&gt;To help you plan for future events you will likely want feedback from your participants. You can use tools like Google Forms to create a short questionnaire which you can send out to participants afterwards. In our experience about 20% of participants will fill in a survey that is 10 questions long.&lt;/p&gt;
&lt;p&gt;This feedback can be very helpful for making changes to the content or format. For example in our first tutorial we use OBS for both the intro screen and screen sharing throughout. However Zoom limits webcams to 720p and adds heavy compression, so the quality for participants was not good and 50% of the surveys mentioned poor video. In later tutorials we only used OBS for the intro screen and then used the built in screen sharing utility in Zoom which provided a better experience and no user reported any audio/video issues in the survey.&lt;/p&gt;
&lt;p&gt;Here are some examples of questions we asked and how they were answered for our tutorial.&lt;/p&gt;
&lt;section id="have-you-used-dask-before"&gt;
&lt;h3&gt;Have you used Dask before?&lt;/h3&gt;
&lt;p&gt;When writing our material we said we were “targeting users who were either new to Dask, or had been using it for a while but wanted to learn more about the wider project.”. Our feedback results confirm that we are hitting these groups.&lt;/p&gt;
&lt;p&gt;We could’ve been more specific and asked folks to rank their ability. But the more complex the questions the less likely folks will fill them out, so it’s a balancing act.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Forms response chart. Question title: Have you used Dask before? 39% no, 61% yes." src="https://i.imgur.com/T1loyeb.png" /&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="did-we-cover-all-the-topics-you-were-expecting-and-if-not-what-was-missing"&gt;
&lt;h3&gt;Did we cover all the topics you were expecting? And if not, what was missing?&lt;/h3&gt;
&lt;p&gt;Depending on the complexity of your project you may have to make compromises on what you can cover in the time you have. Dask is a large project and so we couldn’t cover everything, so we wanted to check we had covered the basics.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Forms response chart. Question title: Did we cover all the topics you expected? 22% no, 78% yes." src="https://i.imgur.com/la3dqrA.png" /&gt;&lt;/p&gt;
&lt;p&gt;Most of the feedback we had from folks who answered no were asking about advanced topics like Kubernetes, Google Cloud deployments, deep dives into internal workings, etc. I’m satisfied that this shouldn’t have been in this tutorial, but it adds weight to our plans to run deep dives in the future.&lt;/p&gt;
&lt;p&gt;Once useful bit of feedback we had here was “When should I use Dask and when should I stick with Pandas?”. This is something which definitely should be covered by an intro tutorial, so our material is clearly lacking here. As a result we can go back and make modifications and improve the content.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="how-was-the-pace"&gt;
&lt;h3&gt;How was the pace?&lt;/h3&gt;
&lt;p&gt;Setting the pace is hard. If you’re targeting a range of abilities then it’s easy to go too fast or slow for a big chunk of the attendees.&lt;/p&gt;
&lt;p&gt;Our feedback shows that folks were generally happy, but we are leaning on the side of being too fast. Given that we are filling our allocated time this probably indicates that we should cut a little content in order to slow things down.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Forms response chart. Question title: How was the pace? 70% Just right, 26% Too fast, 4% Too slow." src="https://i.imgur.com/mHPNmwp.png" /&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="which-sections-did-you-find-more-informative"&gt;
&lt;h3&gt;Which sections did you find more informative?&lt;/h3&gt;
&lt;p&gt;By asking what sections were most informative we can identify things to cut in future if we do need to slow things down. It also shows areas where we may want to spend more time and add more content.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Forms response chart. Question title: Which sections did you find more informative?" src="https://i.imgur.com/XLzSEw4.png" /&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="what-would-be-your-preferred-platform-for-a-tutorial-like-this"&gt;
&lt;h3&gt;What would be your preferred platform for a tutorial like this?&lt;/h3&gt;
&lt;p&gt;We had to make a decision on which video platform to use based on the criteria we discussed earlier. For our tutorials we chose Zoom. By doing a user survey we were able to check that this worked for people and also see if there is an alternative that folks prefer.&lt;/p&gt;
&lt;p&gt;Our results confirmed that folks were happy with Zoom. These results may be a little biased given that we used Zoom, but I’m confident that we can keep using it and folks will have a good experience.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Forms response chart. Question title: What would be your preferred platform for a tutorial like this? 70% Zoom, &amp;lt;5% for options including YouTube, Twitch, Jitsi, and No preference" src="https://i.imgur.com/fMxTZOK.png" /&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="would-you-recommend-the-tutorial-to-a-colleague"&gt;
&lt;h3&gt;Would you recommend the tutorial to a colleague?&lt;/h3&gt;
&lt;p&gt;The last thing to check is that folks had a good time. It gives you great pleasure as an instructor to see 100% of folks say they would recommend to a colleague.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;These results may be biased because if folks wouldn’t recommend it they probably wouldn’t bother to fill out a survey. But hey, I’ll take it!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Forms response chart. Question title: Would you recommend the tutorial to a colleague? 100% Yes." src="https://i.imgur.com/RzrXvfn.png" /&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/21/running-tutorials.md&lt;/span&gt;, line 318)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="wrap-up"&gt;
&lt;h1&gt;Wrap up&lt;/h1&gt;
&lt;p&gt;In this post we have covered why and how you can run community tutorials for open source projects.&lt;/p&gt;
&lt;p&gt;In summary you should run tutorials because:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;You can share knowledge with a range of people with different learning styles&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can give back to your community&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can grow your community&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can improve maintainers knowledge of the whole project&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And you can run a tutorial by following these steps:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Break your project into sections&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write up interactive documents on each section with tools like Jupyter notebooks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Give people access to this content with services like Binder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manage attendees with services like Eventbrite&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Advertise your tutorial on social media&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get everyone in a video meeting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make use of the interactive tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deliver your material&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gather feedback&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/08/21/running-tutorials/"/>
    <summary>For the last couple of months we’ve been running community tutorials every three weeks or so. The response from the community has been great and we’ve had 50-100 people at each 90 minute session.</summary>
    <category term="Community" label="Community"/>
    <category term="Tutorials" label="Tutorials"/>
    <published>2020-08-21T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/08/06/ray-tune/</id>
    <title>Comparing Dask-ML and Ray Tune's Model Selection Algorithms</title>
    <updated>2020-08-06T00:00:00+00:00</updated>
    <author>
      <name>&lt;a href="https://stsievert.com"&gt;Scott Sievert&lt;/a&gt; (University of Wisconsin–Madison)</name>
    </author>
    <content type="html">&lt;p&gt;Hyperparameter optimization is the process of deducing model parameters that
can’t be learned from data. This process is often time- and resource-consuming,
especially in the context of deep learning. A good description of this process
can be found at “&lt;a class="reference external" href="https://scikit-learn.org/stable/modules/grid_search.html"&gt;Tuning the hyper-parameters of an estimator&lt;/a&gt;,” and
the issues that arise are concisely summarized in Dask-ML’s documentation of
“&lt;a class="reference external" href="https://ml.dask.org/hyper-parameter-search.html"&gt;Hyper Parameter Searches&lt;/a&gt;.”&lt;/p&gt;
&lt;p&gt;There’s a host of libraries and frameworks out there to address this problem.
&lt;a class="reference external" href="https://scikit-learn.org/stable/modules/grid_search.html"&gt;Scikit-Learn’s module&lt;/a&gt; has been mirrored &lt;a class="reference external" href="https://ml.dask.org/hyper-parameter-search.html"&gt;in Dask-ML&lt;/a&gt; and
&lt;a class="reference external" href="https://automl.github.io/auto-sklearn/master/"&gt;auto-sklearn&lt;/a&gt;, both of which offer advanced hyperparameter optimization
techniques. Other implementations that don’t follow the Scikit-Learn interface
include &lt;a class="reference external" href="https://docs.ray.io/en/master/tune.html"&gt;Ray Tune&lt;/a&gt;, &lt;a class="reference external" href="https://www.automl.org/"&gt;AutoML&lt;/a&gt; and &lt;a class="reference external" href="https://medium.com/optuna/optuna-supports-hyperband-93b0cae1a137"&gt;Optuna&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://docs.ray.io"&gt;Ray&lt;/a&gt; recently provided a wrapper to &lt;a class="reference external" href="https://docs.ray.io/en/master/tune.html"&gt;Ray Tune&lt;/a&gt; that mirrors the Scikit-Learn
API called tune-sklearn (&lt;a class="reference external" href="https://docs.ray.io/en/master/tune/api_docs/sklearn.html"&gt;docs&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/ray-project/tune-sklearn"&gt;source&lt;/a&gt;). &lt;a class="reference external" href="https://medium.com/distributed-computing-with-ray/gridsearchcv-2-0-new-and-improved-ee56644cbabf"&gt;The introduction&lt;/a&gt; of this library
states the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Cutting edge hyperparameter tuning techniques (Bayesian optimization, early
stopping, distributed execution) can provide significant speedups over grid
search and random search.&lt;/p&gt;
&lt;p&gt;However, the machine learning ecosystem is missing a solution that provides
users with the ability to leverage these new algorithms while allowing users
to stay within the Scikit-Learn API. In this blog post, we introduce
tune-sklearn [Ray’s tuning library] to bridge this gap. Tune-sklearn is a
drop-in replacement for Scikit-Learn’s model selection module with
state-of-the-art optimization features.&lt;/p&gt;
&lt;p&gt;—&lt;a class="reference external" href="https://medium.com/distributed-computing-with-ray/gridsearchcv-2-0-new-and-improved-ee56644cbabf"&gt;GridSearchCV 2.0 — New and Improved&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;This claim is inaccurate: for over a year Dask-ML has provided access to
“cutting edge hyperparameter tuning techniques” with a Scikit-Learn compatible
API. To correct their statement, let’s look at each of the features that Ray’s
tune-sklearn provides, and compare them to Dask-ML:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Here’s what [Ray’s] tune-sklearn has to offer:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistency with Scikit-Learn API&lt;/strong&gt; …&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modern hyperparameter tuning techniques&lt;/strong&gt; …&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Framework support&lt;/strong&gt; …&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scale up&lt;/strong&gt; … [to] multiple cores and even multiple machines.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;[Ray’s] Tune-sklearn is also &lt;strong&gt;fast&lt;/strong&gt;.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Dask-ML’s model selection module has every one of the features:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistency with Scikit-Learn API:&lt;/strong&gt; Dask-ML’s model selection API
mirrors the Scikit-Learn model selection API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modern hyperparameter tuning techniques:&lt;/strong&gt; Dask-ML offers state-of-the-art
hyperparameter tuning techniques.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Framework support:&lt;/strong&gt; Dask-ML model selection supports many libraries
including Scikit-Learn, PyTorch, Keras, LightGBM and XGBoost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scale up:&lt;/strong&gt; Dask-ML supports distributed tuning (how could it not?) and
larger-than-memory datasets.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dask-ML is also &lt;strong&gt;fast.&lt;/strong&gt; In “&lt;a class="reference internal" href="#speed"&gt;&lt;span class="xref myst"&gt;Speed&lt;/span&gt;&lt;/a&gt;” we show a benchmark between
Dask-ML, Ray and Scikit-Learn:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2020-model-selection/n_workers=8.png" width="450px"
 /&gt;&lt;/p&gt;
&lt;p&gt;Only time-to-solution is relevant; all of these methods produce similar model
scores. See “&lt;a class="reference internal" href="#speed"&gt;&lt;span class="xref myst"&gt;Speed&lt;/span&gt;&lt;/a&gt;” for details.&lt;/p&gt;
&lt;p&gt;Now, let’s walk through the details on how to use Dask-ML to obtain the 5
features above.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/06/ray-tune.md&lt;/span&gt;, line 95)&lt;/p&gt;
&lt;p&gt;Document headings start at H3, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="consistency-with-the-scikit-learn-api"&gt;

&lt;p&gt;&lt;em&gt;Dask-ML is consistent with the Scikit-Learn API.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here’s how to use Scikit-Learn’s, Dask-ML’s and Ray’s tune-sklearn
hyperparameter optimization:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;## Trimmed example; see appendix for more detail&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sklearn.model_selection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomizedSearchCV&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RandomizedSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_ml.model_selection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HyperbandSearchCV&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HyperbandSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;tune_sklearn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TuneSearchCV&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TuneSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The definitions of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;model&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;params&lt;/span&gt;&lt;/code&gt; follow the normal Scikit-Learn
definitions as detailed in the &lt;a class="reference internal" href="#full-example-usage"&gt;&lt;span class="xref myst"&gt;appendix&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Clearly, both Dask-ML and Ray’s tune-sklearn are Scikit-Learn compatible. Now
let’s focus on how each search performs and how it’s configured.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/06/ray-tune.md&lt;/span&gt;, line 126)&lt;/p&gt;
&lt;p&gt;Document headings start at H3, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="modern-hyperparameter-tuning-techniques"&gt;
&lt;h1&gt;Modern hyperparameter tuning techniques&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Dask-ML offers state-of-the-art hyperparameter tuning techniques
in a Scikit-Learn interface.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://medium.com/distributed-computing-with-ray/gridsearchcv-2-0-new-and-improved-ee56644cbabf"&gt;The introduction&lt;/a&gt; of Ray’s tune-sklearn made this claim:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;tune-sklearn is the only
Scikit-Learn interface that allows you to easily leverage Bayesian
Optimization, HyperBand and other optimization techniques by simply toggling a few parameters.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;The state-of-the-art in hyperparameter optimization is currently
“&lt;a class="reference external" href="https://arxiv.org/pdf/1603.06560.pdf"&gt;Hyperband&lt;/a&gt;.” Hyperband reduces the amount of computation
required with a &lt;em&gt;principled&lt;/em&gt; early stopping scheme; past that, it’s the same as
Scikit-Learn’s popular &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;RandomizedSearchCV&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Hyperband &lt;em&gt;works.&lt;/em&gt; As such, it’s very popular. After the introduction of
Hyperband in 2016 by Li et. al, &lt;a class="reference external" href="https://arxiv.org/pdf/1603.06560.pdf"&gt;the paper&lt;/a&gt; has been cited
&lt;a class="reference external" href="https://scholar.google.com/scholar?cites=10473284631669296057&amp;amp;amp;as_sdt=5,39&amp;amp;amp;sciodt=0,39&amp;amp;amp;hl=en"&gt;over 470 times&lt;/a&gt; and has been implemented in many different libraries
including &lt;a class="reference external" href="https://ml.dask.org/modules/generated/dask_ml.model_selection.HyperbandSearchCV.html#dask_ml.model_selection.HyperbandSearchCV"&gt;Dask-ML&lt;/a&gt;, &lt;a class="reference external" href="https://docs.ray.io/en/master/tune/api_docs/schedulers.html#asha-tune-schedulers-ashascheduler"&gt;Ray Tune&lt;/a&gt;, &lt;a class="reference external" href="https://keras-team.github.io/keras-tuner/documentation/tuners/#hyperband-class"&gt;keras-tune&lt;/a&gt;, &lt;a class="reference external" href="https://medium.com/optuna/optuna-supports-hyperband-93b0cae1a137"&gt;Optuna&lt;/a&gt;,
&lt;a class="reference external" href="https://www.automl.org/"&gt;AutoML&lt;/a&gt;,&lt;a class="footnote-reference brackets" href="#automl" id="id1" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;1&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; and &lt;a class="reference external" href="https://nni.readthedocs.io/en/latest/Tuner/HyperbandAdvisor.html"&gt;Microsoft’s NNI&lt;/a&gt;. The original paper shows a
rather drastic improvement over all the relevant
implementations,&lt;a class="footnote-reference brackets" href="#hyperband-figs" id="id2" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;2&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; and this drastic improvement persists in
follow-up works.&lt;a class="footnote-reference brackets" href="#follow-up" id="id3" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;3&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; Some illustrative results from Hyperband are
below:&lt;/p&gt;
&lt;p&gt;&lt;img width="80%" src="/images/2020-model-selection/hyperband-fig-7-8.png"
 style="display: block; margin-left: auto; margin-right: auto;" /&gt;&lt;/p&gt;
&lt;div style="max-width: 80%; word-wrap: break-word;" style="text-align: center;"&gt;
&lt;p&gt;&lt;sup&gt;All algorithms are configured to do the same amount of work except “random
2x” which does twice as much work. “hyperband (finite)” is similar Dask-ML’s
default implementation, and “bracket s=4” is similar to Ray’s default
implementation. “random” is a random search. SMAC,&lt;a class="footnote-reference brackets" href="#smac" id="id4" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;4&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt;
spearmint,&lt;a class="footnote-reference brackets" href="#spearmint" id="id5" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;5&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; and TPE&lt;a class="footnote-reference brackets" href="#tpe" id="id6" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;6&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; are popular Bayesian algorithms. &lt;/sup&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Hyperband is undoubtedly a “cutting edge” hyperparameter optimization
technique. Dask-ML and Ray offer Scikit-Learn implementations of this algorithm
that rely on similar implementations, and Dask-ML’s implementation also has a
&lt;a class="reference external" href="https://ml.dask.org/hyper-parameter-search.html#hyperband-parameters-rule-of-thumb"&gt;rule of thumb&lt;/a&gt; for configuration. Both Dask-ML’s and Ray’s documentation
encourages use of Hyperband.&lt;/p&gt;
&lt;p&gt;Ray does support using their Hyperband implementation on top of a technique
called Bayesian sampling. This changes the hyperparameter sampling scheme for
model initialization. This can be used in conjunction with Hyperband’s early
stopping scheme. Adding this option to Dask-ML’s Hyperband implementation is
future work for Dask-ML.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/06/ray-tune.md&lt;/span&gt;, line 222)&lt;/p&gt;
&lt;p&gt;Document headings start at H3, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="framework-support"&gt;
&lt;h1&gt;Framework support&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Dask-ML model selection supports many libraries including Scikit-Learn, PyTorch, Keras, LightGBM and XGBoost.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ray’s tune-sklearn supports these frameworks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;tune-sklearn is used primarily for tuning
Scikit-Learn models, but it also supports and provides examples for many
other frameworks with Scikit-Learn wrappers such as Skorch (Pytorch),
KerasClassifiers (Keras), and XGBoostClassifiers (XGBoost).&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Clearly, both Dask-ML and Ray support the many of the same libraries.&lt;/p&gt;
&lt;p&gt;However, both Dask-ML and Ray have some qualifications. Certain libraries don’t
offer an implementation of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;partial_fit&lt;/span&gt;&lt;/code&gt;,&lt;a class="footnote-reference brackets" href="#ray-pf" id="id7" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;7&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; so not all of the modern
hyperparameter optimization techniques can be offered. Here’s a table comparing
different libraries and their support in Dask-ML’s model selection and Ray’s
tune-sklearn:&lt;/p&gt;
&lt;div class="pst-scrollable-table-container"&gt;&lt;table class="table"&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head text-center"&gt;&lt;p&gt;Model Library&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-center"&gt;&lt;p&gt;Dask-ML support&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-center"&gt;&lt;p&gt;Ray support&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-center"&gt;&lt;p&gt;Dask-ML: early stopping?&lt;/p&gt;&lt;/th&gt;
&lt;th class="head text-center"&gt;&lt;p&gt;Ray: early stopping?&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-center"&gt;&lt;p&gt;&lt;a class="reference external" href="https://scikit-learn.org/"&gt;Scikit-Learn&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔*&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔*&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="text-center"&gt;&lt;p&gt;&lt;a class="reference external" href="https://pytorch.org/"&gt;PyTorch&lt;/a&gt; (via &lt;a class="reference external" href="https://skorch.readthedocs.io/"&gt;Skorch&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-center"&gt;&lt;p&gt;&lt;a class="reference external" href="https://keras.io/"&gt;Keras&lt;/a&gt; (via &lt;a class="reference external" href="https://github.com/adriangb/scikeras"&gt;SciKeras&lt;/a&gt;)&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔**&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔**&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td class="text-center"&gt;&lt;p&gt;&lt;a class="reference external" href="https://lightgbm.readthedocs.io/"&gt;LightGBM&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;❌&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;❌&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td class="text-center"&gt;&lt;p&gt;&lt;a class="reference external" href="https://xgboost.ai/"&gt;XGBoost&lt;/a&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;✔&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;❌&lt;/p&gt;&lt;/td&gt;
&lt;td class="text-center"&gt;&lt;p&gt;❌&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;sup&gt;* Only for &lt;a class="reference external" href="https://scikit-learn.org/stable/modules/computing.html#incremental-learning"&gt;the models that implement &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;partial_fit&lt;/span&gt;&lt;/code&gt;&lt;/a&gt;.&lt;/sup&gt;&lt;br&gt;
&lt;sup&gt;** Thanks to work by the Dask developers around &lt;a class="reference external" href="https://github.com/adriangb/scikeras/issues/24"&gt;scikeras#24&lt;/a&gt;.&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;By this measure, Dask-ML and Ray model selection have the same level of
framework support. Of course, Dask has tangential integration with LightGBM and
XGBoost through &lt;a class="reference external" href="https://ml.dask.org/xgboost.html"&gt;Dask-ML’s &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;xgboost&lt;/span&gt;&lt;/code&gt; module&lt;/a&gt; and &lt;a class="reference external" href="https://github.com/dask/dask-lightgbm"&gt;dask-lightgbm&lt;/a&gt;.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/06/ray-tune.md&lt;/span&gt;, line 272)&lt;/p&gt;
&lt;p&gt;Document headings start at H3, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="scale-up"&gt;
&lt;h1&gt;Scale up&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Dask-ML supports distributed tuning (how could it not?), aka parallelization
across multiple machines/cores. In addition, it also supports
larger-than-memory data.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;[Ray’s] Tune-sklearn leverages Ray Tune, a library for distributed
hyperparameter tuning, to efficiently and transparently parallelize cross
validation on multiple cores and even multiple machines.&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Naturally, Dask-ML also scales to multiple cores/machines because it relies on
Dask. Dask has wide support for &lt;a class="reference external" href="https://docs.dask.org/en/latest/setup.html"&gt;different deployment options&lt;/a&gt; that span
from your personal machine to supercomputers. Dask will very likely work on top
of any computing system you have available, including Kubernetes, SLURM, YARN
and Hadoop clusters as well as your personal machine.&lt;/p&gt;
&lt;p&gt;Dask-ML’s model selection also scales to larger-than-memory datasets, and is
thoroughly tested. Support for larger-than-memory data is untested in Ray, and
there are no examples detailing how to use Ray Tune with the distributed
dataset implementations in PyTorch/Keras.&lt;/p&gt;
&lt;p&gt;In addition, I have benchmarked Dask-ML’s model selection module to see how the
time-to-solution is affected by the number of Dask workers in “&lt;a class="reference external" href="https://blog.dask.org/2019/09/30/dask-hyperparam-opt"&gt;Better and
faster hyperparameter optimization with Dask&lt;/a&gt;.” That is, how does the
time to reach a particular accuracy scale with the number of workers &lt;span class="math notranslate nohighlight"&gt;\(P\)&lt;/span&gt;? At
first, it’ll scale like &lt;span class="math notranslate nohighlight"&gt;\(1/P\)&lt;/span&gt; but with large number of workers the serial
portion will dictate time to solution according to &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Amdahl%27s_law"&gt;Amdahl’s Law&lt;/a&gt;. Briefly, I
found Dask-ML’s &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;HyperbandSearchCV&lt;/span&gt;&lt;/code&gt; speedup started to saturate around 24
workers for a particular search.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/06/ray-tune.md&lt;/span&gt;, line 311)&lt;/p&gt;
&lt;p&gt;Document headings start at H3, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="speed"&gt;
&lt;h1&gt;Speed&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Both Dask-ML and Ray are much faster than Scikit-Learn.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ray’s tune-sklearn runs some benchmarks in &lt;a class="reference external" href="https://medium.com/distributed-computing-with-ray/gridsearchcv-2-0-new-and-improved-ee56644cbabf"&gt;the introduction&lt;/a&gt; with the
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;GridSearchCV&lt;/span&gt;&lt;/code&gt; class found in Scikit-Learn and Dask-ML. A more fair benchmark
would be use Dask-ML’s &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;HyperbandSearchCV&lt;/span&gt;&lt;/code&gt; because it is almost the same as the
algorithm in Ray’s tune-sklearn. To be specific, I’m interested in comparing
these methods:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Scikit-Learn’s &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;RandomizedSearchCV&lt;/span&gt;&lt;/code&gt;. This is a popular implementation, one
that I’ve bootstrapped myself with a custom model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dask-ML’s &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;HyperbandSearchCV&lt;/span&gt;&lt;/code&gt;. This is an early stopping technique for
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;RandomizedSearchCV&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ray tune-sklearn’s &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;TuneSearchCV&lt;/span&gt;&lt;/code&gt;. This is a slightly different early
stopping technique than &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;HyperbandSearchCV&lt;/span&gt;&lt;/code&gt;’s.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each search is configured to perform the same task: sample 100 parameters and
train for no longer than 100 “epochs” or passes through the
data.&lt;a class="footnote-reference brackets" href="#random-search" id="id8" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;8&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; Each estimator is configured as their respective
documentation suggests. Each search uses 8 workers with a single cross
validation split, and a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;partial_fit&lt;/span&gt;&lt;/code&gt; call takes one second with 50,000
examples. The complete setup can be found in &lt;a class="reference internal" href="#appendix"&gt;&lt;span class="xref myst"&gt;the appendix&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here’s how long each library takes to complete the same search:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/2020-model-selection/n_workers=8.png" width="450px"
 /&gt;&lt;/p&gt;
&lt;p&gt;Notably, we didn’t improve the Dask-ML codebase for this benchmark, and ran the
code as it’s been for the last year.&lt;a class="footnote-reference brackets" href="#priority-impl" id="id9" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;9&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; Regardless, it’s possible that
other artifacts from &lt;a class="reference external" href="http://matthewrocklin.com/blog/work/2017/03/09/biased-benchmarks"&gt;biased benchmarks&lt;/a&gt; crept into this benchmark.&lt;/p&gt;
&lt;p&gt;Clearly, Ray and Dask-ML offer similar performance for 8 workers when compared
with Scikit-Learn. To Ray’s credit, their implementation is ~15% faster than
Dask-ML’s with 8 workers. We suspect that this performance boost comes from the
fact that Ray implements an asynchronous variant of Hyperband. We should
investigate this difference between Dask and Ray, and how each balances the
tradeoffs, number FLOPs vs. time-to-solution. This will vary with the number
of workers: the asynchronous variant of Hyperband provides no benefit if used
with a single worker.&lt;/p&gt;
&lt;p&gt;Dask-ML reaches scores quickly in serial environments, or when the number of
workers is small. Dask-ML prioritizes fitting high scoring models: if there are
100 models to fit and only 4 workers available, Dask-ML selects the models that
have the highest score. This is most relevant in serial
environments;&lt;a class="footnote-reference brackets" href="#priority" id="id10" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;10&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt; see “&lt;a class="reference external" href="https://blog.dask.org/2019/09/30/dask-hyperparam-opt"&gt;Better and faster hyperparameter optimization
with Dask&lt;/a&gt;” for benchmarks. This feature is omitted from this
benchmark, which only focuses on time to solution.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/06/ray-tune.md&lt;/span&gt;, line 377)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="conclusion"&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Dask-ML and Ray offer the same features for model selection: state-of-the-art
features with a Scikit-Learn compatible API, and both implementations have
fairly wide support for different frameworks and rely on backends that can
scale to many machines.&lt;/p&gt;
&lt;p&gt;In addition, the Ray implementation has provided motivation for further
development, specifically on the following items:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Adding support for more libraries, including Keras&lt;/strong&gt; (&lt;a class="reference external" href="https://github.com/dask/dask-ml/issues/696"&gt;dask-ml#696&lt;/a&gt;,
&lt;a class="reference external" href="https://github.com/dask/dask-ml/pull/713"&gt;dask-ml#713&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/adriangb/scikeras/issues/24"&gt;scikeras#24&lt;/a&gt;). SciKeras is a Scikit-Learn wrapper for
Keras that (now) works with Dask-ML model selection because SciKeras models
implement the Scikit-Learn model API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Better documenting the models that Dask-ML supports&lt;/strong&gt;
(&lt;a class="reference external" href="https://github.com/dask/dask-ml/pull/699"&gt;dask-ml#699&lt;/a&gt;). Dask-ML supports any model that implement the
Scikit-Learn interface, and there are wrappers for Keras, PyTorch, LightGBM
and XGBoost. Now, &lt;a class="reference external" href="https://ml.dask.org"&gt;Dask-ML’s documentation&lt;/a&gt; prominently highlights this
fact.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Ray implementation has also helped motivate and clarify future work.
Dask-ML should include the following implementations:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A Bayesian sampling scheme for the Hyperband implementation&lt;/strong&gt; that’s
similar to Ray’s and BOHB’s (&lt;a class="reference external" href="https://github.com/dask/dask-ml/issues/697"&gt;dask-ml#697&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A configuration of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;HyperbandSearchCV&lt;/span&gt;&lt;/code&gt; that’s well-suited for
exploratory hyperparameter searches.&lt;/strong&gt; An initial implementation is in
&lt;a class="reference external" href="https://github.com/dask/dask-ml/pull/532"&gt;dask-ml#532&lt;/a&gt;, which should be benchmarked against Ray.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Luckily, all of these pieces of development are straightforward modifications
because the Dask-ML model selection framework is pretty flexible.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Thank you &lt;a class="reference external" href="https://github.com/TomAugspurger"&gt;Tom Augspurger&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/mrocklin"&gt;Matthew Rocklin&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/jsignell"&gt;Julia Signell&lt;/a&gt;, and &lt;a class="reference external" href="https://github.com/quasiben"&gt;Benjamin
Zaitlen&lt;/a&gt; for your feedback, suggestions and edits.&lt;/em&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/06/ray-tune.md&lt;/span&gt;, line 427)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="appendix"&gt;
&lt;h1&gt;Appendix&lt;/h1&gt;
&lt;section id="benchmark-setup"&gt;
&lt;h2&gt;Benchmark setup&lt;/h2&gt;
&lt;p&gt;This is the complete setup for the benchmark between Dask-ML, Scikit-Learn and
Ray. Complete details can be found at
&lt;a class="reference external" href="https://github.com/stsievert/dask-hyperband-comparison"&gt;stsievert/dask-hyperband-comparison&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let’s create a dummy model that takes 1 second for a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;partial_fit&lt;/span&gt;&lt;/code&gt; call with
50,000 examples. This is appropriate for this benchmark; we’re only interested
in the time required to finish the search, not how well the models do.
Scikit-learn, Ray and Dask-ML have have very similar methods of choosing
hyperparameters to evaluate; they differ in their early stopping techniques.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;scipy.stats&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uniform&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sklearn.model_selection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;make_classification&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;benchmark&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConstantFunction&lt;/span&gt;  &lt;span class="c1"&gt;# custom module&lt;/span&gt;

&lt;span class="c1"&gt;# This model sleeps for `latency * len(X)` seconds before&lt;/span&gt;
&lt;span class="c1"&gt;# reporting a score of `value`.&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConstantFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;50e3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_iter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_iter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="c1"&gt;# This dummy dataset mirrors the MNIST dataset&lt;/span&gt;
&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_classification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;60e3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;n_features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;784&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This model will take 2 minutes to train for 100 epochs (aka passes through the
data). Details can be found at &lt;a class="reference external" href="https://github.com/stsievert/dask-hyperband-comparison"&gt;stsievert/dask-hyperband-comparison&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let’s configure our searches to use 8 workers with a single cross-validation
split:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sklearn.model_selection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomizedSearchCV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ShuffleSplit&lt;/span&gt;
&lt;span class="n"&gt;split&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ShuffleSplit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_splits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;kwargs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RandomizedSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_jobs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_iter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;n_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 20.88 minutes&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_ml.model_selection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HyperbandSearchCV&lt;/span&gt;
&lt;span class="n"&gt;dask_search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HyperbandSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_iter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_iter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aggressiveness&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;tune_sklearn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TuneSearchCV&lt;/span&gt;
&lt;span class="n"&gt;ray_search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TuneSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_iter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;n_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_iters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_iter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;early_stopping&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;dask_search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 2.93 minutes&lt;/span&gt;
&lt;span class="n"&gt;ray_search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 2.49 minutes&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="full-example-usage"&gt;
&lt;h2&gt;Full example usage&lt;/h2&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sklearn.linear_model&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SGDClassifier&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;scipy.stats&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loguniform&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sklearn.datasets&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;make_classification&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SGDClassifier&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;alpha&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;loguniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1e-5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1e-3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;l1_ratio&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;make_classification&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;sklearn.model_selection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RandomizedSearchCV&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RandomizedSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_ml.model_selection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HyperbandSearchCV&lt;/span&gt;
&lt;span class="n"&gt;HyperbandSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;tune_sklearn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TuneSearchCV&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TuneSearchCV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;hr class="docutils" /&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/08/06/ray-tune.md&lt;/span&gt;, line 40)&lt;/p&gt;
&lt;p&gt;Duplicate reference definition: TSNE [myst.duplicate_def]&lt;/p&gt;
&lt;/aside&gt;
&lt;hr class="footnotes docutils" /&gt;
&lt;aside class="footnote-list brackets"&gt;
&lt;aside class="footnote brackets" id="automl" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id1"&gt;1&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Their implementation of Hyperband in &lt;a class="reference external" href="https://github.com/automl/HpBandSter"&gt;HpBandSter&lt;/a&gt; is included in &lt;a class="reference external" href="https://www.automl.org/wp-content/uploads/2018/09/chapter7-autonet.pdf"&gt;Auto-PyTorch&lt;/a&gt; and &lt;a class="reference external" href="https://github.com/automl/BOAH"&gt;BOAH&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="hyperband-figs" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id2"&gt;2&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;See Figures 4, 7 and 8 in “&lt;a class="reference external" href="https://arxiv.org/pdf/1603.06560.pdf"&gt;Hyperband: A Novel Bandit-Based Approach to Hyperparameter Optimization&lt;/a&gt;.”&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="follow-up" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id3"&gt;3&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;See Figure 1 of &lt;a class="reference external" href="http://proceedings.mlr.press/v80/falkner18a/falkner18a.pdf"&gt;the BOHB paper&lt;/a&gt; and &lt;a class="reference external" href="https://arxiv.org/pdf/1801.01596.pdf"&gt;a paper&lt;/a&gt; from an augmented reality company.&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="smac" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id4"&gt;4&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;SMAC is described in “&lt;a class="reference external" href="https://www.cs.ubc.ca/~hutter/papers/10-TR-SMAC.pdf"&gt;Sequential Model-Based Optimization forGeneral Algorithm Configuration&lt;/a&gt;,” and is available &lt;a class="reference external" href="https://www.automl.org/automated-algorithm-design/algorithm-configuration/smac/"&gt;in AutoML&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="spearmint" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id5"&gt;5&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Spearmint is described in “&lt;a class="reference external" href="https://papers.nips.cc/paper/4522-practical-bayesian-optimization-of-machine-learning-algorithms.pdf"&gt;Practical Bayesian Optimization of MachineLearning Algorithms&lt;/a&gt;,” and is available in &lt;a class="reference external" href="https://github.com/HIPS/Spearmint"&gt;HIPS/spearmint&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="tpe" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id6"&gt;6&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;TPE is described in Section 4 of “&lt;a class="reference external" href="http://papers.nips.cc/paper/4443-algorithms-for-hyper-parameter-optimization.pdf"&gt;Algorithms for Hyperparameter Optimization&lt;/a&gt;,” and is available &lt;a class="reference external" href="http://hyperopt.github.io/hyperopt/"&gt;through Hyperopt&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="ray-pf" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id7"&gt;7&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;From &lt;a class="reference external" href="https://github.com/ray-project/tune-sklearn/blob/31f228e21ef632a89a74947252d8ad5323cbd043/README.md"&gt;Ray’s README.md&lt;/a&gt;: “If the estimator does not support &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;partial_fit&lt;/span&gt;&lt;/code&gt;, a warning will be shown saying early stopping cannot be done and it will simply run the cross-validation on Ray’s parallel back-end.”&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="random-search" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id8"&gt;8&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;I choose to benchmark random searches instead of grid searches because random searches produce better results because grid searches require estimating how important each parameter is; for more detail see “&lt;a class="reference external" href="http://www.jmlr.org/papers/volume13/bergstra12a/bergstra12a.pdf"&gt;Random Search for Hyperparameter Optimization&lt;/a&gt;” by Bergstra and Bengio.&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="priority-impl" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id9"&gt;9&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Despite a relevant implementation in &lt;a class="reference external" href="https://github.com/dask/dask-ml/pull/527"&gt;dask-ml#527&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="priority" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#id10"&gt;10&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Because priority is meaningless if there are an infinite number of workers.&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="bohb-exps" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;11&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Details are in “&lt;a class="reference external" href="http://proceedings.mlr.press/v80/falkner18a/falkner18a.pdf"&gt;BOHB: Robust and Efficient Hyperparameter Optimization at Scale&lt;/a&gt;.”&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="nlp-future" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;12&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Future work is combining this with the Dask-ML’s Hyperband implementation.&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="openai" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;13&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Computing &lt;a class="reference external" href="https://en.wikipedia.org/wiki/N-gram"&gt;n-grams&lt;/a&gt; requires a ton of memory and computation. For OpenAI, NLP preprocessing took 8 GPU-months! (&lt;a class="reference external" href="https://openai.com/blog/language-unsupervised/#drawbacks"&gt;source&lt;/a&gt;)&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="stopping" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;14&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;Hyperband’s theory answers “how many models should be stopped?” and “when should they be stopped?”&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="bohb-parallel" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;15&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;In Section 4.2 of &lt;a class="reference external" href="http://proceedings.mlr.press/v80/falkner18a/falkner18a.pdf"&gt;their paper&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;/aside&gt;
</content>
    <link href="https://blog.dask.org/2020/08/06/ray-tune/"/>
    <summary>Hyperparameter optimization is the process of deducing model parameters that
can’t be learned from data. This process is often time- and resource-consuming,
especially in the context of deep learning. A good description of this process
can be found at “Tuning the hyper-parameters of an estimator,” and
the issues that arise are concisely summarized in Dask-ML’s documentation of
“Hyper Parameter Searches.”</summary>
    <category term="dask" label="dask"/>
    <category term="dask-ml" label="dask-ml"/>
    <category term="machine-learning" label="machine-learning"/>
    <category term="ray" label="ray"/>
    <published>2020-08-06T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/07/30/beginners-config/</id>
    <title>Configuring a Distributed Dask Cluster</title>
    <updated>2020-07-30T00:00:00+00:00</updated>
    <author>
      <name>Julia Signell (Saturn Cloud)</name>
    </author>
    <content type="html">&lt;p&gt;&lt;em&gt;Configuring a Dask cluster can seem daunting at first, but the good news is that the Dask project has a lot of built in heuristics that try its best to anticipate and adapt to your workload based on the machine it is deployed on and the work it receives. Possibly for a long time you can get away with not configuring anything special at all. That being said, if you are looking for some tips to move on from using Dask locally, or have a Dask cluster that you are ready to optimize with some more in-depth configuration, these tips and tricks will help guide you and link you to the best Dask docs on the topic!&lt;/em&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/30/beginners-config.md&lt;/span&gt;, line 12)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="how-to-host-a-distributed-dask-cluster"&gt;

&lt;p&gt;The biggest jump for me was from running a local version of Dask for just an hour or so at a time during development, to standing up a production-ready version of Dask. Broadly there are two styles:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;a static dask cluster – one that is always on, always awake, always ready to accept work&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;an ephemeral dask cluster – one that is spun up or down easily with a Python API, and, when on, starts a minimal dask master node that itself only spins up dask workers when work is actually submitted&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Though those are the two broad main categories, there are tons of choices of how to actually achieve that. It depends on a number of factors including what cloud provider products you want to use and if those resources are pre-provisioned for you and whether you want to use a python API or a different deployment tool to actually start the Dask processes. A very exhaustive list of all the different ways you could provision a dask cluster is in the dask docs under &lt;a class="reference external" href="https://docs.dask.org/en/latest/setup.html"&gt;Setup&lt;/a&gt;. As just a taste of what is described in those docs, you could:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Install and start up the dask processes &lt;a class="reference external" href="https://docs.dask.org/en/latest/setup/cli.html"&gt;manually from the CLI&lt;/a&gt; on cloud instances you provision, such as AWS EC2 or GCP GCE&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use popular deployment interfaces such as &lt;a class="reference external" href="https://docs.dask.org/en/latest/setup/kubernetes-helm.html"&gt;helm for kubernetes&lt;/a&gt; to deploy dask in cloud container clusters you provision, such as AWS Fargate or GCP GKE&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use ‘native’ deployment python APIs, provided by the dask developers, to create (and interactively configure) dask on deployment infrastructure they support, either through the general-purpose &lt;a class="reference external" href="https://gateway.dask.org/"&gt;Dask Gateway&lt;/a&gt; which supports multiple backends, or directly against cluster managers such as kubernetes with &lt;a class="reference external" href="https://kubernetes.dask.org/en/latest/"&gt;dask-kubernetes&lt;/a&gt; or YARN with &lt;a class="reference external" href="https://yarn.dask.org/en/latest/"&gt;dask-yarn&lt;/a&gt;, as long as you’ve already provisioned the kubernetes cluster or hadoop cluster already&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use a nearly full-service deployment python API called &lt;a class="reference external" href="https://cloudprovider.dask.org/en/latest/"&gt;Dask Cloud Provider&lt;/a&gt;, that will go one step farther and provision the cluster for you too, as long as you give it AWS credentials (and as of time of writing, it only supports AWS)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, there are a ton of options. On top of all of those, you might contract a managed service provider to provision and configure your dask cluster for you according to your specs, such as &lt;a class="reference external" href="https://www.saturncloud.io"&gt;Saturn Cloud&lt;/a&gt; (&lt;em&gt;Disclaimer: one of the authors (Julia Signell) works for Saturn Cloud&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Whatever you choose, the whole point is to unlock the power of parallelism in Python that Dask provides, in as scalable a manner as possible which is what getting it running on distributed infrastructure is all about. Once you know where and with what API you are going to deploy your dask cluster, the real configuration process for your Dask cluster and its workload begins.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/30/beginners-config.md&lt;/span&gt;, line 30)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how-to-choose-instance-type-for-your-cluster"&gt;
&lt;h1&gt;How to choose instance type for your cluster&lt;/h1&gt;
&lt;p&gt;When you are ready to set up your dask cluster for production, you will need to make some decisions about the infrastructure your scheduler and your workers will be running on, especially if you are using one of the options from &lt;a class="reference internal" href="#how-to-host-a-distributed-dask-cluster"&gt;&lt;span class="xref myst"&gt;How to host a distributed dask cluster&lt;/span&gt;&lt;/a&gt; that requires pre-provisioned infrastructure. Whether your infrastructure is on-prem or in the cloud, the classic decision points need to be made:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Memory requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CPU requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Storage requirements&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have tested your workload locally, a simple heuristic is to multiply the CPU, storage, and memory usage of your work by some multiplier that is related to how scaled down your local experiments are from your expected production usage. For example, if you test your workload locally with a 10% sample of data, multiplying any observed resource usage by at least 10 may get you close to your minimum instance size. Though in reality Dask’s many underlying optimizations means that it shouldn’t regularly require linear growth of resources to work on more data, this simple heuristic may give you a starting point as a good first pass technique.&lt;/p&gt;
&lt;p&gt;In the same vein, choosing the smallest instance and running with a predetermined subset of data and scaling up until it runs effectively gives you a hint towards the minimum instance size. If your local environment is too underpowered to run your flows locally with 10%+ of your source data, if it is a highly divergent environment (for example a different OS, or with many competing applications running in the background), or if it is difficult or annoying to monitor CPU, memory, and storage of your flow’s execution using your local machine, isolating the test case on the smallest workable node is a better option.&lt;/p&gt;
&lt;p&gt;On the flip side, choosing the biggest instance you can afford and observing the discrepancy between max CPU/memory/storage metrics and scaling back based on the ratio of unused resources can be a quicker way to find your ideal size.&lt;/p&gt;
&lt;p&gt;Wherever you land on node size might be heavily influenced by what you want to pay for, but as long as your node size is big enough that you are avoiding strict out of memory errors, the flip side of what you pay for with nodes closest to your minimum run specs is time. Since the point of your Dask cluster is to run distributed, parallel computations, you can get significant time savings if you scale up your instance to allow for more parallelism. If you have long running models that take hours to train that you can reduce to minutes, and get back some of your time or your employee’s time to see the feedback loop quickly, then scaling up over your minimum specs is worth it.&lt;/p&gt;
&lt;p&gt;Should your scheduler node and worker nodes be the same size? It may certainly be tempting to provision them at separate instance sizes to optimize resources. It’s worth a quick dive into the general resource requirements of each to get a good sense.&lt;/p&gt;
&lt;p&gt;For the scheduler, a serialized version of each task is submitted to it is held in memory for as long as it needs to determine which worker should take the work. This is not necessarily the same amount of memory needed to actually execute the task, but skimping too much on memory here may prevent work from being scheduled. From a CPU perspective, the needs of the scheduler are likely much lower than your workers, but starving the scheduler of CPU will cause deadlock, and when the scheduler is stuck or dies your workers also cannot get any work. Storage wise, the Dask scheduler does not persist much to disk, even temporarily, so it’s storage needs are quite low.&lt;/p&gt;
&lt;p&gt;For the workers, the specific resource needs of your task code may overtake any generalizations we can make. If nothing else, they need enough memory and CPU to deserialize each task payload, and serialize it up again to return as a Future to the Dask scheduler. Dask workers may persist the results of computations in memory, including distributed across the memory of the cluster, which you can read more about &lt;a class="reference external" href="https://distributed.dask.org/en/latest/memory.html"&gt;here&lt;/a&gt;. Regarding storage needs, fundamentally tasks submitted to Dask workers should not write to local storage - the scheduler does not guarantee work will be run on a given worker - so the storage costs should be directly related to the installation footprint of your worker’s dependencies and any ephemeral storage of the dask workers. Temporary files the workers create may include spilling in-memory data to local disk if they run out of memory as long as &lt;a class="reference external" href="https://docs.dask.org/en/latest/configuration-reference.html#distributed.worker.memory.spill"&gt;that behavior isn’t disabled&lt;/a&gt;, which means that reducing memory can have an effect on your ephemeral storage needs.&lt;/p&gt;
&lt;p&gt;Generally we would recommend simplifying your life and keeping your scheduler and worker nodes the same node size, but if you wanted to optimize them, use the above CPU, memory and storage patterns to give you a starting point for configuring them separately.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/30/beginners-config.md&lt;/span&gt;, line 54)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how-to-choose-number-of-workers"&gt;
&lt;h1&gt;How to choose number of workers&lt;/h1&gt;
&lt;p&gt;Every dask cluster has one scheduler and any number of workers. The scheduler keeps track of what work needs to be done and what has already been completed. The workers do work, share results between themselves and report back to the scheduler. More background on what this entails is available in the &lt;a class="reference external" href="https://distributed.dask.org/en/latest/worker.html"&gt;dask.distributed documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When setting up a dask cluster you have to decide how many workers to use. It can be tempting to use many workers, but that isn’t always a good idea. If you use too many workers some may not have enough to do and spend much of their time idle. Even if they have enough to do, they might need to share data with each other which can be slow. Additionally if your machine has finite resources (rather than one node per worker), then each worker will be weaker - they might run out of memory, or take a long time to finish a task.&lt;/p&gt;
&lt;p&gt;On the other hand if you use too few workers you don’t get to take full advantage of the parallelism of dask and your work might take longer to complete overall.&lt;/p&gt;
&lt;p&gt;Before you decide how many workers to use, try using the default. In many cases dask can choose a default that makes use of the size and shape of your machine. If that doesn’t work, then you’ll need some information about the size and shape of your work. In particular you’ll want to know:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;What size is your computer or what types of compute nodes do you have access to?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How big is your data?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What is the structure of the computation that you are trying to do?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are working on your local machine, then the size of the computer is fixed and knowable. If you are working on HPC or cloud instances then you can choose the resources allotted to each worker. You make the decision about the size of your cluster based on factors we discussed in &lt;a class="reference internal" href="#how-to-choose-instance-type-for-your-cluster"&gt;&lt;span class="xref myst"&gt;How to choose instance type for your cluster&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dask is often used in situations where the data are too big to fit in memory. In these cases the data are split into chunks or partitions. Each task is computed on the chunk and then the results are aggregated. You will learn about how to change the shape of your data &lt;a class="reference internal" href="#how-to-host-a-distributed-dask-cluster"&gt;&lt;span class="xref myst"&gt;below&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The structure of the computation might be the hardest to reason about. If possible, it can be helpful to try out the computation on a very small subset of the data. You can see the task graph for a particular computation by calling &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.visualize()&lt;/span&gt;&lt;/code&gt;. If the graph is too large to comfortably view inline, then take a look at the &lt;a class="reference external" href="https://docs.dask.org/en/latest/diagnostics-distributed.html"&gt;Dask dashboard&lt;/a&gt; graph tab. This shows the task graph as it runs and lights up each section. To make dask most efficient, you want a task graph that isn’t too big or too interconnected. The &lt;a class="reference external" href="https://docs.dask.org/en/latest/best-practices.html#avoid-very-large-graphs"&gt;dask docs&lt;/a&gt; discuss several techniques for optimizing your task graph.&lt;/p&gt;
&lt;p&gt;To pick the number of workers to use, think about how many concurrent tasks are happening at any given part of the graph. If each task contains a non-trivial amount of work, then the fastest way to run dask is to have a worker for each concurrent task. For chunked data, if each worker is able to comfortably hold one data chunk in memory and do some computation on that data, then the number of chunks should be a multiple of the number of workers. This ensures that there is always enough work for a worker to do.&lt;/p&gt;
&lt;p&gt;If you have a highly variable number of tasks, then you can also consider using an adaptive cluster. In an adaptive cluster, you set the minimum and maximum number of workers and let the cluster add and remove workers as needed. When the scheduler determines that some workers aren’t needed anymore it asks the cluster to shut them down, and when more are needed, the scheduler asks the cluster to spin more up. This can work nicely for task graphs that start out with few input tasks then have more tasks in the middle, and then some aggregation or reductions at the end.&lt;/p&gt;
&lt;p&gt;Once you have started up your cluster with some workers, you can monitor their progress in the &lt;a class="reference external" href="https://docs.dask.org/en/latest/diagnostics-distributed.html"&gt;dask dashboard&lt;/a&gt;. There you can check on their memory consumption, watch their progress through the task graph, and access worker-level logs. Watching your computation in this way, provides insight into potential speedups and builds intuition about the number of workers to use in the future.&lt;/p&gt;
&lt;p&gt;The tricky bit about choosing the number of workers to use is that in practice the size and shape of your machine, data, and task graph can change. Figuring out how many workers to use can end up feeling like an endless fiddling of knobs. If this is starting to drive you crazy then remember that you can always change the number or workers, even while the cluster is running.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/30/beginners-config.md&lt;/span&gt;, line 82)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how-to-choose-nthreads-to-utilize-multithreading"&gt;
&lt;h1&gt;How to choose nthreads to utilize multithreading&lt;/h1&gt;
&lt;p&gt;When starting dask workers themselves, there are two very important configuration options to play against each other: how many workers and how many threads per worker. You can actually manipulate both on the same worker process with flags, such as in the form &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask-worker&lt;/span&gt; &lt;span class="pre"&gt;--nprocs&lt;/span&gt; &lt;span class="pre"&gt;2&lt;/span&gt; &lt;span class="pre"&gt;--nthreads&lt;/span&gt; &lt;span class="pre"&gt;2&lt;/span&gt;&lt;/code&gt;, though &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--nprocs&lt;/span&gt;&lt;/code&gt; simply spins up another worker in the background so it is cleaner configuration to avoid setting &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--nprocs&lt;/span&gt;&lt;/code&gt; and instead manipulate that configuration with whatever you use to specify total number of workers. We already talked about &lt;a class="reference internal" href="#how-to-host-a-distributed-dask-cluster"&gt;&lt;span class="xref myst"&gt;how to choose number of workers&lt;/span&gt;&lt;/a&gt;, but you may modify your decision about that if you change a workers’ &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--nthreads&lt;/span&gt;&lt;/code&gt; to increase the amount of work an individual worker can do.&lt;/p&gt;
&lt;p&gt;When deciding the best number of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;nthreads&lt;/span&gt;&lt;/code&gt; for your workers, it all boils down to the type of work you expect those workers to do. The fundamental principle is that multiple threads are best to share data between tasks, but worse if running code that doesn’t release Python’s GIL (“Global Interpreter Lock”). Increasing the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;nthreads&lt;/span&gt;&lt;/code&gt; for work that does not release the Python’s GIL has no effect; the worker cannot use threading to optimize the speed of computation if the GIL is locked. This is a possible point of confusion for new Dask users who want to increase their parallelism, but don’t see any gains from increasing the threading limit of their workers.&lt;/p&gt;
&lt;p&gt;As discussed in &lt;a class="reference internal" href="#%5Bhttps://distributed.dask.org/en/latest/worker.html%5D(https://distributed.dask.org/en/latest/worker.html)"&gt;&lt;span class="xref myst"&gt;the Dask docs on workers&lt;/span&gt;&lt;/a&gt;, there are some rules of thumb when to worry about GIL lockages, and thus prefer more workers over heavier individual workers with high &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;nthreads&lt;/span&gt;&lt;/code&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;If your code is mostly pure Python (in non-optimized Python libraries) on non-numerical data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If your code causes computations external to Python that are long running and don’t release the GIL explicitly&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Conveniently, a lot of dask users are running exclusively numerical computations using Python libraries optimized for multithreading, namely NumPy, Pandas, SciPy, etc in the PyData stack. If you do mostly numerical computations using those or similarly optimized libraries, you should emphasize a higher thread count. If you truly are doing mostly numerical computations, you can specify as many total threads as you have cores; if you are doing any work that would cause a thread to pause, for example any I/O (to write results to disk, perhaps), you can specify &lt;em&gt;more&lt;/em&gt; threads than you have cores, since some will be occasionally sitting idle. The ideal number regarding how many more threads than cores to set in that situation is complex to estimate and dependent on your workload, but taking some advice from &lt;a class="reference external" href="https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor"&gt;concurrent.futures&lt;/a&gt;, 5 times the processors on your machine is a historical upper bound to limit your total thread count to for heavily I/O dependent workloads.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/30/beginners-config.md&lt;/span&gt;, line 95)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how-to-chunk-arrays-and-partition-dataframes"&gt;
&lt;h1&gt;How to chunk arrays and partition DataFrames&lt;/h1&gt;
&lt;p&gt;There are many different methods of triggering work in dask. For instance: you can wrap functions with delayed or submit work directly to the client (for a comparison of the options see &lt;a class="reference external" href="https://docs.dask.org/en/latest/user-interfaces.html"&gt;User Interfaces&lt;/a&gt;). If you are loading structured data into dask objects, then you are likely using &lt;a class="reference external" href="https://docs.dask.org/en/latest/array.html"&gt;dask.array&lt;/a&gt; or &lt;a class="reference external" href="https://docs.dask.org/en/latest/dataframe.html"&gt;dask.dataframe&lt;/a&gt;. These modules mimic numpy and pandas respectively - making it easier to interact with large arrays and large tabular datasets.&lt;/p&gt;
&lt;p&gt;When using dask.dataframe and dask.array, computations are divided among workers by splitting the data into pieces. In dask.dataframe these pieces are called &lt;a class="reference external" href="https://docs.dask.org/en/latest/dataframe-design.html#partitions"&gt;partitions&lt;/a&gt; and in dask.array they are called &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-chunks.html"&gt;chunks&lt;/a&gt;, but the principle is the same. In the case of dask.array each chunk holds a numpy array and in the case of dask.dataframe each partition holds a pandas dataframe. Either way, each one contains a small part of the data, but is representative of the whole and must be small enough to comfortably fit in worker memory.&lt;/p&gt;
&lt;p&gt;Often when loading in data, the partitions/chunks will be determined automatically. For instance, when reading from a directory containing many csv files, each file will become a partition. If your data are not split up by default, then it can be done manually using df.set_index or array.rechunk. If they are split up by default and you want to change the shape of the chunks, the file-level chunks should be a multiple of the dask level chunks (read more about this &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-best-practices.html#orient-your-chunks"&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;As the user, you know how the data are going to be used, so you can often partition it in ways that lead to more efficient computations. For instance if you are going to be aggregating to a monthly step, it can make sense to chunk along the time axis. If instead you are going to be looking at a particular feature at different altitudes, it might make sense to chunk along the altitude. More tips for chunking dask.arrays are described in &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-best-practices.html"&gt;Best Practices&lt;/a&gt;. Another scenario in which it might be helpful to repartition is if you have filtered the data down to a subset of the original. In that case your partitions will likely be too small. See the dask.dataframe &lt;a class="reference external" href="https://docs.dask.org/en/latest/dataframe-best-practices.html#repartition-to-reduce-overhead"&gt;Best Practices&lt;/a&gt; for more details on how to handle that case.&lt;/p&gt;
&lt;p&gt;When choosing the size of chunks it is best to make them neither too small, nor too big (around 100MB is often reasonable). Each chunk needs to be able to fit into the worker memory and operations on that chunk should take some non-trivial amount of time (more than 100ms). For many more recommendations take a look at the docs on &lt;a class="reference external" href="https://docs.dask.org/en/latest/array-chunks.html"&gt;chunks&lt;/a&gt; and on &lt;a class="reference external" href="https://docs.dask.org/en/latest/dataframe-design.html#partitions"&gt;partitions&lt;/a&gt;.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;&lt;em&gt;We hope this helps you make decisions about whether to configure your Dask deployment differently and give you the confidence to try it out. We found all of this great information in the Dask docs, so if you are feeling inspired please follow the links we’ve sprinkled throughout and learn even more about Dask!&lt;/em&gt;&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/07/30/beginners-config/"/>
    <summary>Configuring a Dask cluster can seem daunting at first, but the good news is that the Dask project has a lot of built in heuristics that try its best to anticipate and adapt to your workload based on the machine it is deployed on and the work it receives. Possibly for a long time you can get away with not configuring anything special at all. That being said, if you are looking for some tips to move on from using Dask locally, or have a Dask cluster that you are ready to optimize with some more in-depth configuration, these tips and tricks will help guide you and link you to the best Dask docs on the topic!</summary>
    <category term="config" label="config"/>
    <category term="distributed" label="distributed"/>
    <published>2020-07-30T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/07/23/current-state-of-distributed-dask-clusters/</id>
    <title>The current state of distributed Dask clusters</title>
    <updated>2020-07-23T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;Dask enables you to build up a graph of the computation you want to perform and then executes it in parallel for you. This is great for making best use of your computer’s hardware. It is also great when you want to expand beyond the limits of a single machine.&lt;/p&gt;
&lt;p&gt;In this post we will cover:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#manual-setup"&gt;&lt;span class="xref myst"&gt;Manual cluster setup&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#cluster-managers"&gt;&lt;span class="xref myst"&gt;Review of deployment options today&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="#future"&gt;&lt;span class="xref myst"&gt;Analysis of that state&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/23/current-state-of-distributed-dask-clusters.md&lt;/span&gt;, line 15)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="manual-setup"&gt;

&lt;p&gt;Let’s dive in by covering the most straight forward way to setup a distributed Dask cluster.&lt;/p&gt;
&lt;section id="setup-scheduler-and-workers"&gt;
&lt;h2&gt;Setup scheduler and workers&lt;/h2&gt;
&lt;p&gt;Imagine we have three computers, we will call them &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MachineA&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MachineB&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MachineC&lt;/span&gt;&lt;/code&gt;. Each of these machines has a functioning Python environment and we have &lt;a class="reference external" href="https://docs.dask.org/en/latest/install.html"&gt;installed Dask with &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;conda&lt;/span&gt; &lt;span class="pre"&gt;install&lt;/span&gt; &lt;span class="pre"&gt;dask&lt;/span&gt;&lt;/code&gt;&lt;/a&gt;. If we want to pull them together into a Dask cluster we start by running a scheduler on &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MachineA&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-console notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;dask-scheduler
&lt;span class="go"&gt;distributed.scheduler - INFO - -----------------------------------------------&lt;/span&gt;
&lt;span class="go"&gt;distributed.scheduler - INFO - Local Directory: /tmp/scheduler-btqf8ve1&lt;/span&gt;
&lt;span class="go"&gt;distributed.scheduler - INFO - -----------------------------------------------&lt;/span&gt;
&lt;span class="go"&gt;distributed.scheduler - INFO - Clear task state&lt;/span&gt;
&lt;span class="go"&gt;distributed.scheduler - INFO -   Scheduler at: tcp://MachineA:8786&lt;/span&gt;
&lt;span class="go"&gt;distributed.scheduler - INFO -   dashboard at:               :8787&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Next we need to start a worker process on both &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MachineB&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MachineC&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-console notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;dask-worker&lt;span class="w"&gt; &lt;/span&gt;tcp://MachineA:8786
&lt;span class="go"&gt;distributed.nanny - INFO -         Start Nanny at:    &amp;#39;tcp://127.0.0.1:51224&amp;#39;&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO -       Start worker at:      tcp://127.0.0.1:51225&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO -          Listening to:      tcp://127.0.0.1:51225&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO -          dashboard at:            127.0.0.1:51226&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO - Waiting to connect to:        tcp://MachineA:8786&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO - -------------------------------------------------&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO -               Threads:                          4&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO -                Memory:                    8.00 GB&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO -       Local Directory:       /tmp/worker-h3wfwg7j&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO - -------------------------------------------------&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO -         Registered to:        tcp://MachineA:8786&lt;/span&gt;
&lt;span class="go"&gt;distributed.worker - INFO - -------------------------------------------------&lt;/span&gt;
&lt;span class="go"&gt;distributed.core - INFO - Starting established connection&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If we start a worker on both of our two space machines Dask will autodetect the resources on the machine and make them available to the scheduler. In the example above the worker has detected 4 CPU cores and 8GB of RAM. Therefore our scheduler has access to a total of 8 cores and 16GB of RAM and it will use these resources to run through the computation graph as quickly as possible. If we add more workers on more machines then the amount of resources available to the scheduler increases and computation times should get faster.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;: While the scheduler machine probably has the same resources as the other two these will not be used in the computation.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Lastly we need to connect to our scheduler from our Python session.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;tcp://MachineA:8786&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Creating this &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Client&lt;/span&gt;&lt;/code&gt; object within the Python global namespace means that any Dask code you execute will detect this and hand the computation off to the scheduler which will then execute on the workers.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="accessing-the-dashboard"&gt;
&lt;h2&gt;Accessing the dashboard&lt;/h2&gt;
&lt;p&gt;The Dask distributed scheduler also has a dashboard which can be opened in a web browser. As you can see in the output above the default location for this is on the scheduler machine at port &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;8787&lt;/span&gt;&lt;/code&gt;. So you should be able to navigate to &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;http://MachineA:8787&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;a href="https://i.imgur.com/VzQIVpI.png"&gt;
&lt;img alt="Dask dashboard" src="https://i.imgur.com/VzQIVpI.png" width="100%" align="center"&gt;
&lt;/a&gt;
&lt;p&gt;If you are using Jupyter Lab as your Python environment you are also able to open individual plots from the dashboard as windows in Jupyter Lab with the &lt;a class="reference external" href="https://github.com/dask/dask-labextension"&gt;Dask Lab Extension&lt;/a&gt;.&lt;/p&gt;
&lt;a href="https://i.imgur.com/SNk6F0H.png"&gt;
&lt;img alt="Dask Lab Extension" src="https://i.imgur.com/SNk6F0H.png" width="100%" align="center"&gt;
&lt;/a&gt;
&lt;/section&gt;
&lt;section id="recap"&gt;
&lt;h2&gt;Recap&lt;/h2&gt;
&lt;p&gt;In this minimal example we have installed Dask on some machines, ran a distributed scheduler on one of them and workers on the others. We then connected to our cluster from our Python session and opened the dashboard to keep an eye on the cluster.&lt;/p&gt;
&lt;p&gt;What we haven’t covered is where these machines came from in the first place. In the rest of this post we will discuss the different ways that folks tend to run clusters out in the wild and give an overview of the various tools that exist to help you set up Dask clusters on a variety of infrastructure.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/23/current-state-of-distributed-dask-clusters.md&lt;/span&gt;, line 85)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="cluster-requirements"&gt;
&lt;h1&gt;Cluster requirements&lt;/h1&gt;
&lt;p&gt;In order to run a Dask cluster you must be able to install Dask on a machine and start the scheduler and worker components. These machines need to be able to communicate via a network so that these components can speak to each other.&lt;/p&gt;
&lt;p&gt;You also need to be able to access the scheduler from your Python session via a network in order to connect the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Client&lt;/span&gt;&lt;/code&gt; and access the dashboard.&lt;/p&gt;
&lt;p&gt;Lastly the Python environment in the Python session where you create your &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Client&lt;/span&gt;&lt;/code&gt; must match the Python environment where the workers are running. This is because Dask uses &lt;a class="reference external" href="https://github.com/cloudpipe/cloudpickle"&gt;cloudpickle&lt;/a&gt; to serialize objects and send them to workers and to retrieve results. Therefore package versions must match in both locations.&lt;/p&gt;
&lt;p&gt;We will need to bear these requirements in mind as we discuss the different platforms that folks generally want to run Dask on.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/23/current-state-of-distributed-dask-clusters.md&lt;/span&gt;, line 95)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="cluster-types"&gt;
&lt;h1&gt;Cluster types&lt;/h1&gt;
&lt;p&gt;There are two “types” of clusters that I tend to see folks running. Fixed clusters and ephemeral clusters.&lt;/p&gt;
&lt;section id="fixed-clusters"&gt;
&lt;h2&gt;Fixed clusters&lt;/h2&gt;
&lt;p&gt;One common way of setting up a cluster is to run the scheduler and worker commands as described above, but leave them running indefinitely. For the purpose of this article I’ll refer to this as a “fixed cluster”. You may use something like &lt;a class="reference external" href="https://www.freedesktop.org/wiki/Software/systemd/"&gt;systemd&lt;/a&gt; or &lt;a class="reference external" href="http://supervisord.org/"&gt;supervisord&lt;/a&gt; to manage the processes and ensure they are always running on the machines. The Dask cluster can then be treated as a service.&lt;/p&gt;
&lt;p&gt;In this paradigm once a cluster is set up folks may start their Python session, connect their client to this existing cluster, do some work and disconnect again. They might later come back to that cluster and run further work. The cluster will sit idle in the meantime.&lt;/p&gt;
&lt;p&gt;It is also common in this paradigm for multiple users to share this single cluster, however this is not recommended as the Dask scheduler does not manage users or clients separately and work will be executed on a first come first served bases. Therefore we recommend that users use a cluster one at a time.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ephemeral-clusters"&gt;
&lt;h2&gt;Ephemeral clusters&lt;/h2&gt;
&lt;p&gt;An ephemeral cluster is one which only exists for the duration of the work. In this case a user may SSH onto the machines, run the commands to set up the cluster, connect a client and perform work, then disconnect and exit the Dask processes. A basic way of doing this would be to create a bash script which calls &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ssh&lt;/span&gt;&lt;/code&gt; and sets up the cluster. You would run this script in the background while performing your work and then kill it once you are done. We will cover other implementations of this in the coming sections.&lt;/p&gt;
&lt;p&gt;Ephemeral clusters allow you to leverage a bunch of machines but free them up again when you are done. This is especially useful when you are using a system like a cloud service or a batch scheduler where you have limited credits, or are charged for provisioned resources.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/23/current-state-of-distributed-dask-clusters.md&lt;/span&gt;, line 113)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="adaptivity"&gt;
&lt;h1&gt;Adaptivity&lt;/h1&gt;
&lt;p&gt;Ephemeral clusters are also generally easier to scale as you will likely have an automated mechanism for starting workers. The Dask scheduler maintains an estimate of how long it expects the outstanding work will take to complete. If the scheduler has a mechanism for starting and stopping workers then it will scale up the workers in an attempt to complete all outstanding work within 5 seconds. This is referred to as adaptive mode.&lt;/p&gt;
&lt;p&gt;The mechanisms for starting and stopping workers are added via plugins. Many of the implementations we are about to discuss include this logic.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/23/current-state-of-distributed-dask-clusters.md&lt;/span&gt;, line 119)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="connectivity"&gt;
&lt;h1&gt;Connectivity&lt;/h1&gt;
&lt;p&gt;Dask uses TCP to communicate between client, scheduler and workers by default. This means that all of these components must be on a TCP/IP network with open routes between the machines. Many connectivity problems step from firewalls or private networks blocking connections between certain components. An example of this would be running Dask on a cloud platform like AWS, but running the Python session and client on your laptop while sitting in a coffee shop using the free wifi. You must ensure you are able to route traffic between components, either by exposing the Dask cluster to the internet or by connecting your laptop to the private network via a VPN or tunnel.&lt;/p&gt;
&lt;p&gt;There is also &lt;a class="reference external" href="https://blog.dask.org/2019/06/09/ucx-dgx"&gt;ongoing work to add support for UCX&lt;/a&gt; to Dask, which will allow it to make use of InfiniBand or NVLink networks where they are available.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/23/current-state-of-distributed-dask-clusters.md&lt;/span&gt;, line 125)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="cluster-managers"&gt;
&lt;h1&gt;Cluster Managers&lt;/h1&gt;
&lt;p&gt;In the following section we are going to cover a range of cluster manager implementations which are available within the Dask community.&lt;/p&gt;
&lt;p&gt;In the Dask distributed codebase there is a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Cluster&lt;/span&gt;&lt;/code&gt; superclass which can be subclassed to build various cluster managers for different platforms. Members of the community have taken this and built their own packages which enable creating a Dask cluster on a specific platform, for example &lt;a class="reference external" href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The design of these classes is that you import the cluster manager into your Python session and instantiate it. The object then handles starting the scheduler and worker processes on the target platform. You can then create a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Client&lt;/span&gt;&lt;/code&gt; object as usual from that cluster object to connect to it.&lt;/p&gt;
&lt;p&gt;All of these cluster manager objects are ephemeral clusters, they only exist for the duration of the Python session and then will be cleaned up.&lt;/p&gt;
&lt;section id="local-cluster"&gt;
&lt;h2&gt;Local Cluster&lt;/h2&gt;
&lt;p&gt;Let’s start with one of the reference implementations of &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Cluster&lt;/span&gt;&lt;/code&gt; from the Dask distributed codebase &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;LocalCluster&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LocalCluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LocalCluster&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This cluster manager starts a scheduler on your local machine, and then starts a worker for every CPU core that it finds on the machine.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ssh-cluster"&gt;
&lt;h2&gt;SSH Cluster&lt;/h2&gt;
&lt;p&gt;Another reference implementation is &lt;a class="reference external" href="https://docs.dask.org/en/latest/setup/ssh.html"&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SSHCluster&lt;/span&gt;&lt;/code&gt;&lt;/a&gt;. This is one of the most pure and simple ways of using multiple machines with Dask distributed and is very similar to our initial example in this blog post.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SSHCluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SSHCluster&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;MachineA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;MachineB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;MachineC&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The first argument here is a list of machines which we can SSH into and set up a Dask cluster on. The first machine in the list will be used as the scheduler and the rest as workers.&lt;/p&gt;
&lt;p&gt;As the scheduler will likely use far less resources than the workers you may even want to run that locally and make use of all three remote machines as workers.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SSHCluster&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;MachineA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;MachineB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;MachineC&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id="speccluster"&gt;
&lt;h2&gt;SpecCluster&lt;/h2&gt;
&lt;p&gt;The last implementation that is included in the core Dask distributed library is &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SpecCluster&lt;/span&gt;&lt;/code&gt;. This is actually another superclass and is designed to be subclassed by other developers when building cluster managers. However it goes further than &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Cluster&lt;/span&gt;&lt;/code&gt; in expecting the developer to provide a full specification for schedulers and workers as Python classes. There is also a superclass called &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ProcessInterface&lt;/span&gt;&lt;/code&gt; which is designed to be used when creating those scheduler and worker classes.&lt;/p&gt;
&lt;p&gt;Having standard interfaces means a more consistent experience for users. Many of the cluster manager we will cover next use &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SpecCluster&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dask-kubernetes"&gt;
&lt;h2&gt;Dask Kubernetes&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://kubernetes.dask.org/en/latest/"&gt;Dask Kubernetes&lt;/a&gt; provides a cluster manager for &lt;a class="reference external" href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt; called &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;KubeCluster&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Kubernetes provides high level APIs and abstract concepts for scheduling linux containers on a cluster of machines. It provides abstracted concepts for processes, containers, networks, storage, etc to empower better use of data centre scale resources.&lt;/p&gt;
&lt;p&gt;As a Dask user it generally doesn’t matter to you how your cluster is set up. But if you’ve been given access to a Kubernetes cluster by your organisation or institution you will need to understand those concepts in order to schedule your work on it.&lt;/p&gt;
&lt;p&gt;The &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;KubeCluster&lt;/span&gt;&lt;/code&gt; cluster manager further abstracts away those concepts into the Dask terms we are familiar with.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_kubernetes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;KubeCluster&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KubeCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;cluster_specific_kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In order for this code to work you will need to have &lt;a class="reference external" href="https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/"&gt;configured your Kubernetes credentials&lt;/a&gt;, in the same way that for the SSH example you will need to configure your keys.&lt;/p&gt;
&lt;p&gt;Your client will also need to be able to access the Dask scheduler, and you probably want to be able to open the dashboard in your browser. However Kubernetes uses an overlay network which means that the IP addresses assigned to the scheduler and workers are only routable within the cluster. This is fine for them talking to each other but means you wont be able to get in from the outside.&lt;/p&gt;
&lt;p&gt;One way around this is to ensure your Python session is also running inside the Kubernetes cluster. A popular way of setting up an interactive Python environment on Kubernetes is with &lt;a class="reference external" href="https://zero-to-jupyterhub.readthedocs.io/en/latest/"&gt;Zero to Jupyter Hub&lt;/a&gt;, which gives you access to &lt;a class="reference external" href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt; notebooks running within the Kubernetes cluster.&lt;/p&gt;
&lt;p&gt;The alternative is exposing your scheduler to the external network. You can do this by &lt;a class="reference external" href="https://kubernetes.io/docs/tutorials/kubernetes-basics/expose/expose-intro/"&gt;exposing the Kubernetes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Service&lt;/span&gt;&lt;/code&gt; object&lt;/a&gt; associated with the scheduler or by &lt;a class="reference external" href="https://kubernetes.io/docs/concepts/services-networking/ingress/"&gt;setting up and configuring an Ingress component&lt;/a&gt; for your Kubernetes cluster. Both of these options require some knowledge of Kubernetes.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dask-helm-chart"&gt;
&lt;h2&gt;Dask Helm chart&lt;/h2&gt;
&lt;p&gt;Another option for running Dask on a Kubernetes cluster is using the &lt;a class="reference external" href="https://github.com/dask/helm-chart"&gt;Dask Helm Chart&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is an example of a fixed cluster setup. Helm is a way of installing specific resources on a Kubernetes cluster, similar to a package manager like &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;apt&lt;/span&gt;&lt;/code&gt; or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;yum&lt;/span&gt;&lt;/code&gt;. The Dask Helm chart includes a Jupyter notebook, a Dask scheduler and three Dask workers. The workers can be scaled manually by interacting with the Kubernetes API but not adaptively by the Dask scheduler itself.&lt;/p&gt;
&lt;p&gt;This feels like a different approach to what we’ve seen so far. It gives you a Dask cluster which is always available, and a Jupyter notebook to drive the cluster from. You then have to take your work to the cluster’s Jupyter session rather than spawning a cluster from your existing work place.&lt;/p&gt;
&lt;p&gt;One benefit of this approach is that because the Jupyter notebook has been set up as part of the cluster it already has the Lab Extension installed and also has been &lt;a class="reference external" href="https://github.com/dask/helm-chart/blob/f413647f90d6e278515b172c623977578a535aa2/dask/templates/dask-jupyter-deployment.yaml#L47-L48"&gt;pre-configured&lt;/a&gt; with the location of the Dask cluster. So unlike previous examples where you need to either give the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Client&lt;/span&gt;&lt;/code&gt; the address of the scheduler or a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Cluster&lt;/span&gt;&lt;/code&gt; object, in this instance it will auto-detect the cluster from environment variables that are set by the Helm chart.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# The address is loaded from an environment variable&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you call &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Client&lt;/span&gt;&lt;/code&gt; without any arguments in other situations where the scheduler location has not been configured it will automatically create a &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;LocalCluster&lt;/span&gt;&lt;/code&gt; object and use that.&lt;/em&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dask-jobqueue"&gt;
&lt;h2&gt;Dask Jobqueue&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/dask/dask-jobqueue"&gt;Dask Jobqueue&lt;/a&gt; is a set of cluster managers aimed at HPC users.&lt;/p&gt;
&lt;p&gt;When working as a researcher or academic with access to an HPC or Supercomputer you likely have to submit work to that machine via some kind of job queueing system. This is often in the form of a bash script which contains metadata about how much resource you need on the machine and the commands you want to run.&lt;/p&gt;
&lt;p&gt;Dask Jobqueue has cluster manager objects for &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Portable_Batch_System"&gt;PBS&lt;/a&gt;, &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Slurm_Workload_Manager"&gt;Slurm&lt;/a&gt;, and &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Oracle_Grid_Engine"&gt;SGE&lt;/a&gt;. When creating these cluster managers they will construct scripts for the batch scheduler based on your arguments and submit them using your default credentials.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_jobqueue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PBSCluster&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PBSCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;cluster_specific_kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;As batch systems like these often have a long wait time you may not immediately get access to your cluster object and scaling can be slow. Depending on the queueing policies it may be best to think of this as a fixed sized cluster. However if you have a responsive interactive queue then you can use this like any other autoscaling cluster manager.&lt;/p&gt;
&lt;p&gt;Again it is expected that your Python session is able to connect to the IP address of the scheduler. This may vary depending on your HPC centre setup as to how you can ensure this.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dask-yarn"&gt;
&lt;h2&gt;Dask Yarn&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://yarn.dask.org/en/latest/"&gt;Dask Yarn&lt;/a&gt; is a cluster manager for traditional &lt;a class="reference external" href="https://hadoop.apache.org/"&gt;Hadoop&lt;/a&gt; systems.&lt;/p&gt;
&lt;p&gt;Hadoop is a framework that allows for the distributed processing of large data sets across clusters of computers using simple programming models. It is a common piece of infrastrcture in Java/Scala ecosystems for processing large volumes of data. However you can also use the scheduling functionality called YARN to schedule Dask workers and leverage the underlying hardware resources.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_yarn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;YarnCluster&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;YarnCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;cluster_specific_kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Dask Yarn is only intended to be used from a Hadoop edge node which will have access to the internal network of the Hadoop cluster.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dask-cloudprovider"&gt;
&lt;h2&gt;Dask Cloudprovider&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://cloudprovider.dask.org/en/latest/"&gt;Dask Cloudprovider&lt;/a&gt; is a collection of cluster managers for leveraging cloud native APIs.&lt;/p&gt;
&lt;p&gt;Cloud providers such as &lt;a class="reference external" href="https://aws.amazon.com/"&gt;Amazon&lt;/a&gt;, &lt;a class="reference external" href="https://azure.microsoft.com/"&gt;Microsoft&lt;/a&gt; and &lt;a class="reference external" href="https://cloud.google.com/"&gt;Google&lt;/a&gt; have many APIs available for building and running various types of infrastructure. These range from traditional virtual servers running linux or Windows to higher level APIs that can execute small snippets of code on demand. They have batch systems, Hadoop systems, machine learning systems and more.&lt;/p&gt;
&lt;p&gt;The ideal scenario for running Dask on a cloud provider would be a service which would allow you to run the scheduler and worker with specified Python environments and then connect to them securely from the outside. Such a service doesn’t quite exist, but similar things do to varying degrees.&lt;/p&gt;
&lt;p&gt;One example is &lt;a class="reference external" href="https://aws.amazon.com/fargate/"&gt;AWS Fargate&lt;/a&gt; which is a managed container platform. You can run &lt;a class="reference external" href="https://www.docker.com/"&gt;Docker containers&lt;/a&gt; on demand which each have a unique IP address which can be public or private. This means we can run Dask scheduler and worker processes within a &lt;a class="reference external" href="https://github.com/dask/dask-docker"&gt;Dask container&lt;/a&gt; and connect to them from our Python session. This service is &lt;a class="reference external" href="https://aws.amazon.com/fargate/pricing/"&gt;billed per second&lt;/a&gt; for the requested resources, so makes most sense as an ephemeral service which has no cost when you aren’t using it.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_cloudprovider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FargateCluster&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FargateCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;cluster_specific_kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This cluster manager uses your &lt;a class="reference external" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html#cli-quick-configuration"&gt;AWS credentials&lt;/a&gt; to authenticate and request AWS resources on Fargate, and then connects your local session to the Dask cluster running on the cloud.&lt;/p&gt;
&lt;p&gt;There are even higher level services such as &lt;a class="reference external" href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt; or &lt;a class="reference external" href="https://cloud.google.com/functions"&gt;Google Cloud Functions&lt;/a&gt; which allow you to execute code on demand and you are billed for the execution time of the code. These are referred to as “serverless” services as the servers are totally abstracted away. This would be perfect for out Dask cluster as you could submit the scheduler and workers as the code to run. &lt;strong&gt;However&lt;/strong&gt; when running these cloud functions it is not possible to get a network connection between them as they do not have routable IP addresses, so there is no way to set up a Dask cluster made of these executing functions. Maybe one day!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="dask-gateway"&gt;
&lt;h2&gt;Dask Gateway&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://gateway.dask.org/"&gt;Dask Gateway&lt;/a&gt; is a central service for managing Dask clusters. It provides a secure API which multiple users can communicate with to request Dask servers. It can spawn Dask clusters on Kubernetes, Yarn or batch systems.&lt;/p&gt;
&lt;p&gt;This tool is targeted at IT administrators who want to enable their users to create Dask clusters, but want to maintain some centralized control instead of each user creating their own thing. This can also be useful for tracking Dask usage and setting per user limits.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_gateway&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GatewayCluster&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GatewayCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;cluster_specific_kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For each user the commands for creating and using a gateway cluster are the same. It is down to the administrator to setup and manage the gateway server and configure &lt;a class="reference external" href="https://gateway.dask.org/authentication.html#"&gt;authentication via kerberos or Jupyter Hub&lt;/a&gt;. They should also provide &lt;a class="reference external" href="https://gateway.dask.org/configuration-user.html"&gt;configuration&lt;/a&gt; to their users so that Dask Gateway knows how to connect to the gateway server. In a large organisation or institution the IT department also likely provisions the machines that staff are using, and so should be able to drop configuration files onto users computers.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="local-cuda-cluster"&gt;
&lt;h2&gt;Local CUDA Cluster&lt;/h2&gt;
&lt;p&gt;The last cluster manager I’m going to cover is &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;LocalCUDACluster&lt;/span&gt;&lt;/code&gt; from the &lt;a class="reference external" href="https://github.com/rapidsai/dask-cuda"&gt;Dask CUDA&lt;/a&gt; package.&lt;/p&gt;
&lt;p&gt;This is slightly different than the other cluster managers in that it is constructing a Dask cluster which is specifically optimised for a single piece of hardware. In this case it is targeting machines with GPUs ranging from your laptop with an onboard NVIDIA GPU to an &lt;a class="reference external" href="https://www.nvidia.com/en-gb/data-center/dgx-2/"&gt;NVIDIA DGX-2&lt;/a&gt; with multiple GPUs running in your datacentre.&lt;/p&gt;
&lt;p&gt;The cluster manager closely follows the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;LocalCluster&lt;/span&gt;&lt;/code&gt; in that is creates resources locally on the current machine, but instead of creating one worker per CPU core it creates one per GPU. It also changes some of the configuration defaults to ensure good performance of GPU workloads.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_cuda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LocalCUDACluster&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LocalCUDACluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;cluster_specific_kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This package also has an alternative Dask worker bash command called &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dask-cuda-worker&lt;/span&gt;&lt;/code&gt; which also modified the defaults of the Dask worker to ensure it is optimised for GPU work.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/23/current-state-of-distributed-dask-clusters.md&lt;/span&gt;, line 309)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="future"&gt;
&lt;h1&gt;Future&lt;/h1&gt;
&lt;p&gt;Now that we have laid out the current state of the Dask distributed cluster ecosystem let’s discuss where we could go next.&lt;/p&gt;
&lt;p&gt;As shown at the beginning a Dask cluster is a combination of scheduler, workers and client which enable distributed execution of Python functions. Setting up your own cluster on your own machines is straight forward, but there is such a variety of ways to provision infrastructure that we now have a number of ways of automating this.&lt;/p&gt;
&lt;p&gt;This variation opens up a number of questions about how we can improve things.&lt;/p&gt;
&lt;section id="do-we-need-more-fixed-cluster-options"&gt;
&lt;h2&gt;Do we need more fixed cluster options?&lt;/h2&gt;
&lt;p&gt;While covering the various cluster managers we only covered one fixed cluster implementation, the Helm chart. Is there a requirement for more fixed clusters? Examples may be &lt;a class="reference external" href="https://aws.amazon.com/cloudformation/"&gt;CloudFormation&lt;/a&gt; or &lt;a class="reference external" href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; templates which follow the same structure as the Helm chart, providing a Jupyter service, Dask scheduler and fixed number of workers.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="can-we-bridge-some-gaps"&gt;
&lt;h2&gt;Can we bridge some gaps?&lt;/h2&gt;
&lt;p&gt;Could the Dask Kubernetes cluster manager connect to an existing cluster that was built using the Helm chart to then perform adaptive scaling? I’ve been asked this a lot but it is currently unclear how to get to this position. The cluster manager and Helm chart use different Kubernetes resources to achieve the same goal, so some unification would be needed before this is possible.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="are-ephemeral-clusters-too-ephemeral"&gt;
&lt;h2&gt;Are ephemeral clusters too ephemeral?&lt;/h2&gt;
&lt;p&gt;Many of the cluster managers only exist for the duration of the Python session. However some like the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;YarnCluster&lt;/span&gt;&lt;/code&gt; allow you to disconnect and reconnect from the cluster. This allows you to treat a YARN cluster more like a fixed cluster.&lt;/p&gt;
&lt;p&gt;In other circumstances the Python session may have a timeout or limit and may be killed before the Dask cluster can complete its work. Would there be benefit to letting the Dask cluster continue to exist? With the Python session cleared up the client and futures will also be garbage collected. So perhaps not.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="can-we-manage-conda-environments-better"&gt;
&lt;h2&gt;Can we manage conda environments better?&lt;/h2&gt;
&lt;p&gt;Currently it is the responsibility of the person creating the cluster to ensure that the worker’s conda environment matches the one where the &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Client&lt;/span&gt;&lt;/code&gt; is going to be created. On fixed clusters this can be easier as the Python/Jupyter environment can be provided within the same set of systems. However on ephemeral clusters where you may be reaching into a cloud or batch system they may not match your laptop’s environment for example.&lt;/p&gt;
&lt;p&gt;Perhaps there could be integration between workers and conda to create dynamic environments on the fly. Exploring the performance impact of this would be interesting.&lt;/p&gt;
&lt;p&gt;Another option could be enabling users to start a remote Jupyter kernel on a worker. They wouldn’t have access to the same filesystem, but they would share a conda environment.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/07/23/current-state-of-distributed-dask-clusters/"/>
    <summary>Dask enables you to build up a graph of the computation you want to perform and then executes it in parallel for you. This is great for making best use of your computer’s hardware. It is also great when you want to expand beyond the limits of a single machine.</summary>
    <published>2020-07-23T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/07/21/faster-scheduling/</id>
    <title>Faster Scheduling</title>
    <updated>2020-07-21T00:00:00+00:00</updated>
    <author>
      <name>Matthew Rocklin (Coiled)</name>
    </author>
    <content type="html">&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 8)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="summary"&gt;

&lt;p&gt;This post discusses Dask overhead costs for task scheduling,
and then lays out a rough plan for acceleration.&lt;/p&gt;
&lt;p&gt;This post is written for other maintainers, and often refers to internal
details. It is not intended for broad readability.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 16)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="how-does-this-problem-present"&gt;
&lt;h1&gt;How does this problem present?&lt;/h1&gt;
&lt;p&gt;When we submit large graphs there is a bit of a delay between us calling
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.compute()&lt;/span&gt;&lt;/code&gt; and work actually starting on the workers. In some cases, that
delay can affect usability and performance.&lt;/p&gt;
&lt;p&gt;Additionally, in far fewer cases, the gaps in between tasks can be an issue,
especially if those tasks are very short and for some reason can not be made
longer.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 26)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="who-cares"&gt;
&lt;h1&gt;Who cares?&lt;/h1&gt;
&lt;p&gt;First, this is a problem that affects about 1-5% of Dask users. These are people
who want to process millions of tasks relatively quickly. Let’s list a few use
cases:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Xarray/Pangeo workloads at the 10-100TB scale&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NVIDIA RAPIDS workloads on large tabular data (GPUs make computing fast, so other costs become
relatively larger)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some mystery use cases inside of some hedge funds&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It does not affect the everyday user, who processes 100GB to a few TB of data,
and doesn’t mind waiting 10s for things to start running.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 40)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="coarse-breakdown-of-costs"&gt;
&lt;h1&gt;Coarse breakdown of costs&lt;/h1&gt;
&lt;p&gt;When you call &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x.sum().compute()&lt;/span&gt;&lt;/code&gt; a few things happen:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graph generation:&lt;/strong&gt; Some Python code in a Dask collection library, like
dask array, calls the sum function, which generates a task graph on the
client side.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graph Optimization:&lt;/strong&gt; We then optimize that graph, also on the client
side, in order to remove unnecessary work, fuse tasks, apply important high
level optimizations, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graph Serializtion:&lt;/strong&gt; We now pack up that graph in a form that can be
sent over to the scheduler.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graph Communication:&lt;/strong&gt; We fire those bytes across a wire over to the
scheduler&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scheduler.update_graph:&lt;/strong&gt; The scheduler receives these bytes, unpacks
them, and then updates its own internal data structures&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scheduling:&lt;/strong&gt; The scheduler then assigns ready tasks to workers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Communicate to workers:&lt;/strong&gt; The scheduler sends out lots of smaller
messages to each of the workers with the tasks that they can perform&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Workers work:&lt;/strong&gt; The workers then perform this work, and start
communicating back and forth with the scheduler to receive new instructions&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Generally most people today are concerned with steps 1-6. Once things get out
to the workers and progress bars start moving people tend to care a bit less
(but not zero).&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 66)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="what-do-other-people-do"&gt;
&lt;h1&gt;What do other people do?&lt;/h1&gt;
&lt;p&gt;Let’s look at a few things that other projects do, and see if there are things
that we can learn. These are commonly suggested, but there are challenges with
most of them.&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rewrite the scheduler it in C++/Rust/C/Cython&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Proposal: Python is slow. Want to make it faster? Don’t use Python. See
academic projects.&lt;/p&gt;
&lt;p&gt;Challenge: This makes sense for some parts of the pipeline above, but not
for others. It also makes it harder to attract maintainers.&lt;/p&gt;
&lt;p&gt;What we should consider: Some parts of the scheduler and optimization
algorithms could be written in a lower level language, maybe Cython. We’ll
need to be careful about maintainability.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Distributed scheduling&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Proposal: The scheduler is slow, maybe have many schedulers? See Ray.&lt;/p&gt;
&lt;p&gt;Challenge: It’s actually really hard to make the kinds of decisions that
Dask has to make if scheduling state is spread on many computers.
Distributed scheduling works better when the workload is very either
uniform or highly decoupled.
Distributed scheduling is really attractive to people who like solving
interesting/hard problems.&lt;/p&gt;
&lt;p&gt;What we should consider: We can move some simple logic down to the workers.
We’ve already done this with the easy stuff though.
It’s not clear how much additional benefit there is here.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build specialty scheduling around collections&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Proposal: If Dask were to become just a dataframe library or just an array
computing library then it could special-case things more effectively. See
Spark, Mars, and others.&lt;/p&gt;
&lt;p&gt;Challenge: Yes, but Dask is not a dataframe library or an array library.
The three use cases we mention above are all very different.&lt;/p&gt;
&lt;p&gt;What we should consider: modules like dask array and dask dataframe should
develop high level query blocks, and we should endeavor to
communicate these subgraphs over the wire directly so that they are more
compact.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 113)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="what-should-we-actually-do"&gt;
&lt;h1&gt;What should we actually do?&lt;/h1&gt;
&lt;p&gt;Because our pipeline has many stages, each of which can be slow for different
reasons, we’ll have to do many things. Additionally, this is a hard problem
because changing one piece of the project at this level has repurcussions for
many other pieces. The rest of this post tries to lay out a consistent set of
changes. Let’s start with a summary:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;For Dask array/dataframe let’s use high level graphs more aggressively so
that we can communicate only abstract representations between the client
and scheduler.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;But this breaks low level graph optimizations, fuse, cull, and slice fusion
in particular. We can make these unnecessary with two changes:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;We can make high level graphs considerably smarter to handle cull and slice fusion&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can move a bit more of the scheduling down to the workers to
replicate the advantages of low-level fusion there&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then, once all of the graph manipulation happens on the scheduler, let’s
try to accelerate it, hopefully in a language that the current dev
community can understand, like Cython&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At the same time in parallel, let’s take a look at our network stack&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We’ll go into these in more depth below&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 136)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="graph-generation"&gt;
&lt;h1&gt;Graph Generation&lt;/h1&gt;
&lt;section id="high-level-graph-history"&gt;
&lt;h2&gt;High Level Graph History&lt;/h2&gt;
&lt;p&gt;A year or two ago we moved graph generation costs from user-code-typing time to
graph-optimization-time with high level graphs&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;                 &lt;span class="c1"&gt;# graph generation used to happen here&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;  &lt;span class="c1"&gt;# now it happens here&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This really improved usability, and also let us do some high level
optimizations which sometimes allowed us to skip some lower-level optimization
costs.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="can-we-push-this-further"&gt;
&lt;h2&gt;Can we push this further?&lt;/h2&gt;
&lt;p&gt;The first four stages of our pipeline happen on the client:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graph generation:&lt;/strong&gt; Some Python code in a Dask collection library, like
dask array, calls the sum function, which generates a task graph on the
client side.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graph Optimization:&lt;/strong&gt; We then optimize that graph, also on the client
side, in order to remove unnecessary work, fuse tasks, apply important high
level optimizations, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graph Serializtion:&lt;/strong&gt; We now pack up that graph in a form that can be
sent over to the scheduler.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Graph Communication:&lt;/strong&gt; We fire those bytes across a wire over to the
scheduler&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If we’re able to stay with the high level graph representation through these
stages all the way until graph communication, then we can communicate a far
more compact representation up to the scheduler. We can drop a lot of these
costs, at least for the high level collection APIs (delayed and client.submit
would still be slow, client.map might be ok though).&lt;/p&gt;
&lt;p&gt;This has a couple of other nice benefits:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;User’s code won’t block, and we can alert the user immediately that we’re
on the job&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We’ve centralized costs in just the scheduler,
so there is now only one place where we might have to think about low-level code&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;(some conversation here: https://github.com/dask/distributed/issues/3872)&lt;/p&gt;
&lt;/section&gt;
&lt;section id="however-low-level-graph-optimizations-are-going-to-be-a-problem"&gt;
&lt;h2&gt;However, low-level graph optimizations are going to be a problem&lt;/h2&gt;
&lt;p&gt;In principle changing the distributed scheduler to accept a variety of graph
layer types is a tedious but straightforward problem. I’m not concerned.&lt;/p&gt;
&lt;p&gt;The bigger concern is what to do with low-level graph optimizations.
Today we have three of these that really matter:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Task fusion: this is what keeps your &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;read_parquet&lt;/span&gt;&lt;/code&gt; task merged with your
subsequent blockwise tasks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Culling: this is what makes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;df.head()&lt;/span&gt;&lt;/code&gt; or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x[0]&lt;/span&gt;&lt;/code&gt; fast&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Slice fusion: this is why &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x[:100][5]&lt;/span&gt;&lt;/code&gt; works well&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order for us to transmit abstract graph layers up to the scheduler, we need
to remove the need for these low level graph optimizations. I think that we
can do this with a combination of two approaches:&lt;/p&gt;
&lt;section id="more-clever-high-level-graph-manipulation"&gt;
&lt;h3&gt;More clever high level graph manipulation&lt;/h3&gt;
&lt;p&gt;We already do this a bit with blockwise, which has its own fusion, and which
removes much of the need for fusion generally. But other blockwise-like
operations, like &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;read_*&lt;/span&gt;&lt;/code&gt; will probably have to join the Blockwise family.&lt;/p&gt;
&lt;p&gt;Getting culling to work properly may require us to teach each of the individual
graph layers how to track dependencies in each layer type and cull themselves.
This may get tricky.&lt;/p&gt;
&lt;p&gt;Slicing is doable, we just need someone to go in, grok all of the current
slicing optimizations, and make high level graph layers for these
computations. This would be a great project for a sharp masters student&lt;/p&gt;
&lt;/section&gt;
&lt;section id="send-speculative-tasks-to-the-workers"&gt;
&lt;h3&gt;Send speculative tasks to the workers&lt;/h3&gt;
&lt;p&gt;High level Blockwise fusion handles many of the use cases for low-level fusion,
but not all. For example I/O layers like &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dd.read_parquet&lt;/span&gt;&lt;/code&gt; or &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;da.from_zarr&lt;/span&gt;&lt;/code&gt;
aren’t fused at a high level.&lt;/p&gt;
&lt;p&gt;We can resolve this either by making them blockwise layers (this requires
expanding the blockwise abstraction, which may be hard) or alternatively we can
start sending not-yet-ready tasks to workers before all of their dependencies
are finished if we’re highly confident that we know where they’re going to go.
This would give us some of the same results of fusion, but would keep all of
the task types separate (which would be nice for diagnostics) and might still
give us some of the same performance benefits that we get from fusion.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="unpack-abstract-graph-layers-on-the-scheduler"&gt;
&lt;h2&gt;Unpack abstract graph layers on the scheduler&lt;/h2&gt;
&lt;p&gt;So after we’ve removed the need for low level optimizations, and we just send
the abstract graph layers up to the scheduler directly, we’ll need to teach the
scheduler how to unpack those graph layers.&lt;/p&gt;
&lt;p&gt;This is a little tricky because the Scheduler can’t run user Python code (for
security reasons). We’ll have to register layer types (like blockwise,
rechunk, dataframe shuffle) that the scheduler knows about and trusts ahead of
time. We’ll still always support custom layers, and these will be at the same
speed that they’ve always been, but hopefully there will be far less need for
these if we go all-in on high level layers.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 240)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="rewrite-scheduler-in-low-level-language"&gt;
&lt;h1&gt;Rewrite scheduler in low-level language&lt;/h1&gt;
&lt;p&gt;Once most of the finicky bits are moved to the scheduler, we’ll have one place
where we can focus on low level graph state manipulation.&lt;/p&gt;
&lt;p&gt;Dask’s distributed scheduler is two things:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p&gt;A Tornado TCP application that receives signals from clients and workers
and send signals out to clients and workers&lt;/p&gt;
&lt;p&gt;This is async-heavy networking code&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A complex state machine internally that responds to those state changes&lt;/p&gt;
&lt;p&gt;This is a complex data structure heavy Python code&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;section id="networking"&gt;
&lt;h2&gt;Networking&lt;/h2&gt;
&lt;p&gt;Jim has an interesting project here that shows promise: https://github.com/jcrist/ery
Reducing latency between workers and the scheduler would be good, and would
help to accelerate stages 7-8 in the pipeline listed at the top of this post.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="state-machine"&gt;
&lt;h2&gt;State machine&lt;/h2&gt;
&lt;p&gt;Rewriting the state machine in some lower level language would be fine.
Ideally this would be in a language that was easy for the current maintainer
community to maintain, (Cython?) but we may also consider making a more firm
interface here that would allow other groups to experiment safely.&lt;/p&gt;
&lt;p&gt;There are some advantages to this (more experimentation by different groups)
but also some costs (splitting of core efforts and mismatches for users).
Also, I suspect that splitting out also probably means that we’ll probably lose the dashboard,
unless those other groups are very careful to expose the same state to Bokeh.&lt;/p&gt;
&lt;p&gt;There is more exploration to do here. Regardless I think that it probably makes
sense to try to isolate the state machine from the networking system.
Maybe this also makes it easier for people to profile in isolation.&lt;/p&gt;
&lt;p&gt;In speaking with a few different groups most people have expressed reservation
about having multiple different state machine codes. This was done in
MapReduce and Spark and resulted in difficult to maintain community dynamics.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 282)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="high-level-graph-optimizations"&gt;
&lt;h1&gt;High Level Graph Optimizations&lt;/h1&gt;
&lt;p&gt;Once we have everything in smarter high level graph layers,
we will also be more ripe for optimization.&lt;/p&gt;
&lt;p&gt;We’ll need a better way to write down these optimizations with a separated
traversal system and a set of rules. A few of us have
written these things before, maybe it’s time we revisit them.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/21/faster-scheduling.md&lt;/span&gt;, line 291)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="what-we-need"&gt;
&lt;h1&gt;What we need&lt;/h1&gt;
&lt;p&gt;This would require some effort, but I think that it would hit several high
profile problems at once. There are a few tricky things to get right:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;A framework for high level graph layers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An optimization system for high level graph layers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Separation of the scheduler into two parts&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For this I think that we’ll need people who are fairly familiar with Dask to do this right.&lt;/p&gt;
&lt;p&gt;And there there is a fair amount of follow-on work&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Build a hierarchy of layers for dask dataframe&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build a hierarchy of layers for dask array&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build optimizations for those to remove the need for low level graph
optimizations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rewrite core parts of the scheduler in Cython&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Experiment with the networking layer, maybe with a new Comm&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’ve been thinking about the right way to enact this change.
Historically most Dask changes over the past few years have been incremental or
peripheral, due to how burdened the maintainers are. There might be enough
pressure on this problem though that we can get some dedicated engineering
effort from a few organizations though, which might change how possible this is.
We’ve gotten 25% time from a few groups. I’m curious if we can gate 100% time
for some people for a few months.&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/07/21/faster-scheduling/"/>
    <summary>Document headings start at H2, not H1 [myst.header]</summary>
    <published>2020-07-21T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/07/17/scipy-2020-maintainers-track/</id>
    <title>Last Year in Review</title>
    <updated>2020-07-17T00:00:00+00:00</updated>
    <author>
      <name>Jacob Tomlinson (NVIDIA)</name>
    </author>
    <content type="html">&lt;p&gt;We recently enjoyed the 2020 SciPy conference from the comfort of our own homes this year. The 19th annual Scientific Computing with Python conference was a virtual conference this year due to the global pandemic. The annual SciPy Conference brought together over 1500 participants from industry, academia, and government to showcase their latest projects, learn from skilled users and developers, and collaborate on code development.&lt;/p&gt;
&lt;p&gt;As part of the maintainers track we presented an update on Dask.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/17/scipy-2020-maintainers-track.md&lt;/span&gt;, line 14)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="video"&gt;

&lt;p&gt;You can find the video on the SciPy YouTube channel. The Dask update runs from 0:00-19:30.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/XC0M76CmzHg" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/17/scipy-2020-maintainers-track.md&lt;/span&gt;, line 20)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="slides"&gt;
&lt;h1&gt;Slides&lt;/h1&gt;
&lt;script async class="speakerdeck-embed" data-id="ae0f04df5b7341eaa3e2989221be1889" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/07/17/scipy-2020-maintainers-track.md&lt;/span&gt;, line 24)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="talk-summary"&gt;
&lt;h1&gt;Talk Summary&lt;/h1&gt;
&lt;p&gt;Here’s a summary of the main topics covered in the talk. You can also check out the &lt;a class="reference external" href="https://threadreaderapp.com/thread/1280885850914553856.html"&gt;original thread on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;section id="community-overview"&gt;
&lt;h2&gt;Community overview&lt;/h2&gt;
&lt;p&gt;We’ve been trying to gauge the size of our community lately. The best proxy we have right now is the number of weekly visitors to the &lt;a class="reference external" href="https://docs.dask.org/en/latest/"&gt;Dask documentation&lt;/a&gt;. Which currently stands at around 10,000.&lt;/p&gt;
&lt;img alt="Dask documentation analytics showing growth to 10,000 weekly users over the last four years" src="https://pbs.twimg.com/media/EcaS9DpWkAEBaB4.jpg" style="width: 100%;" /&gt;
&lt;p&gt;Dask also came up in the &lt;a class="reference external" href="https://www.jetbrains.com/lp/devecosystem-2020/python/"&gt;Jetbrains Python developer survey&lt;/a&gt;. We were excited to see 5% of all the Python developers who filled out the survey said they use Dask. Which shows health in the PyData community as well as Dask.&lt;/p&gt;
&lt;img alt="Jetbrains survey results showing Dask used by 5% of Python users, beaten only by the Spark/hadoop ecosystem" src="https://pbs.twimg.com/media/EcaTTuiX0AIT2KB.jpg" style="width: 100%;" /&gt;
&lt;p&gt;We are running &lt;a class="reference external" href="https://dask.org/survey"&gt;our own survey&lt;/a&gt; at the moment. If you are a Dask user please take a few minutes to fill it out. We would really appreciate it.&lt;/p&gt;
&lt;img alt="Link to the Dask survey" src="https://pbs.twimg.com/media/EcaTlITXYAAVs-y.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="community-events"&gt;
&lt;h2&gt;Community events&lt;/h2&gt;
&lt;p&gt;In February we had an in-person &lt;a class="reference external" href="https://blog.dask.org/2020/04/28/dask-summit"&gt;Dask Summit&lt;/a&gt; where a mixture of OSS maintainers and institutional users met. We had talks and workshops to help figure out our challenges and set our direction.&lt;/p&gt;
&lt;img alt="A room of attendees at the Dask summit" src="https://pbs.twimg.com/media/EcaUbHLXQAAHckq.jpg" style="width: 100%;" /&gt;
&lt;p&gt;The Dask community also has a &lt;a class="reference external" href="https://docs.dask.org/en/latest/support.html"&gt;monthly meeting&lt;/a&gt;! It is held on the first Thursday of the month at 10:00 US Central Time. If you’re a Dask user you are welcome to come to hear updates from maintainers and share what you’re working on.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="community-projects"&gt;
&lt;h2&gt;Community projects&lt;/h2&gt;
&lt;p&gt;There are many projects built on Dask. Looking at the preliminary results from the 2020 Dask survey shows some that are especially popular.&lt;/p&gt;
&lt;img alt="Graph showing the most popular projects built on Dask; Xarray, RAPIDS, XGBoost, Prefect and Iris" src="https://pbs.twimg.com/media/EcaVSHpX0AAMDYs.png" style="width: 100%;" /&gt;
&lt;p&gt;Let’s take a look at each of those.&lt;/p&gt;
&lt;section id="xarray"&gt;
&lt;h3&gt;Xarray&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://xarray.pydata.org/en/stable/"&gt;Xarray&lt;/a&gt; allows you to work on multi-dimensional datasets that have supporting metadata arrays in a Pandas-like way.&lt;/p&gt;
&lt;img alt="Slide showing xarray code example" src="https://pbs.twimg.com/media/EcaVbOaXkAMQ4SU.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="rapids"&gt;
&lt;h3&gt;RAPIDS&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://rapids.ai/"&gt;RAPIDS&lt;/a&gt; is an open-source suite of GPU accelerated Python libraries. Using these tools you can execute end-to-end data science and analytics pipelines entirely on GPUs. All using familiar PyData APIs.&lt;/p&gt;
&lt;img alt="Slide showing RAPIDS dataframe code example" src="https://pbs.twimg.com/media/EcaWFfDXkAEX4B_.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="blazingsql"&gt;
&lt;h3&gt;BlazingSQL&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://blazingsql.com"&gt;BlazingSQL&lt;/a&gt; builds on RAPIDS and Dask to provide an open-source distributed, GPU accelerated SQL engine.&lt;/p&gt;
&lt;img alt="Slide showing BlazingSQL code example" src="https://pbs.twimg.com/media/EcaWW_CXsAM7XP7.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="xgboost"&gt;
&lt;h3&gt;XGBoost&lt;/h3&gt;
&lt;p&gt;While &lt;a class="reference external" href="https://examples.dask.org/machine-learning/xgboost.html"&gt;XGBoost&lt;/a&gt; has been around for a long time you can now prepare your data on your Dask cluster and then bootstrap your XGBoost cluster on top of Dask and hand the distributed dataframes straight over.&lt;/p&gt;
&lt;img alt="Slide showing XGBoost code example" src="https://pbs.twimg.com/media/EcaXKlRWsAAjLYe.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="prefect"&gt;
&lt;h3&gt;Prefect&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://www.prefect.io/"&gt;Prefect&lt;/a&gt; is a workflow manager which is built on top of Dask’s scheduling engine. “Users organize Tasks into Flows, and Prefect takes care of the rest.”&lt;/p&gt;
&lt;img alt="Slide showing Prefect code example" src="https://pbs.twimg.com/media/EcaXlf-XYAEPY-Z.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="iris"&gt;
&lt;h3&gt;Iris&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://scitools.org.uk/iris/docs/latest/"&gt;Iris&lt;/a&gt;, part of the &lt;a class="reference external" href="https://scitools.org.uk"&gt;SciTools&lt;/a&gt; suite of tools, uses the CF data model giving you a format-agnostic interface for working with your data. It excels when working with multi-dimensional Earth Science data, where tabular representations become unwieldy and inefficient.&lt;/p&gt;
&lt;img alt="Slide showing Iris code example" src="https://pbs.twimg.com/media/EcaX3S9XsAAU-Sm.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="more-tools"&gt;
&lt;h3&gt;More tools&lt;/h3&gt;
&lt;p&gt;These are the tools our community have told us they like so far. But if you use something which didn’t make the list then head to &lt;a class="reference external" href="https://dask.org/survey"&gt;our survey&lt;/a&gt; and let us know! According to PyPI there are many more out there.&lt;/p&gt;
&lt;img alt="Screenshot of PyPI showing 239 packages with Dask in their name" src="https://pbs.twimg.com/media/EcaYZmPWoAANYhr.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="user-groups"&gt;
&lt;h2&gt;User groups&lt;/h2&gt;
&lt;p&gt;There are many user groups who use Dask. Everything from life sciences, geophysical sciences and beamline facilities to finance, retail and logistics. Check out the great &lt;a class="reference external" href="https://youtu.be/t_GRK4L-bnw"&gt;“Who uses Dask?” talk&lt;/a&gt; from &lt;a class="reference external" href="https://twitter.com/mrocklin"&gt;Matthew Rocklin&lt;/a&gt; for more info.&lt;/p&gt;
&lt;img alt="Screenshot 'Who uses Dask?' YouTube video" src="https://pbs.twimg.com/media/EcaYj2JXQAEvgV3.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="for-profit-companies"&gt;
&lt;h2&gt;For profit companies&lt;/h2&gt;
&lt;p&gt;There has been an increase in for-profit companies building tools with Dask. Including &lt;a class="reference external" href="https://coiled.io/"&gt;Coiled Computing&lt;/a&gt;, &lt;a class="reference external" href="https://www.prefect.io/"&gt;Prefect&lt;/a&gt; and &lt;a class="reference external" href="https://www.saturncloud.io/s/"&gt;Saturn Cloud&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="Slide describing the for-profit companies Coiled, Prefect and Saturn Cloud" src="https://pbs.twimg.com/media/EcaZOqgX0AABFpQ.jpg" style="width: 100%;" /&gt;
&lt;p&gt;We’ve also seen large companies like Microsoft’s &lt;a class="reference external" href="https://azure.microsoft.com/en-gb/services/machine-learning/"&gt;Azure ML&lt;/a&gt; team contributing a cluster manager to &lt;a class="reference external" href="https://cloudprovider.dask.org/en/latest/#azure"&gt;Dask Cloudprovider&lt;/a&gt;. This helps folks get up and running with Dask on AzureML quicker and easier.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="recent-improvements"&gt;
&lt;h2&gt;Recent improvements&lt;/h2&gt;
&lt;section id="communications"&gt;
&lt;h3&gt;Communications&lt;/h3&gt;
&lt;p&gt;Moving on to recent improvements there has been a lot of work to get &lt;a class="reference external" href="https://www.openucx.org/"&gt;Open UCX&lt;/a&gt; supported as a protocol in Dask. Which allows worker-worker communication to be accelerated vastly with hardware that supports &lt;a class="reference external" href="https://en.wikipedia.org/wiki/InfiniBand"&gt;Infiniband&lt;/a&gt; or &lt;a class="reference external" href="https://en.wikipedia.org/wiki/NVLink"&gt;NVLink&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="Slide showing worker communication comparison between UCX/Infiniband and TCP with UCX being much faster" src="https://pbs.twimg.com/media/EcaaTxiXQAE4TD0.jpg" style="width: 100%;" /&gt;
&lt;p&gt;There have also been some &lt;a class="reference external" href="https://blogs.nvidia.com/blog/2020/06/22/big-data-analytics-tpcx-bb/"&gt;recent announcements&lt;/a&gt; around NVIDIA blowing away the TPCx-BB benchmark by outperforming the current leader by 20x. This is a huge success for all the open-source projects that were involved, including Dask.&lt;/p&gt;
&lt;img alt="Slide showing TPCx-BB benchmark results" src="https://pbs.twimg.com/media/EcabNUVWoAQGy8e.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="dask-gateway"&gt;
&lt;h3&gt;Dask Gateway&lt;/h3&gt;
&lt;p&gt;We’ve seen increased adoption of &lt;a class="reference external" href="https://gateway.dask.org"&gt;Dask Gateway&lt;/a&gt;. Many institutions are using it as a way to provide their staff with on-demand Dask clusters.&lt;/p&gt;
&lt;img alt="Slide showing Dask Gateway overview" src="https://pbs.twimg.com/media/EcabpirWkAYtx-W.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="cluster-map-plot-aka-pew-pew-pew"&gt;
&lt;h3&gt;Cluster map plot (aka ‘pew pew pew’)&lt;/h3&gt;
&lt;p&gt;The update that got the most 👏 feedback from the SciPy 2020 attendees was the Cluster Map Plot (known to maintainers as the “pew pew pew” plot). This plot shows a high-level overview of your Dask cluster scheduler and workers and the communication between them.&lt;/p&gt;
&lt;p&gt;&lt;video autoplay="" loop="" controls="" poster="https://pbs.twimg.com/tweet_video_thumb/EcacHRcXkAE53eI.jpg"&gt;&lt;source src="https://video.twimg.com/tweet_video/EcacHRcXkAE53eI.mp4" type="video/mp4"&gt;&lt;img alt="" src="https://pbs.twimg.com/tweet_video_thumb/EcacHRcXkAE53eI.jpg"&gt;&lt;/video&gt;&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="next-steps"&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;section id="high-level-graph-optimization"&gt;
&lt;h3&gt;High-level graph optimization&lt;/h3&gt;
&lt;p&gt;To wrap up with what Dask is going to be doing next we are going to be continuing to work on high-level graph optimization.&lt;/p&gt;
&lt;img alt="Slide showing High Level Graph documentation page" src="https://pbs.twimg.com/media/EcacZvfWsAIfqTz.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;section id="scheduler-performance"&gt;
&lt;h3&gt;Scheduler performance&lt;/h3&gt;
&lt;p&gt;With feedback from our community we are also going to be focussing on making the &lt;a class="reference external" href="https://github.com/dask/distributed/issues/3783"&gt;Dask scheduler more performant&lt;/a&gt;. There are a few things happening including a Rust implementation of the scheduler, dynamic task creation and ongoing benchmarking.&lt;/p&gt;
&lt;img alt="Scheduler performance tasks including a Rust implementation, benchmarking, dynamic tasks and Cython, PyPy and C experiments" src="https://pbs.twimg.com/media/Ecacr6pWoAEd4Tx.jpg" style="width: 100%;" /&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="chan-zuckerberg-foundation-maintainer-post"&gt;
&lt;h2&gt;Chan Zuckerberg Foundation maintainer post&lt;/h2&gt;
&lt;p&gt;Lastly I’m excited to share that with funding from the &lt;a class="reference external" href="https://chanzuckerberg.com/eoss/proposals/scaling-python-with-dask/"&gt;Chan Zuckerberg Foundation&lt;/a&gt;, Dask will be hiring a maintainer who will focus on growing usage in the biological sciences field. If that is of interest to you keep an eye on &lt;a class="reference external" href="https://twitter.com/dask_dev"&gt;our twitter account&lt;/a&gt; for more announcements.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/07/17/scipy-2020-maintainers-track/"/>
    <summary>We recently enjoyed the 2020 SciPy conference from the comfort of our own homes this year. The 19th annual Scientific Computing with Python conference was a virtual conference this year due to the global pandemic. The annual SciPy Conference brought together over 1500 participants from industry, academia, and government to showcase their latest projects, learn from skilled users and developers, and collaborate on code development.</summary>
    <category term="Community" label="Community"/>
    <category term="SciPy" label="SciPy"/>
    <category term="Talk" label="Talk"/>
    <published>2020-07-17T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/05/13/large-svds/</id>
    <title>Large SVDs</title>
    <updated>2020-05-13T00:00:00+00:00</updated>
    <author>
      <name>Alistair Miles (Oxford University)</name>
    </author>
    <content type="html">&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/05/13/large-svds.md&lt;/span&gt;, line 10)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="summary"&gt;

&lt;p&gt;We perform Singular Value Decomposition (SVD) calculations on large datasets.&lt;/p&gt;
&lt;p&gt;We modify the computation both by using fully precise and approximate methods,
and by using both CPUs and GPUs.&lt;/p&gt;
&lt;p&gt;In the end we compute an approximate SVD of 200GB of simulated data and using a mutli-GPU machine in 15-20 seconds.
Then we run this from a dataset stored in the cloud
where we find that I/O is, predictably, a major bottleneck.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/05/13/large-svds.md&lt;/span&gt;, line 21)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="svd-the-simple-case"&gt;
&lt;h1&gt;SVD - The simple case&lt;/h1&gt;
&lt;p&gt;Dask arrays contain a relatively sophisticated SVD algorithm that works in the
tall-and-skinny or short-and-fat cases, but not so well in the roughly-square
case. It works by taking QR decompositions of each block of the array,
combining the R matrices, doing another smaller SVD on those, and then
performing some matrix multiplication to get back to the full result. It’s
numerically stable and decently fast, assuming that the intermediate R
matrices of the QR decompositions mostly fit in memory.&lt;/p&gt;
&lt;p&gt;The memory constraints here are that if you have an &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;n&lt;/span&gt;&lt;/code&gt; by &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;m&lt;/span&gt;&lt;/code&gt; tall and
skinny array (&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;n&lt;/span&gt; &lt;span class="pre"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="pre"&gt;m&lt;/span&gt;&lt;/code&gt;) cut into &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;k&lt;/span&gt;&lt;/code&gt; blocks then you need to have about &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;m**2&lt;/span&gt; &lt;span class="pre"&gt;*&lt;/span&gt; &lt;span class="pre"&gt;k&lt;/span&gt;&lt;/code&gt; space. This is true in many cases, including typical PCA machine learning
workloads, where you have tabular data with a few columns (hundreds at most)
and many rows.&lt;/p&gt;
&lt;p&gt;It’s easy to use and quite robust.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;

&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;10000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;table&gt;  &lt;thead&gt;    &lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;th&gt; Array &lt;/th&gt;&lt;th&gt; Chunk &lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;th&gt; Bytes &lt;/th&gt;&lt;td&gt; 1.60 GB &lt;/td&gt; &lt;td&gt; 100.00 MB &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Shape &lt;/th&gt;&lt;td&gt; (10000000, 20) &lt;/td&gt; &lt;td&gt; (625000, 20) &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Count &lt;/th&gt;&lt;td&gt; 16 Tasks &lt;/td&gt;&lt;td&gt; 16 Chunks &lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;th&gt; Type &lt;/th&gt;&lt;td&gt; float64 &lt;/td&gt;&lt;td&gt; numpy.ndarray &lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;svg width="75" height="170" style="stroke:rgb(0,0,0);stroke-width:1" &gt;
  &lt;!-- Horizontal lines --&gt;
  &lt;line x1="0" y1="0" x2="25" y2="0" style="stroke-width:2" /&gt;
  &lt;line x1="0" y1="7" x2="25" y2="7" /&gt;
  &lt;line x1="0" y1="15" x2="25" y2="15" /&gt;
  &lt;line x1="0" y1="22" x2="25" y2="22" /&gt;
  &lt;line x1="0" y1="30" x2="25" y2="30" /&gt;
  &lt;line x1="0" y1="37" x2="25" y2="37" /&gt;
  &lt;line x1="0" y1="45" x2="25" y2="45" /&gt;
  &lt;line x1="0" y1="52" x2="25" y2="52" /&gt;
  &lt;line x1="0" y1="60" x2="25" y2="60" /&gt;
  &lt;line x1="0" y1="67" x2="25" y2="67" /&gt;
  &lt;line x1="0" y1="75" x2="25" y2="75" /&gt;
  &lt;line x1="0" y1="82" x2="25" y2="82" /&gt;
  &lt;line x1="0" y1="90" x2="25" y2="90" /&gt;
  &lt;line x1="0" y1="97" x2="25" y2="97" /&gt;
  &lt;line x1="0" y1="105" x2="25" y2="105" /&gt;
  &lt;line x1="0" y1="112" x2="25" y2="112" /&gt;
  &lt;line x1="0" y1="120" x2="25" y2="120" style="stroke-width:2" /&gt;
  &lt;!-- Vertical lines --&gt;
  &lt;line x1="0" y1="0" x2="0" y2="120" style="stroke-width:2" /&gt;
  &lt;line x1="25" y1="0" x2="25" y2="120" style="stroke-width:2" /&gt;
  &lt;!-- Colored Rectangle --&gt;
  &lt;polygon points="0.000000,0.000000 25.412617,0.000000 25.412617,120.000000 0.000000,120.000000" style="fill:#ECB172A0;stroke-width:0"/&gt;
  &lt;!-- Text --&gt;
&lt;p&gt;&lt;text x="12.706308" y="140.000000" font-size="1.0rem" font-weight="100" text-anchor="middle" &gt;20&lt;/text&gt;
&lt;text x="45.412617" y="60.000000" font-size="1.0rem" font-weight="100" text-anchor="middle" transform="rotate(-90,45.412617,60.000000)"&gt;10000000&lt;/text&gt;
&lt;/svg&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linalg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;svd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This works fine in the short and fat case too (when you have far more columns
than rows) but we’re always going to assume that one of your dimensions is
unchunked, and that the other dimension has chunks that are quite a bit
longer, otherwise, things might not fit into memory.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/05/13/large-svds.md&lt;/span&gt;, line 105)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="approximate-svd"&gt;
&lt;h1&gt;Approximate SVD&lt;/h1&gt;
&lt;p&gt;If your dataset is large in both dimensions then the algorithm above won’t work
as is. However, if you don’t need exact results, or if you only need a few of
the components, then there are a number of excellent approximation algorithms.&lt;/p&gt;
&lt;p&gt;Dask array has one of these approximation algorithms implemented in the
&lt;a class="reference external" href="https://docs.dask.org/en/latest/array-api.html#dask.array.linalg.svd_compressed"&gt;da.linalg.svd_compressed&lt;/a&gt;
function. And with it we can compute the approximate SVD of very large
matrices.&lt;/p&gt;
&lt;p&gt;We were recently working on a problem (explained below) and found that we were
still running out of memory when dealing with this algorithm. There were two
challenges that we ran into:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p&gt;The algorithm requires multiple passes over the data, but the Dask task
scheduler was keeping the input matrix in memory after it had been loaded once
in order to avoid recomputation.
Things still worked, but Dask had to move the data to disk and back
repeatedly, which reduced performance significantly.&lt;/p&gt;
&lt;p&gt;We resolved this by including explicit recomputation steps in the algorithm.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Related chunks of data would be loaded at different times, and so would
need to stick around longer than necessary to wait for their associated
chunks.&lt;/p&gt;
&lt;p&gt;We resolved this by engaging task fusion as an optimization pass.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before diving further into the technical solution
we quickly provide the use case that was motivating this work.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/05/13/large-svds.md&lt;/span&gt;, line 137)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="application-genomics"&gt;
&lt;h1&gt;Application - Genomics&lt;/h1&gt;
&lt;p&gt;Many studies are using genome sequencing to study genetic variation
between different individuals within a species. These includes
studies of human populations, but also other species such as mice,
mosquitoes or disease-causing parasites. These studies will, in
general, find a large number of sites in the genome sequence where
individuals differ from each other. For example, humans have more
than 100 million variable sites in the genome, and modern studies
like the &lt;a class="reference external" href="https://www.ukbiobank.ac.uk/"&gt;UK BioBank&lt;/a&gt; are working towards
sequencing the genomes of 1 million individuals or more.&lt;/p&gt;
&lt;p&gt;In diploid species like humans, mice or mosquitoes, each individual
carries two genome sequences, one inherited from each parent. At each
of the 100 million variable genome sites there will be two or more
“alleles” that a single genome might carry. One way to think about
this is via the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Punnett_square"&gt;Punnett
square&lt;/a&gt;, which
represents the different possible genotypes that one individual might
carry at one of these variable sites:&lt;/p&gt;
&lt;td&gt;
&lt;img src="https://upload.wikimedia.org/wikipedia/commons/9/93/Punnett_Square_Genetic_Carriers.PNG" alt="punnet square" height="40%" width="40%"&gt;
&lt;/td&gt;
&lt;p&gt;In the above there are three possible genotypes: AA, Aa, and aa. For
computational genomics, these genotypes can be encoded as 0, 1, or 2.
In a study of a species with M genetic variants assayed in N
individual samples, we can represent these genotypes as an (M x N)
array of integers. For a modern human genetics study, the scale of
this array might approach (100 million x 1 million). (Although in
practice, the size of the first dimension (number of variants) can be
reduced somewhat, by at least an order of magnitude, because many
genetic variants will carry little information and/or be correlated
with each other.)&lt;/p&gt;
&lt;p&gt;These genetic differences are not random, but carry information about
patterns of genetic similarity and shared ancestry between
individuals, because of the way they have been inherited through many
generations. A common task is to perform a dimensionality reduction
analysis on these data, such as a &lt;a class="reference external" href="https://journals.plos.org/plosgenetics/article?id=10.1371/journal.pgen.0020190"&gt;principal components
analysis&lt;/a&gt;
(SVD), to identify genetic structure reflecting these differencies in
degree of shared ancestry. This is an essential part of discovering
genetic variants associated with different diseases, and for learning
more about the genetic history of populations and species.&lt;/p&gt;
&lt;p&gt;Reducing the time taken to compute an analysis such as SVD, like all
science, allows for exploring larger datasets and testing more
hypotheses in less time. Practically, this means not simply a fast
SVD but an accelerated pipeline end-to-end, from data loading to
analysis, to understanding.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;We want to run an experiment in less time than it takes to make a cup of tea&lt;/em&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/05/13/large-svds.md&lt;/span&gt;, line 192)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="performant-svds-w-dask"&gt;
&lt;h1&gt;Performant SVDs w/ Dask&lt;/h1&gt;
&lt;p&gt;Now that we have that scientific background, let’s transition back to talking about computation.&lt;/p&gt;
&lt;p&gt;To stop Dask from holding onto the data we intentionally trigger computation as
we build up the graph. This is a bit atypical in Dask calculations (we prefer
to have as much of the computation at once before computing) but useful given
the multiple-pass nature of this problem. This was a fairly easy change, and
is available in &lt;a class="reference external" href="https://github.com/dask/dask/pull/5041"&gt;dask/dask #5041&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, we found that it was helpful to turn on moderately wide task
fusion.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask&lt;/span&gt;
&lt;span class="n"&gt;dask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;optimization.fuse.ave-width&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/05/13/large-svds.md&lt;/span&gt;, line 210)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="then-things-work-fine"&gt;
&lt;h1&gt;Then things work fine&lt;/h1&gt;
&lt;p&gt;We’re going to try this SVD on a few different choices of hardware including:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;A MacBook Pro&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A DGX-2, an NVIDIA worksation with 16 high-end GPUs and fast interconnect&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A twenty-node cluster on AWS&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;section id="macbook-pro"&gt;
&lt;h2&gt;Macbook Pro&lt;/h2&gt;
&lt;p&gt;We can happily perform an SVD on a 20GB array on a Macbook Pro&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;

&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linalg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;svd_compressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This call is no longer entirely lazy, and it recomputes &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; a couple times, but
it works, and it works using only a few GB of RAM on a consumer laptop.&lt;/p&gt;
&lt;p&gt;It takes around 2min 30s time to compute that on a laptop.
That’s great! It was super easy to try out, didn’t require any special
hardware or setup, and in many cases is fast enough.
By working locally we can iterate quickly.&lt;/p&gt;
&lt;p&gt;Now that things work, we can experiment with different hardware.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="adding-gpus-a-15-second-svd"&gt;
&lt;h2&gt;Adding GPUs (a 15 second SVD)&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: one of the authors (Ben Zaitlen) works for NVIDIA&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can dramatically increase performance by using a multi-GPU machine.
NVIDIA and other manufacturers now make machines with multiple GPUs co-located in the same physical box.
In the following section, we will run the calculations on a &lt;strong&gt;DGX2&lt;/strong&gt;, a machine with 16 GPUs and fast interconnect between the GPUs.&lt;/p&gt;
&lt;p&gt;Below is almost the same code, running in significantly less same time but we make the
following changes:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;We increase the size of the array by a factor of &lt;strong&gt;10x&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We switch out NumPy for CuPy, a GPU NumPy implementation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We use a sixteen-GPU DGX-2 machine with NVLink interconnects between GPUs (NVLink will dramatically decrease transfer time between workers)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On A DGX2 we can calculate an SVD on a 200GB Dask array between 10 to 15 seconds.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://gist.github.com/quasiben/98ee254920837313946f621e103d41f4"&gt;full notebook is here&lt;/a&gt;,
but the relevant code snippets are below:&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Some GPU specific setup&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask_cuda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LocalCluster&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LocalCluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;cupy&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.array&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;da&lt;/span&gt;
&lt;span class="n"&gt;rs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RandomState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RandomState&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cupy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RandomState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create the data and run the SVD as normal&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
               &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;uint8&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linalg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;svd_compressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To see this run, we recommend viewing this screencast:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/6hmt1gARqp0" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="read-dataset-from-disk"&gt;
&lt;h2&gt;Read dataset from Disk&lt;/h2&gt;
&lt;p&gt;While impressive, the computation above is mostly bound by generating random
data and then performing matrix calculations. GPUs are good at both of these
things.&lt;/p&gt;
&lt;p&gt;In practice though, our input array won’t be randomly generated, it will be
coming from some dataset stored on disk or increasingly more common, stored in the cloud.
To make things more realistic we perform a similar calculation with data
stored in a &lt;a class="reference external" href="https://zarr.readthedocs.io/en/stable/"&gt;Zarr format&lt;/a&gt;
in &lt;a class="reference external" href="https://cloud.google.com/storage"&gt;GCS&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this &lt;a class="reference external" href="https://gist.github.com/quasiben/e52bc740ae22ae321f30987c65998078"&gt;Zarr SVD example&lt;/a&gt;,
we load a 25GB GCS backed data set onto a DGX2,
run a few processing steps, then perform an SVD.
The combination of preprocessing and SVD calculations ran in 18.7 sec and the data loading took 14.3 seconds.&lt;/p&gt;
&lt;p&gt;Again, on a DGX2, from data loading to SVD we are running in time less than it would take to make a cup of tea.
However, the data loading can be accelerated.
From GCS we are reading into data into the main memory of the machine (host memory), uncompressing the zarr bits,
then moving the data from host memory to the GPU (device memory). Passing data back and forth between host and device memory can significantly decrease performance. Reading directly into the GPU, bypassing host memory, would improve the overall pipeline.&lt;/p&gt;
&lt;p&gt;And so we come back to a common lesson of high performance computing:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;High performance computing isn’t about doing one thing exceedingly well, it’s
about doing nothing poorly&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In this case GPUs made our computation fast enough that we now need to focus on
other components of our system, notably disk bandwidth, and a direct reader for
Zarr data to GPU memory.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="cloud"&gt;
&lt;h2&gt;Cloud&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Diclaimer: one of the authors (Matthew Rocklin) works for Coiled Computing&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can also run this on the cloud with any number of frameworks.
In this case we used the &lt;a class="reference external" href="https://coiled.io"&gt;Coiled Cloud&lt;/a&gt; service to deploy on AWS&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;coiled_cloud&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Cloud&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Cluster&lt;/span&gt;
&lt;span class="n"&gt;cloud&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Cloud&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;cloud&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_cluster_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;organization&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;friends&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;genomics&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;worker_cpu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;worker_memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;16 GiB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;worker_environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;OMP_NUM_THREADS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;OPENBLAS_NUM_THREADS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;# &amp;quot;EXTRA_PIP_PACKAGES&amp;quot;: &amp;quot;zarr&amp;quot;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Cluster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;organization&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;friends&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;typename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;genomics&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;dask.distributed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# then proceed as normal&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Using 20 machines with a total of 80 CPU cores on a dataset that was 10x larger
than the MacBook pro example we were able to run in about the same amount of
time. This shows near optimal scaling for this problem, which is nice to see
given how complex the SVD calculation is.&lt;/p&gt;
&lt;p&gt;A screencast of this problem is viewable here&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/qaJcAvhgLy4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/05/13/large-svds.md&lt;/span&gt;, line 360)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="compression"&gt;
&lt;h1&gt;Compression&lt;/h1&gt;
&lt;p&gt;One of the easiest ways for us to improve performance is to reduce the size of
this data through compression.
This data is highly compressible for two reasons:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;The real-world data itself has structure and repetition
(although the random play data does not)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We’re storing entries that take on only four values.
We’re using eight-bit integers when we only needed two-bit integers&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s solve the second problem first.&lt;/p&gt;
&lt;section id="compression-with-bit-twiddling"&gt;
&lt;h2&gt;Compression with bit twiddling&lt;/h2&gt;
&lt;p&gt;Ideally Numpy would have a two-bit integer datatype.
Unfortunately it doesn’t, and this is hard because memory in computers is
generally thought of in full bytes.&lt;/p&gt;
&lt;p&gt;To work around this we can use bit arithmetic to shove four values into a single value
Here are functions that do that, assuming that our array is square,
and the last dimension is divisible by four.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros_like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;decompress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;back&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros_like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mb"&gt;0b00000011&lt;/span&gt;
    &lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mb"&gt;0b00001100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mb"&gt;0b00110000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mb"&gt;0b11000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;back&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then, we can use these functions along with Dask to store our data in a
compressed state, but lazily decompress on-demand.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decompress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That’s it. We compress each block our data and store that in memory.
However the output variable that we have, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;x&lt;/span&gt;&lt;/code&gt; will decompress each chunk before
we operate on it, so we don’t need to worry about handling compressed blocks.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="compression-with-zarr"&gt;
&lt;h2&gt;Compression with Zarr&lt;/h2&gt;
&lt;p&gt;A slightly more general but probably less efficient route would be to compress
our arrays with a proper compression library like Zarr.&lt;/p&gt;
&lt;p&gt;The example below shows how we do this in practice.&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;zarr&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numcodecs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Blosc&lt;/span&gt;
&lt;span class="n"&gt;compressor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Blosc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lz4&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clevel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shuffle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Blosc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BITSHUFFLE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zarr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compressor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;compressor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_blocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Additionally, if we’re using the dask-distributed scheduler then we want to
make sure that the Blosc compression library doesn’t use additional threads.
That way we don’t have parallel calls of a parallel library, which can cause
some contention&lt;/p&gt;
&lt;div class="highlight-python notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;set_no_threads_blosc&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot; Stop blosc from using multiple threads &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numcodecs&lt;/span&gt;
    &lt;span class="n"&gt;numcodecs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blosc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;

&lt;span class="c1"&gt;# Run on all workers&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_worker_plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set_no_threads_blosc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This approach is more general, and probably a good trick to have up ones’
sleeve, but it also doesn’t work on GPUs, which in the end is why we ended up
going with the bit-twiddling approach one section above, which uses API that
was uniformly accessible within the Numpy and CuPy libraries.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/05/13/large-svds.md&lt;/span&gt;, line 452)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="final-thoughts"&gt;
&lt;h1&gt;Final Thoughts&lt;/h1&gt;
&lt;p&gt;In this post we did a few things, all around a single important problems in
genomics.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;We learned a bit of science&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We translated a science problem into a computational problem,
and in particular into a request to perform large singular value decompositions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We used a canned algorithm in dask.array that performed pretty well,
assuming that we’re comfortable going over the array in a few passes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We then tried that algorithm on three architectures quickly&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;A Macbook Pro&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A multi-GPU machine&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A cluster in the cloud&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally we talked about some tricks to pack more data into the same memory
with compression&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This problem was nice in that we got to dive deep into a technical science
problem, and yet also try a bunch of architecture quickly to investigate
hardware choices that we might make in the future.&lt;/p&gt;
&lt;p&gt;We used several technologies here today, made by several different communities
and companies. It was great to see how they all worked together seamlessly to
provide a flexible-yet-consistent experience.&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/05/13/large-svds/"/>
    <summary>Document headings start at H2, not H1 [myst.header]</summary>
    <category term="CuPy" label="CuPy"/>
    <category term="GPU" label="GPU"/>
    <category term="array" label="array"/>
    <published>2020-05-13T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/04/28/dask-summit/</id>
    <title>Dask Summit</title>
    <updated>2020-04-28T00:00:00+00:00</updated>
    <author>
      <name>Mike McCarty (Capital One Center for Machine Learning) and Matthew Rocklin (Coiled Computing)</name>
    </author>
    <content type="html">&lt;p&gt;In late February members of the Dask community gathered together in Washington, DC.
This was a mix of open source project maintainers
and active users from a broad range of institutions.
This post shares a summary of what happened at this workshop,
including slides, images, and lessons learned.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: this event happened just before the widespread effects of the COVID-19
outbreak in the US and Europe. We were glad to see each other, but wouldn’t recommend doing this today.&lt;/em&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 18)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="who-came"&gt;

&lt;p&gt;This was an invite-only event of fifty people, with a cap of three people per
organization. We intentionally invited an even mix of half people who
self-identified as open source maintainers, and half people who identified as
institutional users. We had attendees from academia, small startups, tech
companies, government institutions, and large enterprise. It was surprising
how much we all had in common.
We had attendees from the following companies:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Anaconda&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Berkeley Institute for Datascience&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Blue Yonder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Brookhaven National Lab&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Capital One&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chan Zuckerberg Initiative&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Coiled Computing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Columbia University&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;D. E. Shaw &amp;amp; Co.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flatiron Health&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Howard Hughes Medial Institute, Janelia Research Campus&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inria&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kitware&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lawrence Berkeley National Lab&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Los Alamos National Laboratory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MetroStar Systems&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Microsoft&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NIMH&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NVIDIA&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;National Center for Atmospheric Research (NCAR)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;National Energy Research Scientific Computing (NERSC) Center&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prefect&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quansight&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Related Sciences&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Saturn Cloud&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Smithsonian Institution&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SymphonyRM&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The HDF Group&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;USGS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ursa Labs&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 59)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="objectives"&gt;
&lt;h1&gt;Objectives&lt;/h1&gt;
&lt;p&gt;The Dask community comes from a broad range of backgrounds.
It’s an odd bunch, all solving very different problems,
but all with a surprisingly common set of needs.
We’ve all known each other on GitHub for several years,
and have a long shared history, but many of us had never met in person.&lt;/p&gt;
&lt;p&gt;In hindsight, this workshop served two main purposes:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;It helped us to see that we were all struggling with the same problems
and so helped to form direction and motivate future work&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It helped us to create social bonds and collaborations that help us manage
the day to day challenges of building and maintaining community software
across organizations&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 75)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="structure"&gt;
&lt;h1&gt;Structure&lt;/h1&gt;
&lt;p&gt;We met for three days.&lt;/p&gt;
&lt;p&gt;On days 1-2 we started with quick talks from the attendees and followed with
afternoon working sessions.&lt;/p&gt;
&lt;p&gt;Talks were short around 10-15 minutes
(having only experts in the room meant that we could easily skip the introductory material)
and always had the same structure:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p&gt;A brief description of the domain that they’re in and why it’s important&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example: We look at seismic readings from thousand of measurement devices around
the world to understand and predict catastrophic earthquakes&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How they use Dask to solve this problem&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example: this means that we need to cross-correlate thousands of very
long timeseries. We use Xarray on AWS with some custom operations.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What is wrong with Dask, and what they would like to see improved&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Example: It turns out that our axes labels can grow larger than what
Xarray was designed for. Also, the task graph size for Dask can become a
limitation&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These talks were structured into six sections:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Workflow and pipelines&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deployment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Imaging&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;General data analysis&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performance and tooling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Xarray&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We didn’t capture video, but we do have slides from each of the talks below.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 113)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="workflow-and-pipelines"&gt;
&lt;h1&gt;1: Workflow and Pipelines&lt;/h1&gt;
&lt;section id="blue-yonder"&gt;
&lt;h2&gt;Blue Yonder&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: ETL Pipelines for Machine Learning&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Florian Jetter&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also attending:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Nefta Kanilmaz&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lucas Rademaker&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vSk2zAnSmzpbz5BgK70mpPmeQeV4h1IkCQh-EU8KXrZFJQGHmlMTuHvln3CmOQVTg/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="360" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="prefect"&gt;
&lt;h2&gt;Prefect&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Prefect + Dask: Parallel / Distributed Workflows&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Chris White, CTO&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="//www.slideshare.net/slideshow/embed_code/key/4wiUwkDHmdzVTW" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen&gt; &lt;/iframe&gt; &lt;div style="margin-bottom:5px"&gt; &lt;strong&gt; &lt;a href="//www.slideshare.net/ChrisWhite249/dask-prefect" title="Dask + Prefect" target="_blank"&gt;Dask + Prefect&lt;/a&gt; &lt;/strong&gt; from &lt;strong&gt;&lt;a href="https://www.slideshare.net/ChrisWhite249" target="_blank"&gt;Chris White&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;
&lt;/section&gt;
&lt;section id="symphonyrm"&gt;
&lt;h2&gt;SymphonyRM&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Dask and Prefect for Data Science in Healthcare&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenter: Joe Schmid, CTO&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vSCDbXrXtrL9vmA0hQ1NNk5DY0-3Azpcf9FbYgjoLuKV79vf_nm7wdUZl1NsL5DZqRmlUTP--u9HM56/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 139)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="deployment"&gt;
&lt;h1&gt;2: Deployment&lt;/h1&gt;
&lt;section id="quansight"&gt;
&lt;h2&gt;Quansight&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Building Cloud-based Data Science Platforms with Dask&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Dharhas Pothina&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also attending: - James Bourbeau - Dhavide Aruliah&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vSZ1fSrkWvzMPlx-f0qk7w2xj_uDp5q-Tg11S9UlynoohZV0VYjdFduDUrAdhptSYfpzFu9Wask1WSN/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="479" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="nvidia-and-microsoft-azure"&gt;
&lt;h2&gt;NVIDIA and Microsoft/Azure&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Native Cloud Deployment with Dask-Cloudprovider&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Jacob Tomlinson, Tom Drabas, and Code Peterson&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vT-B1c0r8MWMF8wvW4lNly-qmOCqhFqKdhshXnVql6UVkYQ-aGprY3Du0VH0PJBccOmM84ncw0lDV77/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="inria"&gt;
&lt;h2&gt;Inria&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: HPC Deployments with Dask-Jobqueue&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Loïc Esteve&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://lesteve.github.io/talks/2020-dask-jobqueue-dask-workshop/slides.html" frameborder="0" width="1000" height="800"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="anaconda"&gt;
&lt;h2&gt;Anaconda&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Title: Dask Gateway&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Jim Crist&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also attending: - Tom Augspurger - Eric Dill - Jonathan Helmus&lt;/p&gt;
&lt;style&gt;
    iframe {
        overflow:hidden;
    }
&lt;/style&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="http://jcrist.github.io/talks/dask_summit_2020/slides.html" frameborder="1" width="600" height="355" scrolling="no"&gt;&lt;/iframe&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 176)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="imaging"&gt;
&lt;h1&gt;3: Imaging&lt;/h1&gt;
&lt;section id="kitware"&gt;
&lt;h2&gt;Kitware&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Scientific Image Analysis and Visualization with ITK&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Matt McCormick&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vRz2SV2G-1LEXXCF0n9vugF13s7ABpLDT-yH3WtxQEOjt2FVHE7apl3nQhqkOiLeY9kSzM_Mrs6fJOk/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="id1"&gt;
&lt;h2&gt;Kitware&lt;/h2&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: INFO/1 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 185); &lt;em&gt;&lt;a href="#id1"&gt;backlink&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Duplicate implicit target name: “kitware”.&lt;/p&gt;
&lt;/aside&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Image processing with X-rays and electrons&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Marcus Hanwell&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vRT--l76IcSPlIP_N6ClUtm2ECZaxkvIGrBNyyoFmJNQu6kS6CilWoleIMCur2FQ7ZpEkkCsw7UXnRd/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="national-institutes-of-mental-health"&gt;
&lt;h2&gt;National Institutes of Mental Health&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Brain imaging&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: John Lee&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vTH1X0cSjozmCDvSQ8CtcxPPYejkLROC_b92W6uwznG5litWq_MwKJzUMnAQi0Prw/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="janelia-howard-hughes-medical-institute"&gt;
&lt;h2&gt;Janelia / Howard Hughes Medical Institute&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Spark, Dask, and FlyEM HPC&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Stuart Berg&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vSnZ-JgHAoAOUirqmLcI3GaKyC4oVo3vThZZ4oyx8vZjJ66An09JIhbcoy6k7ufTw/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="479" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 206)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="general-data-analysis"&gt;
&lt;h1&gt;4: General Data Analysis&lt;/h1&gt;
&lt;section id="brookhaven-national-labs"&gt;
&lt;h2&gt;Brookhaven National Labs&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Dask at DOE Light Sources&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Dan Allan&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vRd8PVHjW7Umjo1rUjR7XWDT95CcEoE_3jH-ceDHsN_lMv_4M2qnlFiFvtMl9SX0Eb1EFQTGkzUWCDy/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="d-e-shaw-group"&gt;
&lt;h2&gt;D.E. Shaw Group&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Dask at D.E. Shaw&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Akihiro Matsukawa&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="id2"&gt;
&lt;h2&gt;Anaconda&lt;/h2&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: INFO/1 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 220); &lt;em&gt;&lt;a href="#id2"&gt;backlink&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Duplicate implicit target name: “anaconda”.&lt;/p&gt;
&lt;/aside&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Dask Dataframes and Dask-ML summary&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Tom Augspurger&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vTs6nNsMkV92Uj4QUns1VB8pKlKSsRgUAGwvcbTOPqMazSAhxtawVNgb04YmHVFmb0z8-no-cdS8mE8/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 227)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="performance-and-tooling"&gt;
&lt;h1&gt;5: Performance and Tooling&lt;/h1&gt;
&lt;section id="berkeley-institute-for-data-science"&gt;
&lt;h2&gt;Berkeley Institute for Data Science&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Numpy APIs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Sebastian Berg&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="ursa-labs"&gt;
&lt;h2&gt;Ursa Labs&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Arrow&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Joris Van den Bossche&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vQY3ubjCFkMcU_b8p2xmuXN8VVR1BxxSWZDe5Vy-ftnH2CstZILvTo2pRBv5R_VDk85rNjVoWew2AJl/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="nvidia"&gt;
&lt;h2&gt;NVIDIA&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: RAPIDS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Keith Kraus&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also attending: - Mike Beaumont - Richard Zamora&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vQiNrupzQlSqsu95AAHqIhU1V_iVUav_0WlIp4dXdSE6Izze1BL8mkFbIzg7p8CndEi9bjWaC2OVlyu/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="id3"&gt;
&lt;h2&gt;NVIDIA&lt;/h2&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: INFO/1 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 249); &lt;em&gt;&lt;a href="#id3"&gt;backlink&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Duplicate implicit target name: “nvidia”.&lt;/p&gt;
&lt;/aside&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: UCX&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Ben Zaitlen&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vRU-vsXsnXgeLKdmtWZkZVV_-mOojsNesCbQKJgmWkwSjxj5ZdwkmS6X4tOt3HpFrIOfNROSlV_8l84/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 256)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="xarray"&gt;
&lt;h1&gt;6: Xarray&lt;/h1&gt;
&lt;section id="usgs-and-ncar"&gt;
&lt;h2&gt;USGS and NCAR&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Dask in Pangeo&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Rich Signell and Anderson Banihirwe&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vStqGiQy6pJDYhRgF-BZylQussINK5BGlhnidOVCUECo_ebYqRH9cSY4e-2z7BfFFvTfvkqq_M1jXBX/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="366" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;/section&gt;
&lt;section id="lbnl"&gt;
&lt;h2&gt;LBNL&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Accelerating Experimental Science with Dask&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Matt Henderson&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://drive.google.com/file/d/1DVVzYmhkDhO2xs0tmxpPCkxx5c4o63bO/view"&gt;Slides&lt;/a&gt; - Fill too large to preview&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="lanl"&gt;
&lt;h2&gt;LANL&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Title: Seismic Analysis&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Presenters: Jonathan MacCarthy&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;iframe src="https://docs.google.com/presentation/d/e/2PACX-1vSWAgKLxt1tBZxXjQfIRQNFPvAMFYZ-z0hkMy7euPnOHwO9pomH_gM8cKUTKXA68w/embed?start=false&amp;loop=false&amp;delayms=3000" frameborder="0" width="600" height="404" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"&gt;&lt;/iframe&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 278)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="unstructured-time"&gt;
&lt;h1&gt;Unstructured Time&lt;/h1&gt;
&lt;p&gt;Having rapid fire talks in the morning, followed by unstructured time in the
afternoon was a productive combination. Below you’ll see pictures from
geo-scientists and quants talking about the same challenges, and library
maintainers from Pandas/Arrow/RAPDIS/Dask all working together on joint
solutions.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://pbs.twimg.com/media/ERykEc9XUAEFq-L?format=jpg&amp;name=large"
     width="40%"&gt;
&lt;img src="https://pbs.twimg.com/media/ERzEcEeXkAU35sg?format=jpg&amp;name=large"
    width="40%"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://pbs.twimg.com/media/ERyz7B5X0AIrDkn?format=jpg&amp;name=large"
    width="40%"&gt;
&lt;img src="https://pbs.twimg.com/media/ERzXhHnWAAE_zDA?format=jpg&amp;name=large"
    width="40%"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://pbs.twimg.com/media/ERz3GDgXsAcE6Id?format=jpg&amp;name=large"
    width="40%"&gt;
&lt;img src="https://pbs.twimg.com/media/ERz4ur2WkAAGJwm?format=jpg&amp;name=large"
    width="40%"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://pbs.twimg.com/media/ER0sZceUYAAF5fW?format=jpg&amp;name=large"
    width="40%"&gt;
&lt;img src="https://pbs.twimg.com/media/ER0yY2rX0AEFfXi?format=jpg&amp;name=large"
    width="40%"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://pbs.twimg.com/media/ERyz98YWAAAmJbE?format=jpg&amp;name=large"
    width="40%"&gt;
&lt;img src="https://pbs.twimg.com/media/ERz5S2dWoAEhFHc?format=jpg&amp;name=large"
    width="40%"&gt;&lt;/p&gt;
&lt;p&gt;This unstructured time is a productive combination that we would recommend to
other technically diverse groups in the future. Engagement and productivity was
really high throughout the workshop.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 315)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="final-thoughts"&gt;
&lt;h1&gt;Final Thoughts&lt;/h1&gt;
&lt;p&gt;Dask’s strength comes from this broad community of stakeholders.&lt;/p&gt;
&lt;p&gt;An early technical focus on simplicity and pragmatism allowed the project to be
quickly adopted within many different domains. As a result, the practitioners
within these domains are largely the ones driving the project forward today.
This Community Driven Development brings an incredible diversity of technical
and cultural challenges and experience that force the project to quickly evolve
in a way that is constrained towards pragmatism.&lt;/p&gt;
&lt;p&gt;There is still plenty of work to do.
Short term this workshop brought up many technical challenges that are shared
by all (simpler deployments, scheduling under task constraints, active memory
management). Longer term we need to welcome more people into this community,
both by increasing the diversity of domains, and the diversity of individuals
(the vast majority of attendees were white men in their thirties from the US
and western Europe).&lt;/p&gt;
&lt;p&gt;We’re in a good position to effect this change.
Dask’s recent growth has captured the attention of many different institutions.
Now is a critical time to be intentional about the projects growth to make sure
that the project and community continue to reflect a broad and ethical set of
principles.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/04/28/dask-summit.md&lt;/span&gt;, line 340)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="acknowledgements"&gt;
&lt;h1&gt;Acknowledgements&lt;/h1&gt;
&lt;section id="sponsors"&gt;
&lt;h2&gt;Sponsors&lt;/h2&gt;
&lt;p&gt;Without the support of our sponsors, this workshop would not have been possible.
Thanks to Anaconda, Capital One and NVIDIA for their support and generous
donations toward this event.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="organizers"&gt;
&lt;h2&gt;Organizers&lt;/h2&gt;
&lt;p&gt;Thank you very much to the organizers who took time from their busy schedules
and worked so hard to put together this event.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Brittany Treadway (Capital One)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keith Kraus (NVIDIA)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Matthew Rocklin (Coiled Computing)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mike Beaumont (NVIDIA)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mike McCarty (Capital One)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Neia Woodson (Capital One)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jake Schmitt (Capital One)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jim Crist (Anaconda)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/04/28/dask-summit/"/>
    <summary>In late February members of the Dask community gathered together in Washington, DC.
This was a mix of open source project maintainers
and active users from a broad range of institutions.
This post shares a summary of what happened at this workshop,
including slides, images, and lessons learned.</summary>
    <published>2020-04-28T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>https://blog.dask.org/2020/01/14/estimating-users/</id>
    <title>Estimating Users</title>
    <updated>2020-01-14T00:00:00+00:00</updated>
    <author>
      <name>Matthew Rocklin</name>
    </author>
    <content type="html">&lt;p&gt;People often ask me &lt;em&gt;“How many people use Dask?”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As with any non-invasive open source software, the answer to this is
&lt;em&gt;“I don’t know”&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;There are many possible proxies for user counts, like downloads, GitHub stars,
and so on, but most of them are wildly incorrect.
As a project maintainer who tries to find employment for other maintainers,
I’m incentivized to take the highest number I can find,
but that is somewhat dishonest.
That number today is in the form of this likely false statement.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Dask has 50-100k daily downloads.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This number comes from looking at the Python Package Index (PyPI)
(image from &lt;a class="reference external" href="https://pypistats.org"&gt;pypistats.org&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/dask-pypi-downloads-total.png"&gt;&lt;img src="/images/dask-pypi-downloads-total.png" width="100%" alt="Total Dask downloads"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is a huge number, but is almost certainly misleading.
Common sense tells us that there are not 100k new Dask users every day.&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/01/14/estimating-users.md&lt;/span&gt;, line 31)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;section id="bots-dominate-download-counts"&gt;

&lt;p&gt;If you dive in more deeply to numbers like these you will find that they are
almost entirely due to automated processes. For example, of Dask’s 100k new
users, a surprising number of them seem to be running Linux.&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/linux-reigns.png"&gt;&lt;img src="/images/linux-reigns.png" width="100%" alt="Linux dominates download counts"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;While it’s true that Dask is frequently run on Linux because it is a
distributed library, it would be odd to see every machine in that deployment
individually &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pip&lt;/span&gt; &lt;span class="pre"&gt;install&lt;/span&gt; &lt;span class="pre"&gt;dask&lt;/span&gt;&lt;/code&gt;. It’s more likely that these downloads are the
result of automated systems, rather than individual users.&lt;/p&gt;
&lt;p&gt;Anecdotally, if you get access to fine grained download data, one finds that a
small set of IPs dominate download counts. These tend to come mostly from
continuous integration services like Travis and Circle, are coming from AWS,
or are coming from a few outliers in the world (sometimes people in China try
to mirror everything)..&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/01/14/estimating-users.md&lt;/span&gt;, line 50)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="check-windows"&gt;
&lt;h1&gt;Check Windows&lt;/h1&gt;
&lt;p&gt;So, in an effort to avoid this effect we start looking at just Windows
downloads.&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/dask-windows-downloads.png"&gt;&lt;img src="/images/dask-windows-downloads.png" width="100%" alt="Dask Monthly Windows Downloads"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The magnitudes here seem more honest to me. These monthly numbers translate to
about 1000 downloads a day (perhaps multiplied by two or three for OSX and
Linux), which seems more in line with my expectations.&lt;/p&gt;
&lt;p&gt;However even this is strange. The structure doesn’t match my personal experience.
Why the big change in adoption in 2018?
What is the big spike in 2019?
Anecdotally maintainers did not notice a significant jump in users there.
Instead, we’ve experienced smooth continuous growth of adoption over time
(this is what most long-term software growth looks like).
It’s also odd that there hasn’t been continued growth since 2018. Anecdotally
Dask seems to have grown somewhat constantly over the last few years. Phase
transitions like these don’t match observed reality (at least in so far as I
personally have observed it).&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://nbviewer.jupyter.org/gist/mrocklin/ef6f9b6a649a6d78b2221d8fdeea5f2a"&gt;&lt;em&gt;Notebook for plot available here&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/01/14/estimating-users.md&lt;/span&gt;, line 74)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="documentation-views"&gt;
&lt;h1&gt;Documentation views&lt;/h1&gt;
&lt;p&gt;My favorite metric is looking at weekly unique users to documentation.&lt;/p&gt;
&lt;p&gt;This is an over-estimate of users because many people look at the documentation
without using the project. This is also an under-estimate because many users
don’t consult our documentation on a weekly basis (oh I wish).&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/dask-weekly-users.png"&gt;&lt;img src="/images/dask-weekly-users.png" width="100%" alt="Dask weekly users on documentation"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This growth pattern matches my expectations and my experience with maintaining
a project that has steadily gained traction over several years.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Plot taken from Google Analytics&lt;/em&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/01/14/estimating-users.md&lt;/span&gt;, line 89)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="dependencies"&gt;
&lt;h1&gt;Dependencies&lt;/h1&gt;
&lt;p&gt;It’s also important to look at dependencies of a project. For example many
users in the earth and geo sciences use Dask through another project,
&lt;a class="reference external" href="https://xarray.pydata.org"&gt;Xarray&lt;/a&gt;. These users are much less likely to touch
Dask directly, but often use Dask as infrastructure underneath the Xarray
library. We should probably add in something like half of Xarray’s users as
well.&lt;/p&gt;
&lt;p&gt;&lt;a href="/images/xarray-weekly-users.png"&gt;&lt;img src="/images/xarray-weekly-users.png" width="100%" alt="Xarray weekly users on documentation"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Plot taken from Google Analytics, supplied by &lt;a class="reference external" href="https://joehamman.com/"&gt;Joe Hamman&lt;/a&gt; from Xarray&lt;/em&gt;&lt;/p&gt;
&lt;aside class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/opt/build/repo/2020/01/14/estimating-users.md&lt;/span&gt;, line 102)&lt;/p&gt;
&lt;p&gt;Document headings start at H2, not H1 [myst.header]&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="summary"&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;Dask has somewhere between 100k new users every day (download counts)
or something like 10k users total (weekly unique IPs). The 10k number sounds
more likely to me, maybe bumping up to 15k due to dependencies.
The fact is though that no one really knows.&lt;/p&gt;
&lt;p&gt;Judging the use of community maintained OSS is important as we try to value its
impact on society. This is also a fundamentally difficult problem.
I hope that this post helps to highlight how these numbers may be misleading,
and encourages us all to think more deeply about estimating impact.&lt;/p&gt;
&lt;/section&gt;
</content>
    <link href="https://blog.dask.org/2020/01/14/estimating-users/"/>
    <summary>People often ask me “How many people use Dask?”</summary>
    <published>2020-01-14T00:00:00+00:00</published>
  </entry>
</feed>
