Skip to content

Change default blas_info dictionary in cmodule #444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 9, 2023

Conversation

lucianopaz
Copy link
Member

@lucianopaz lucianopaz commented Sep 18, 2023

Closes #441

Motivation for these changes

numpy.__config__.get_info has been deprecated and removed, so we have to set default link libraries some other way.

Implementation details

This is a very rough first implementation. The idea is to use something like python's sys.path to look for libraries files (this would match a conda env lib folder and also the system level python lib folders, I still need to figure out what to do with venvs). Then look for some default libraries to link against, like mkl or openblas. I added LAPACK, but maybe the c code that is generated by pytensor doesn't have the proper includes to be able to link against that.

I haven't changed any other code yet, but the long term goal would be to remove most of the blas ldflag logic from the startup. In particular, I flagged a couple of platforms for deprecation (EPD and Canopy, which I think are no longer developed anyway)

Checklist

Major / Breaking Changes

  • default_blas_ldflags has changed and might give different default link flags. It no longer looks at numpy for blas information, but rather searches some common system paths to try to find BLAS libraries to link against. The order of priorities is 1) MKL, 2) OPENBLAS, 3) LAPACK, 4) BLAS

New features

  • ...

Bugfixes

  • ...

Documentation

  • ...

Maintenance

  • ...

@lucianopaz
Copy link
Member Author

I've decided to completely change the default_blas_ldflags function. The past implementation was a mess. It tried to import mkl, had a bunch of patches here and there to try to work around old conda issues, support something called Canopy and EPD. I decided to simplify the logic a lot, so this might lead to breaks. The idea is the following:

  1. Determine the search path of the compiler and linker
  2. Use system tools to look at those directories and see if the required BLAS libraries are present or not
  3. There's a list of priority libraries that are searched for. 1) MKL with Intel threading, 2) MKL with GNU threading, 3) openblas, 4) LAPACK, 5) plain and simple BLAS
  4. If they are present, try to compile a simple program that links with BLAS libraries
    1. If the compilation works handle some OMP stuff and finally return the ldflags. At this point I left some old logic about OMP thread handling in place because I didn't want to touch it, but in the future we could refactor it
    2. If the compilation does not work, then move on to the next set libraries to link against
  5. If no combination of libraries works, then return "", which means that pytensor didn't find a blas installation and will use the numpy C-API, which might be slow.

@lucianopaz lucianopaz marked this pull request as ready for review September 22, 2023 09:37
@codecov-commenter
Copy link

codecov-commenter commented Sep 22, 2023

Codecov Report

Merging #444 (f0c1021) into main (36df379) will increase coverage by 0.08%.
Report is 4 commits behind head on main.
The diff coverage is 85.33%.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #444      +/-   ##
==========================================
+ Coverage   80.66%   80.74%   +0.08%     
==========================================
  Files         160      160              
  Lines       46025    46007      -18     
  Branches    11266    11245      -21     
==========================================
+ Hits        37124    37148      +24     
+ Misses       6668     6635      -33     
+ Partials     2233     2224       -9     
Files Coverage Δ
pytensor/tensor/random/op.py 96.25% <ø> (ø)
pytensor/tensor/type.py 94.52% <100.00%> (+0.04%) ⬆️
pytensor/link/c/cmodule.py 55.16% <91.80%> (+3.65%) ⬆️
pytensor/link/c/exceptions.py 41.66% <40.00%> (-38.34%) ⬇️

... and 2 files with indirect coverage changes

@twiecki
Copy link
Member

twiecki commented Sep 23, 2023

Seems like the function just moved to numpy.distutils.system_info.get_info("blas_opt")?

@lucianopaz
Copy link
Member Author

Seems like the function just moved to numpy.distutils.system_info.get_info("blas_opt")?

The distutils module is deprecated and will be removed. Numpy is moving towards using meson as its build toolchain. That’s why I had to go with a different more long term solution for this problem

@ricardoV94 ricardoV94 requested a review from ferrine September 24, 2023 11:44
@twiecki
Copy link
Member

twiecki commented Oct 6, 2023

This is with OSX & accelerate:

>>ipython
Python 3.11.4 | packaged by conda-forge | (main, Jun 10 2023, 18:08:41) [Clang 15.0.7 ]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.14.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import pytensor as pt
WARNING (pytensor.tensor.blas): Using NumPy C-API based implementation for BLAS functions.

In [2]: pt.__file__
Out[2]: '/Users/twiecki/projects/pytensor/pytensor/__init__.py'

In [3]: pt.config
Out[3]: <pytensor.configparser.PyTensorConfigParser at 0x109603610>

In [4]: pt.config.blas__ldflags
Out[4]: ''

@lucianopaz
Copy link
Member Author

This is with OSX & accelerate:

>>ipython
Python 3.11.4 | packaged by conda-forge | (main, Jun 10 2023, 18:08:41) [Clang 15.0.7 ]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.14.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import pytensor as pt
WARNING (pytensor.tensor.blas): Using NumPy C-API based implementation for BLAS functions.

In [2]: pt.__file__
Out[2]: '/Users/twiecki/projects/pytensor/pytensor/__init__.py'

In [3]: pt.config
Out[3]: <pytensor.configparser.PyTensorConfigParser at 0x109603610>

In [4]: pt.config.blas__ldflags
Out[4]: ''

It’s not surprising that the flags fail to link to accelerate. I could add some platform and processor checks to this to actually only try mkl on Intel and to add the -framework flag on macOS

@twiecki
Copy link
Member

twiecki commented Oct 7, 2023

I added a check for conda:

   # Check if in a Conda/Mamba environment
    conda_prefix = os.environ.get('CONDA_PREFIX')
    if conda_prefix:
        # Check for the existence of typical BLAS libraries in the Conda environment
        blas_libs_path = os.path.join(conda_prefix, 'lib')
        if os.path.exists(os.path.join(blas_libs_path, 'libblas.so')):
            return "-lblas"
        elif os.path.exists(os.path.join(blas_libs_path, 'libopenblas.so')):
            return "-lopenblas"
        elif sys.platform == 'darwin' and os.path.exists(os.path.join(blas_libs_path, 'libblas.dylib')):
            return "-lblas"
        # Add checks for other BLAS libraries as necessary

that correctly selects accelerate on my osx.

ricardoV94 referenced this pull request Oct 10, 2023
To discuss: If we don't support 3.12, maybe we should enforce this here in `pyproject.toml`?

Source: <#441 (comment)>

I'm not convinced that this is the right move since it might slightly hinder testing. But if we are explicit, then this seems to me like the correct place for it.
@lucianopaz
Copy link
Member Author

@twiecki, can you try again now? The only difference was that I hadn't included the dylib extension into the default searched libraries.

@twiecki
Copy link
Member

twiecki commented Oct 16, 2023

@lucianopaz That didn't help/fix the issue.

@twiecki
Copy link
Member

twiecki commented Oct 16, 2023

It seems we're running into this issue: klee/klee#591

Changing extra_compile_flags=[f"-Wl,-rpath={rpath}"] if rpath is not None else [], to extra_compile_flags=[f"-Wl,-rpath,{rpath}"] if rpath is not None else [], fixes the issue.

(replacing = with a comma).

@lucianopaz
Copy link
Member Author

(replacing = with a comma).

I didn't know that the clang linker complained about the equal sign but ld didn't. I pushed the edit and the rpath flag should work now

@twiecki
Copy link
Member

twiecki commented Oct 16, 2023

@lucianopaz It now detects blas on OSX with clang. However, it prefers openblas over accelerate (3 over 5). I recommend to add a check specifically for how conda selects blas, as that covers 90% of the installs, and the subsequent check will work for other OS:

   # Check if in a Conda/Mamba environment
    conda_prefix = os.environ.get('CONDA_PREFIX')
    if conda_prefix:
        # Check for the existence of typical BLAS libraries in the Conda environment
        blas_libs_path = os.path.join(conda_prefix, 'lib')
        if os.path.exists(os.path.join(blas_libs_path, 'libblas.so')):
            return "-lblas"
        elif os.path.exists(os.path.join(blas_libs_path, 'libopenblas.so')):
            return "-lopenblas"
        elif sys.platform == 'darwin' and os.path.exists(os.path.join(blas_libs_path, 'libblas.dylib')):
            return "-lblas"
        # Add checks for other BLAS libraries as necessary

@twiecki
Copy link
Member

twiecki commented Oct 16, 2023

Alternatively, I'm not sure what blas (5) implementation is referring to, can we just move that up?

@twiecki
Copy link
Member

twiecki commented Oct 16, 2023

Ok, so I'll put BLAS ahead of openblas, but I'm not sure if that will ever get used as a default

It will on OSX:

>>python -c "import pytensor; print(pytensor.config.blas__ldflags)"
-L/Users/twiecki/micromamba/envs/pymc5/lib -llapack -lblas -lcblas -lm -Wl,-rpath,/Users/twiecki/micromamba/envs/pymc5/lib

@twiecki
Copy link
Member

twiecki commented Oct 16, 2023

Have we tested on windows?

@lucianopaz
Copy link
Member Author

Ok, so I'll put BLAS ahead of openblas, but I'm not sure if that will ever get used as a default

It will on OSX:

>>python -c "import pytensor; print(pytensor.config.blas__ldflags)"
-L/Users/twiecki/micromamba/envs/pymc5/lib -llapack -lblas -lcblas -lm -Wl,-rpath,/Users/twiecki/micromamba/envs/pymc5/lib

Sorry, I meant to say that I don't think that openblas will ever end up being used as a default.

@twiecki
Copy link
Member

twiecki commented Oct 16, 2023

Sorry, I meant to say that I don't think that openblas will ever end up being used as a default.

Yes, that's what we want and what I was saying here:

You can see here: https://github.com/conda-forge/pytensor-suite-feedstock/pull/71/files that it's really only for non-standard OS. For Intel chips we use MKL for ARM64 we use accelerate. That covers 99%.

@lucianopaz
Copy link
Member Author

Have we tested on windows?

I’ll test it

@ricardoV94
Copy link
Member

ricardoV94 commented Oct 16, 2023

Do you need me to test nothing is changed for linux amd64 users (like me)?

@twiecki
Copy link
Member

twiecki commented Oct 16, 2023

Do you need me to test nothing is changed for linux amd64 users (like me)?

Sure, the more testing the better. This one could cause quite some disruption if we get it wrong.

@ricardoV94
Copy link
Member

This is what I got before:

-L/home/ricardo/miniconda3/envs/pytensor/lib -lcblas -lblas -lcblas -lblas -Wl,-rpath,/home/ricardo/miniconda3/envs/pytensor/lib

And what I get after:

-L/usr/lib/x86_64-linux-gnu -L/home/ricardo/miniconda3/envs/pytensor/lib -L/usr/lib/gcc/x86_64-linux-gnu/9 -llapack -lblas -lcblas -lm -Wl,-rpath,/home/ricardo/miniconda3/envs/pytensor/lib

@twiecki
Copy link
Member

twiecki commented Oct 27, 2023 via email

@twiecki
Copy link
Member

twiecki commented Oct 27, 2023

Actually, I don't think -L/usr/lib/x86_64-linux-gnu should show up there as it might override the env settings.

@lucianopaz
Copy link
Member Author

Actually, I don't think -L/usr/lib/x86_64-linux-gnu should show up there as it might override the env settings.

That directory came from the compiler default link directories. That means that it is consistent with the env settings and the compiler configuration. Maybe the safest thing is to not include -L flags to folders that are already part of the compiler or linker’s search path, because they might affect the lookup order when linking.

@twiecki
Copy link
Member

twiecki commented Oct 27, 2023

I guess conda doesn't provide all libraries and some linking still happens against OS libs?

@twiecki
Copy link
Member

twiecki commented Nov 8, 2023

@lucianopaz Any thoughts on this? Should we just merge? CC @ricardoV94

@lucianopaz
Copy link
Member Author

@lucianopaz Any thoughts on this? Should we just merge? CC @ricardoV94

I'll remove the compiler libs from the -L flags and then I think we can merge

@lucianopaz
Copy link
Member Author

@ricardoV94, do you want to try this out on your AMD setup and see if the link flags work for you there?

@ricardoV94
Copy link
Member

ricardoV94 commented Nov 9, 2023

@ricardoV94, do you want to try this out on your AMD setup and see if the link flags work for you there?

This is what I get now

-L/home/ricardo/miniconda3/envs/pytensor/lib -llapack -lblas -lcblas -lm -Wl,-rpath,/home/ricardo/miniconda3/envs/pytensor/lib

vs main:

-L/home/ricardo/miniconda3/envs/pytensor/lib -lcblas -lblas -lcblas -lblas -Wl,-rpath,/home/ricardo/miniconda3/envs/pytensor/lib

@twiecki
Copy link
Member

twiecki commented Nov 9, 2023

@ricardoV94 that looks perfect to me

@lucianopaz
Copy link
Member Author

@ricardoV94, LGTM

@twiecki twiecki merged commit 893dc18 into pymc-devs:main Nov 9, 2023
@twiecki
Copy link
Member

twiecki commented Nov 9, 2023

Congrats on slaying this dragon @lucianopaz!

Time for a new release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BUG: using numpy code that has been deprecated in v1.26.0
4 participants