mirror of
https://github.com/bspeice/speice.io
synced 2024-11-14 22:18:10 -05:00
151 lines
95 KiB
HTML
151 lines
95 KiB
HTML
|
<!doctype html><html lang=en dir=ltr class="blog-wrapper blog-post-page plugin-blog plugin-id-default" data-has-hydrated=false><meta charset=UTF-8><meta name=generator content="Docusaurus v3.6.0"><title data-rh=true>Release the GIL | The Old Speice Guy</title><meta data-rh=true name=viewport content="width=device-width,initial-scale=1.0"><meta data-rh=true name=twitter:card content=summary_large_image><meta data-rh=true property=og:url content=https://speice.io/2019/12/release-the-gil><meta data-rh=true property=og:locale content=en><meta data-rh=true name=docusaurus_locale content=en><meta data-rh=true name=docusaurus_tag content=default><meta data-rh=true name=docsearch:language content=en><meta data-rh=true name=docsearch:docusaurus_tag content=default><meta data-rh=true property=og:title content="Release the GIL | The Old Speice Guy"><meta data-rh=true name=description content="Complaining about the Global Interpreter Lock"><meta data-rh=true property=og:description content="Complaining about the Global Interpreter Lock"><meta data-rh=true property=og:type content=article><meta data-rh=true property=article:published_time content=2019-12-14T12:00:00.000Z><link data-rh=true rel=icon href=/img/favicon.ico><link data-rh=true rel=canonical href=https://speice.io/2019/12/release-the-gil><link data-rh=true rel=alternate href=https://speice.io/2019/12/release-the-gil hreflang=en><link data-rh=true rel=alternate href=https://speice.io/2019/12/release-the-gil hreflang=x-default><script data-rh=true type=application/ld+json>{"@context":"https://schema.org","@id":"https://speice.io/2019/12/release-the-gil","@type":"BlogPosting","author":{"@type":"Person","name":"Bradlee Speice"},"dateModified":"2024-11-10T03:06:23.000Z","datePublished":"2019-12-14T12:00:00.000Z","description":"Complaining about the Global Interpreter Lock","headline":"Release the GIL","isPartOf":{"@id":"https://speice.io/","@type":"Blog","name":"Blog"},"keywords":[],"mainEntityOfPage":"https://speice.io/2019/12/release-the-gil","name":"Release the GIL","url":"https://speice.io/2019/12/release-the-gil"}</script><link rel=alternate type=application/rss+xml href=/rss.xml title="The Old Speice Guy RSS Feed"><link rel=alternate type=application/atom+xml href=/atom.xml title="The Old Speice Guy Atom Feed"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css integrity=sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM crossorigin><link rel=stylesheet href=/assets/css/styles.ae6ff4a3.css><script src=/assets/js/runtime~main.751b419d.js defer></script><script src=/assets/js/main.62ce6156.js defer></script><body class=navigation-with-keyboard><script>!function(){var t,e=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();t=null!==e?e:"light",document.documentElement.setAttribute("data-theme",t)}(),function(){try{for(var[t,e]of new URLSearchParams(window.location.search).entries())if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id=__docusaurus><div role=region aria-label="Skip to main content"><a class=skipToContent_fXgn href=#__docusaurus_skipToContent_fallback>Skip to main content</a></div><nav aria-label=Main class="navbar navbar--fixed-top"><div class=navbar__inner><div class=navbar__items><button aria-label="Toggle navigation bar" aria-expanded=false class="navbar__toggle clean-btn" type=button><svg width=30 height=30 viewBox="0 0 30 30" aria-hidden=true><path stroke=currentColor stroke-linecap=round stroke-miterlimit=10 stroke-width=2 d="M4 7h22M4 15h22M4 23h22"/></svg></button><a class=navbar__brand href=/><div class=navbar__logo><img src=/img/logo.svg alt="Sierpinski Gasket" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src=/img/logo-dark.svg alt="Sierpinski Gasket" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><b class="navbar__title text--truncate">T
|
|||
|
(GIL) seems like a rite of passage for Python developers. It's easy to criticize a design decision
|
|||
|
made before multi-core CPU's were widely available, but the fact that it's still around indicates
|
|||
|
that it generally works <a href=https://wiki.c2.com/?PrematureOptimization target=_blank rel="noopener noreferrer">Good</a>
|
|||
|
<a href=https://wiki.c2.com/?YouArentGonnaNeedIt target=_blank rel="noopener noreferrer">Enough</a>. Besides, there are simple and effective
|
|||
|
workarounds; it's not hard to start a
|
|||
|
<a href=https://docs.python.org/3/library/multiprocessing.html target=_blank rel="noopener noreferrer">new process</a> and use message passing to
|
|||
|
synchronize code running in parallel.</p>
|
|||
|
<p>Still, wouldn't it be nice to have more than a single active interpreter thread? In an age of
|
|||
|
asynchronicity and <em>M:N</em> threading, Python seems lacking. The ideal scenario is to take advantage of
|
|||
|
both Python's productivity and the modern CPU's parallel capabilities.</p>
|
|||
|
<p>Presented below are two strategies for releasing the GIL's icy grip without giving up on what makes
|
|||
|
Python a nice language to start with. Bear in mind: these are just the tools, no claim is made about
|
|||
|
whether it's a good idea to use them. Very often, unlocking the GIL is an
|
|||
|
<a href=https://en.wikipedia.org/wiki/XY_problem target=_blank rel="noopener noreferrer">XY problem</a>; you want application performance, and the
|
|||
|
GIL seems like an obvious bottleneck. Remember that any gains from running code in parallel come at
|
|||
|
the expense of project complexity; messing with the GIL is ultimately messing with Python's memory
|
|||
|
model.</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">load_ext Cython</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token keyword" style="color:hsl(301, 63%, 40%)">from</span><span class="token plain"> numba </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">import</span><span class="token plain"> jit</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">N </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">1_000_000_000</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
|||
|
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=cython>Cython<a href=#cython class=hash-link aria-label="Direct link to Cython" title="Direct link to Cython"></a></h2>
|
|||
|
<p>Put simply, <a href=https://cython.org/ target=_blank rel="noopener noreferrer">Cython</a> is a programming language that looks a lot like Python,
|
|||
|
gets <a href=https://en.wikipedia.org/wiki/Source-to-source_compiler target=_blank rel="noopener noreferrer">transpiled</a> to C/C++, and integrates
|
|||
|
well with the <a href=https://en.wikipedia.org/wiki/CPython target=_blank rel="noopener noreferrer">CPython</a> API. It's great for building Python
|
|||
|
wrappers to C and C++ libraries, writing optimized code for numerical processing, and tons more. And
|
|||
|
when it comes to managing the GIL, there are two special features:</p>
|
|||
|
<ul>
|
|||
|
<li>The <code>nogil</code>
|
|||
|
<a href=https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#declaring-a-function-as-callable-without-the-gil target=_blank rel="noopener noreferrer">function annotation</a>
|
|||
|
asserts that a Cython function is safe to use without the GIL, and compilation will fail if it
|
|||
|
interacts with Python in an unsafe manner</li>
|
|||
|
<li>The <code>with nogil</code>
|
|||
|
<a href=https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#releasing-the-gil target=_blank rel="noopener noreferrer">context manager</a>
|
|||
|
explicitly unlocks the CPython GIL while active</li>
|
|||
|
</ul>
|
|||
|
<p>Whenever Cython code runs inside a <code>with nogil</code> block on a separate thread, the Python interpreter
|
|||
|
is unblocked and allowed to continue work elsewhere. We'll define a "busy work" function that
|
|||
|
demonstrates this principle in action:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">cython</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token comment" style="color:hsl(230, 4%, 64%)"># Annotating a function with `nogil` indicates only that it is safe</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token comment" style="color:hsl(230, 4%, 64%)"># to call in a `with nogil` block. It *does not* release the GIL.</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">cdef unsigned </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">long</span><span class="token plain"> fibonacci</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">unsigned </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">long</span><span class="token plain"> n</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> nogil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">if</span><span class="token plain"> n </span><span class="token operator" style="color:hsl(221, 87%, 60%)"><=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">return</span><span class="token plain"> n</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> cdef unsigned </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">long</span><span class="token plain"> a </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">0</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> b </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> c </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">0</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> c </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> a </span><span class="
|
|||
|
<p>First, let's time how long it takes Cython to calculate the billionth Fibonacci number:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">time</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">_ </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> cython_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
|||
|
<blockquote>
|
|||
|
<pre><p>CPU times: user 365 ms, sys: 0 ns, total: 365 ms
|
|||
|
Wall time: 372 ms</pre>
|
|||
|
</blockquote>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">time</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">_ </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> cython_nogil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
|||
|
<blockquote>
|
|||
|
<pre><p>CPU times: user 381 ms, sys: 0 ns, total: 381 ms
|
|||
|
Wall time: 388 ms</pre>
|
|||
|
</blockquote>
|
|||
|
<p>Both versions (with and without GIL) take effectively the same amount of time to run. Even when
|
|||
|
running this calculation in parallel on separate threads, it is expected that the run time will
|
|||
|
double because only one thread can be active at a time:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">time</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token keyword" style="color:hsl(301, 63%, 40%)">from</span><span class="token plain"> threading </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">import</span><span class="token plain"> Thread</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token comment" style="color:hsl(230, 4%, 64%)"># Create the two threads to run on</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">cython_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t2 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">cython_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token comment" style="color:hsl(230, 4%, 64%)"># Start the threads</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=tok
|
|||
|
<blockquote>
|
|||
|
<pre><p>CPU times: user 641 ms, sys: 5.62 ms, total: 647 ms
|
|||
|
Wall time: 645 ms</pre>
|
|||
|
</blockquote>
|
|||
|
<p>However, if the first thread releases the GIL, the second thread is free to acquire it and run in
|
|||
|
parallel:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">time</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">cython_nogil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t2 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">cython_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">join</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">join</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label
|
|||
|
<blockquote>
|
|||
|
<pre><p>CPU times: user 717 ms, sys: 372 µs, total: 718 ms
|
|||
|
Wall time: 358 ms</pre>
|
|||
|
</blockquote>
|
|||
|
<p>Because <code>user</code> time represents the sum of processing time on all threads, it doesn't change much.
|
|||
|
The <a href=https://en.wikipedia.org/wiki/Elapsed_real_time target=_blank rel="noopener noreferrer">"wall time"</a> has been cut roughly in half
|
|||
|
because each function is running simultaneously.</p>
|
|||
|
<p>Keep in mind that the <strong>order in which threads are started</strong> makes a difference!</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">time</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token comment" style="color:hsl(230, 4%, 64%)"># Note that the GIL-locked version is started first</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">cython_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t2 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">cython_nogil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">join</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="to
|
|||
|
<blockquote>
|
|||
|
<pre><p>CPU times: user 667 ms, sys: 0 ns, total: 667 ms
|
|||
|
Wall time: 672 ms</pre>
|
|||
|
</blockquote>
|
|||
|
<p>Even though the second thread releases the GIL while running, it can't start until the first has
|
|||
|
completed. Thus, the overall runtime is effectively the same as running two GIL-locked threads.</p>
|
|||
|
<p>Finally, be aware that attempting to unlock the GIL from a thread that doesn't own it will crash the
|
|||
|
<strong>interpreter</strong>, not just the thread attempting the unlock:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">cython</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">cdef </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">int</span><span class="token plain"> cython_recurse</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token builtin" style="color:hsl(119, 34%, 47%)">int</span><span class="token plain"> n</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> nogil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">if</span><span class="token plain"> n </span><span class="token operator" style="color:hsl(221, 87%, 60%)"><=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">0</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">return</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">0</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">with</span><span class="token plain"> nogil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">return</span><span class="token plain"> cython_recurse</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">n </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">cython_recurse</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token number" style="color:hsl(35, 99%, 36%)">2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyBut
|
|||
|
<blockquote>
|
|||
|
<pre><p>Fatal Python error: PyEval_SaveThread: NULL tstate<p>Thread 0x00007f499effd700 (most recent call first):
|
|||
|
File "/home/bspeice/.virtualenvs/release-the-gil/lib/python3.7/site-packages/ipykernel/parentpoller.py", line 39 in run
|
|||
|
File "/usr/lib/python3.7/threading.py", line 926 in _bootstrap_inner
|
|||
|
File "/usr/lib/python3.7/threading.py", line 890 in _bootstrap</pre>
|
|||
|
</blockquote>
|
|||
|
<p>In practice, avoiding this issue is simple. First, <code>nogil</code> functions probably shouldn't contain
|
|||
|
<code>with nogil</code> blocks. Second, Cython can
|
|||
|
<a href=https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#conditional-acquiring-releasing-the-gil target=_blank rel="noopener noreferrer">conditionally acquire/release</a>
|
|||
|
the GIL, so these conditions can be used to synchronize access. Finally, Cython's documentation for
|
|||
|
<a href=https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#acquiring-and-releasing-the-gil target=_blank rel="noopener noreferrer">external C code</a>
|
|||
|
contains more detail on how to safely manage the GIL.</p>
|
|||
|
<p>To conclude: use Cython's <code>nogil</code> annotation to assert that functions are safe for calling when the
|
|||
|
GIL is unlocked, and <code>with nogil</code> to actually unlock the GIL and run those functions.</p>
|
|||
|
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=numba>Numba<a href=#numba class=hash-link aria-label="Direct link to Numba" title="Direct link to Numba"></a></h2>
|
|||
|
<p>Like Cython, <a href=https://numba.pydata.org/ target=_blank rel="noopener noreferrer">Numba</a> is a "compiled Python." Where Cython works by
|
|||
|
compiling a Python-like language to C/C++, Numba compiles Python bytecode <em>directly to machine code</em>
|
|||
|
at runtime. Behavior is controlled with a special <code>@jit</code> decorator; calling a decorated function
|
|||
|
first compiles it to machine code before running. Calling the function a second time re-uses that
|
|||
|
machine code unless the argument types have changed.</p>
|
|||
|
<p>Numba works best when a <code>nopython=True</code> argument is added to the <code>@jit</code> decorator; functions
|
|||
|
compiled in <a href=http://numba.pydata.org/numba-doc/latest/user/jit.html?#nopython target=_blank rel="noopener noreferrer"><code>nopython</code></a> mode
|
|||
|
avoid the CPython API and have performance comparable to C. Further, adding <code>nogil=True</code> to the
|
|||
|
<code>@jit</code> decorator unlocks the GIL while that function is running. Note that <code>nogil</code> and <code>nopython</code>
|
|||
|
are separate arguments; while it is necessary for code to be compiled in <code>nopython</code> mode in order to
|
|||
|
release the lock, the GIL will remain locked if <code>nogil=False</code> (the default).</p>
|
|||
|
<p>Let's repeat the same experiment, this time using Numba instead of Cython:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token comment" style="color:hsl(230, 4%, 64%)"># The `int` type annotation is only for humans and is ignored</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token comment" style="color:hsl(230, 4%, 64%)"># by Numba.</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:hsl(119, 34%, 47%)">@jit</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">nopython</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token boolean" style="color:hsl(35, 99%, 36%)">True</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> nogil</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token boolean" style="color:hsl(35, 99%, 36%)">True</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token keyword" style="color:hsl(301, 63%, 40%)">def</span><span class="token plain"> </span><span class="token function" style="color:hsl(221, 87%, 60%)">numba_nogil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">n</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">int</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token operator" style="color:hsl(221, 87%, 60%)">></span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">int</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">if</span><span class="token plain"> n </span><span class="token operator" style="color:hsl(221, 87%, 60%)"><=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">return</span><span class="token plain"> n</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> a </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">0</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> b </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">1</span><span class="token plain"></span><br></span><span
|
|||
|
<p>We'll perform the same tests as above; first, figure out how long it takes the function to run:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">time</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">_ </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> numba_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" class=copyButtonIcon_y97N><path fill=currentColor d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/></svg><svg viewBox="0 0 24 24" class=copyButtonSuccessIcon_LjdS><path fill=currentColor d=M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z /></svg></span></button></div></div></div>
|
|||
|
<blockquote>
|
|||
|
<pre><p>CPU times: user 253 ms, sys: 258 µs, total: 253 ms
|
|||
|
Wall time: 251 ms</pre>
|
|||
|
</blockquote>
|
|||
|
<small><p>Aside: it's not immediately clear why Numba takes ~20% less time to run than Cython for code that should be
|
|||
|
effectively identical after compilation.</small>
|
|||
|
<p>When running two GIL-locked threads, the result (as expected) takes around twice as long to compute:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">time</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">numba_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t2 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">numba_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">join</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">join</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24" cl
|
|||
|
<blockquote>
|
|||
|
<pre><p>CPU times: user 541 ms, sys: 3.96 ms, total: 545 ms
|
|||
|
Wall time: 541 ms</pre>
|
|||
|
</blockquote>
|
|||
|
<p>But if the GIL-unlocking thread starts first, both threads run in parallel:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">time</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">numba_nogil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t2 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">numba_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">join</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">join</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24"
|
|||
|
<blockquote>
|
|||
|
<pre><p>CPU times: user 551 ms, sys: 7.77 ms, total: 559 ms
|
|||
|
Wall time: 279 ms</pre>
|
|||
|
</blockquote>
|
|||
|
<p>Just like Cython, starting the GIL-locked thread first leads to poor performance:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token operator" style="color:hsl(221, 87%, 60%)">%</span><span class="token plain">time</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">numba_gil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t2 </span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain"> Thread</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">target</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token plain">numba_nogil</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> args</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">[</span><span class="token plain">N</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">]</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain">t1</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">join</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">;</span><span class="token plain"> t2</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">.</span><span class="token plain">join</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><br></span></code></pre><div class=buttonGroup__atx><button type=button aria-label="Copy code to clipboard" title=Copy class=clean-btn><span class=copyButtonIcons_eSgA aria-hidden=true><svg viewBox="0 0 24 24"
|
|||
|
<blockquote>
|
|||
|
<pre><p>CPU times: user 524 ms, sys: 0 ns, total: 524 ms
|
|||
|
Wall time: 522 ms</pre>
|
|||
|
</blockquote>
|
|||
|
<p>Finally, unlike Cython, Numba will unlock the GIL if and only if it is currently acquired;
|
|||
|
recursively calling <code>@jit(nogil=True)</code> functions is perfectly safe:</p>
|
|||
|
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(230, 1%, 98%);--prism-color:hsl(230, 8%, 24%)"><div class=codeBlockContent_biex><pre tabindex=0 class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="background-color:hsl(230, 1%, 98%);color:hsl(230, 8%, 24%)"><code class=codeBlockLines_e6Vv><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token keyword" style="color:hsl(301, 63%, 40%)">from</span><span class="token plain"> numba </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">import</span><span class="token plain"> jit</span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:hsl(119, 34%, 47%)">@jit</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">nopython</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token boolean" style="color:hsl(35, 99%, 36%)">True</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">,</span><span class="token plain"> nogil</span><span class="token operator" style="color:hsl(221, 87%, 60%)">=</span><span class="token boolean" style="color:hsl(35, 99%, 36%)">True</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"></span><span class="token keyword" style="color:hsl(301, 63%, 40%)">def</span><span class="token plain"> </span><span class="token function" style="color:hsl(221, 87%, 60%)">numba_recurse</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">n</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">int</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token operator" style="color:hsl(221, 87%, 60%)">></span><span class="token plain"> </span><span class="token builtin" style="color:hsl(119, 34%, 47%)">int</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">if</span><span class="token plain"> n </span><span class="token operator" style="color:hsl(221, 87%, 60%)"><=</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">0</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">:</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">return</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">0</span><span class="token plain"></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain" style=display:inline-block></span><br></span><span class=token-line style="color:hsl(230, 8%, 24%)"><span class="token plain"> </span><span class="token keyword" style="color:hsl(301, 63%, 40%)">return</span><span class="token plain"> numba_recurse</span><span class="token punctuation" style="color:hsl(119, 34%, 47%)">(</span><span class="token plain">n </span><span class="token operator" style="color:hsl(221, 87%, 60%)">-</span><span class="token plain"> </span><span class="token number" style="color:hsl(35, 99%, 36%)">1</span><span class="token punctuation" style="color:hsl(119
|
|||
|
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=conclusion>Conclusion<a href=#conclusion class=hash-link aria-label="Direct link to Conclusion" title="Direct link to Conclusion"></a></h2>
|
|||
|
<p>Before finishing, it's important to address pain points that will show up if these techniques are
|
|||
|
used in a more realistic project:</p>
|
|||
|
<p>First, code running in a GIL-free context will likely also need non-trivial data structures;
|
|||
|
GIL-free functions aren't useful if they're constantly interacting with Python objects whose access
|
|||
|
requires the GIL. Cython provides
|
|||
|
<a href=http://docs.cython.org/en/latest/src/tutorial/cdef_classes.html target=_blank rel="noopener noreferrer">extension types</a> and Numba
|
|||
|
provides a <a href=https://numba.pydata.org/numba-doc/dev/user/jitclass.html target=_blank rel="noopener noreferrer"><code>@jitclass</code></a> decorator to
|
|||
|
address this need.</p>
|
|||
|
<p>Second, building and distributing applications that make use of Cython/Numba can be complicated.
|
|||
|
Cython packages require running the compiler, (potentially) linking/packaging external dependencies,
|
|||
|
and distributing a binary wheel. Numba is generally simpler because the code being distributed is
|
|||
|
pure Python, but can be tricky since errors aren't detected until runtime.</p>
|
|||
|
<p>Finally, while unlocking the GIL is often a solution in search of a problem, both Cython and Numba
|
|||
|
provide tools to directly manage the GIL when appropriate. This enables true parallelism (not just
|
|||
|
<a href=https://stackoverflow.com/a/1050257 target=_blank rel="noopener noreferrer">concurrency</a>) that is impossible in vanilla Python.</div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Blog post page navigation"><a class="pagination-nav__link pagination-nav__link--prev" href=/2019/09/binary-format-shootout><div class=pagination-nav__sublabel>Older post</div><div class=pagination-nav__label>Binary format shootout</div></a><a class="pagination-nav__link pagination-nav__link--next" href=/2011/11/webpack-industrial-complex><div class=pagination-nav__sublabel>Newer post</div><div class=pagination-nav__label>The webpack industrial complex</div></a></nav></main><div class="col col--2"><div class="tableOfContents_bqdL thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href=#cython class="table-of-contents__link toc-highlight">Cython</a><li><a href=#numba class="table-of-contents__link toc-highlight">Numba</a><li><a href=#conclusion class="table-of-contents__link toc-highlight">Conclusion</a></ul></div></div></div></div></div><footer class=footer><div class="container container-fluid"><div class="footer__bottom text--center"><div class=footer__copyright>Copyright © 2024 Bradlee Speice</div></div></div></footer></div>
|