<h3 dir="auto">Describe your issue.</h3>
<p dir="auto">Scipy requires numpy.</p>
<p dir="auto">But both include (at least in their Windows wheels) a form of libopenblas.</p>
<p dir="auto">So when you want to redistribute an app that is using scipy you are forced to redistribute 2 copies of libopenblas  (each 35 MB) that both get loaded into memory.</p>
<p dir="auto">A simple solution would be to publish libopenblas as an own library on Pypi  (and make scipy and numpy depending on it).<br>
An alternative solution would be for scipy to use the numpy installed libopenblas.</p>
<h3 dir="auto">Reproducing Code Example</h3>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="pip install scipy"><pre class="notranslate"><span class="pl-s1">pip</span> <span class="pl-s1">install</span> <span class="pl-s1">scipy</span></pre></div>
<h3 dir="auto">Error message</h3>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="No error message"><pre class="notranslate">No error message</pre></div>
<h3 dir="auto">SciPy/NumPy/Python version information</h3>
<p dir="auto">1.91/1.23.4/3.10.8</p>
<p dir="auto">This is a follow up to <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1055729026" data-permission-text="Title is private" data-url="https://github.com/scipy/scipy/issues/15050" data-hovercard-type="issue" data-hovercard-url="/scipy/scipy/issues/15050/hovercard" href="https://github.com/scipy/scipy/issues/15050">gh-15050</a>, which describes a performance issue on arm64 macOS due to having two OpenBLAS libraries present. In the <code class="notranslate">arm64</code> wheels for SciPy <code class="notranslate">1.7.3</code>, that was fixed by setting a single environment variable (<code class="notranslate">OPENBLAS_THREAD_TIMEOUT</code>) in <code class="notranslate">_distributor_init.py</code>.</p>
<p dir="auto">That may not be the optimal long-term fix, and scikit-learn has seen a similar issue on Linux when two OpenMP runtimes are present. So this issue is for tracking another potential solution. Starting from <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1055729026" data-permission-text="Title is private" data-url="https://github.com/scipy/scipy/issues/15050" data-hovercard-type="issue" data-hovercard-url="/scipy/scipy/issues/15050/hovercard?comment_id=975318631&amp;comment_type=issue_comment" href="https://github.com/scipy/scipy/issues/15050#issuecomment-975318631">#15050 (comment)</a>, comment by <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/ogrisel/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/ogrisel">@ogrisel</a>, copied here:</p>
<blockquote>
<p dir="auto"><em>Opened <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1059328220" data-permission-text="Title is private" data-url="https://github.com/MacPython/scipy-wheels/issues/143" data-hovercard-type="pull_request" data-hovercard-url="/MacPython/scipy-wheels/pull/143/hovercard" href="https://github.com/MacPython/scipy-wheels/pull/143">MacPython/scipy-wheels#143</a> for the environment variable solution. I'd like to go with that for 1.7.3. To be discussed I'd say if we keep that or if we want to rebuild OpenBLAS for 1.8.0.</em></p>
</blockquote>
<p dir="auto"><em>Indeed for a minor release, a minimal fix only for the macos m1 problem is best.</em></p>
<p dir="auto"><em>For the more medium term plan, maybe we could do this series of change more progressively. Here are some suggestions:</em></p>
<ul dir="auto">
<li><em>Build OpenBLAS with <code class="notranslate">THREAD_TIMEOUT=1</code> for the wheels will probably help for the short to medium term on making it possible to workaround problems such as <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="957239769" data-permission-text="Title is private" data-url="https://github.com/scikit-learn/scikit-learn/issues/20642" data-hovercard-type="issue" data-hovercard-url="/scikit-learn/scikit-learn/issues/20642/hovercard" href="https://github.com/scikit-learn/scikit-learn/issues/20642">scikit-learn/scikit-learn#20642</a> when there are several threading runtimes linked to the same Python process.</em></li>
</ul>
<p dir="auto"><em>For the even longer term we could try plan changes to completely avoid redundancy of OpenBLAS and OpenMP wheels in the scipy stack. First for OpenBLAS itself:</em></p>
<ul dir="auto">
<li><em>Expose the Cython BLAS API in <code class="notranslate">numpy.linalg</code> the same way <a href="https://docs.scipy.org/doc/scipy/reference/linalg.cython_blas.html" rel="nofollow">scipy.linalg does it</a>;</em></li>
<li><em>Make scipy use the BLAS implementation of numpy and stop shipping it (while continuing exposing it via the Cython BLAS API replicated in scipy for backward compat reasons). This means that pypi will stop shipping two copies of OpenBLAS for the scipy stack which should help with maintenance in the long run (and reduce a bit the scipy wheel sizes which is always nice).</em></li>
<li><em>This way numpy would be the official BLAS implementation provider for the full scipy stack.</em></li>
</ul>
<p dir="auto"><em>About the switch from native OpenBLAS threads to OpenMP, this would be nice for scikit-learn but <a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60035" rel="nofollow">libgomp is still not fork safe</a>. conda-forge typically replaces libgomp by llvm-openmp by default for this reason. Note that on linux, gcc is still the recommended compiler for conda-forge, which means that the library is built against libgomp but then the openmp runtime is replaced afterwards.</em></p>
<p dir="auto"><em>Also note, for some reason conda-forge decided to use OpenBLAS native threads by default on Linux while it's also possible to make it use OpenMP. See the <code class="notranslate">2020-07-17</code> entry of the <a href="https://conda-forge.org/docs/user/announcements.html" rel="nofollow">conda-forge announcements</a>. Maybe <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/isuruf/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/isuruf">@isuruf</a> can explain why not use LLVM OpenMP (edit: added missing LLVM) by default on Linux?</em></p>
<p dir="auto"><em>More details on OpenMP in conda-forge in <a href="https://conda-forge.org/docs/maintainer/knowledge_base.html#openmp" rel="nofollow">https://conda-forge.org/docs/maintainer/knowledge_base.html#openmp</a></em></p>
<p dir="auto"><em>Maybe the topic of using OpenMP in the scipy stack deserves a <a href="https://discuss.scientific-python.org/c/specs" rel="nofollow">Scientific Python SPEC</a>.</em></p>
<p dir="auto">My main reply:</p>
<blockquote>
<p dir="auto"><em>This way numpy would be the official BLAS implementation provider for the full scipy stack.</em></p>
</blockquote>
<p dir="auto"><em>This sounds less than ideal to me - we'd have to duplicate the Cython APIs, plus NumPy has far fewer BLAS/LAPACK needs than SciPy and is still on an older minimum required version than SciPy (that's why for example Accelerate is re-enabled in NumPy but cannot be in SciPy).</em></p>
<p dir="auto"><em>My first reaction is that shipping a standalone wheel would be preferable to this solution.</em></p>
<blockquote>
<p dir="auto"><em>Maybe the topic of using OpenMP in the scipy stack deserves a Scientific Python SPEC.</em></p>
</blockquote>
<p dir="auto"><em>Yes, I think it does. And it's broader than just packaging, there's an important UX question there as well that deserves thinking about - are we continuing to default to single-threaded everywhere, except for BLAS/LAPACK calls?</em></p>
<p dir="auto">And <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/ogrisel/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/ogrisel">@ogrisel</a>'s reply:</p>
<blockquote>
<p dir="auto"><em>My first reaction is that shipping a standalone wheel would be preferable to this solution.</em></p>
</blockquote>
<p dir="auto"><em>Fine with me as well.</em></p>
<blockquote>
<p dir="auto"><em>are we continuing to default to single-threaded everywhere, except for BLAS/LAPACK calls?</em></p>
</blockquote>
<p dir="auto"><em>I cannot say for scipy but for scikit-learn we have already started to use OpenMP multithreaded Cython code by default for some components and are progressively generalizing this approach in the code base.</em></p>
<p dir="auto">xref <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="450799658" data-permission-text="Title is private" data-url="https://github.com/scipy/scipy/issues/10239" data-hovercard-type="issue" data-hovercard-url="/scipy/scipy/issues/10239/hovercard" href="https://github.com/scipy/scipy/issues/10239">gh-10239</a> for the issues around OpenMP, and why we don't use it in SciPy.</p>
<p dir="auto">For the main topic of this issue: shipping separate OpenBLAS and OpenMP wheels isn't great either, and not something I'd like us to be responsible for. But it's probably either that, or stay with the status quo - which perhaps isn't very healthy if scikit-learn continues to use more OpenMP in more of its API.</p>
<p dir="auto">The issue is specific to PyPI/wheels, because any other binary install mechanism tends to have a proper package manager which can guarantee only a single OpenBLAS/OpenMP library is used. Shipping native dependencies like these as separate wheels is in general an unhealthy idea for multiple reasons, but if it's just limited to OpenBLAS and OpenMP and we can scope it to just the NumPy/SciPy stack for those libraries, then it may be okay to do.</p>