8000 BLD, ENH: Enable Accelerate Framework by Matthew-Badin · Pull Request #18874 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

BLD, ENH: Enable Accelerate Framework #18874

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 4 commits into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
BLD: Enable Accelerate Framework
  • Loading branch information
Matthew-Badin committed Apr 29, 2021
commit e28da7a3d50862fa99b8b704d60fc6543b5af631
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ include numpy/*.pxd
# Note that sub-directories that don't have __init__ are apparently not
# included by 'recursive-include', so list those separately
recursive-include numpy *
recursive-include numpy/_build_utils *
recursive-include numpy/linalg/lapack_lite *
recursive-include tools *
# Add sdist files whose use depends on local configuration.
Expand Down
9 changes: 9 additions & 0 deletions numpy/_build_utils/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
=======
WARNING
=======

This directory (numpy/_build_utils) is *not* part of the public numpy API,
- it is internal build support for numpy.
- it is only present in source distributions or during an in place build
- it is *not* installed with the rest of numpy

Empty file added numpy/_build_utils/__init__.py
Empty file.
21 changes: 21 additions & 0 deletions numpy/_build_utils/apple_accelerate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os
import sys
import re

__all__ = ['uses_accelerate_framework']

def uses_accelerate_framework(info):
""" Returns True if Accelerate framework is used for BLAS/LAPACK """
# If we're not building on Darwin (macOS), don't use Accelerate
if sys.platform != "darwin":
return False
# If we're building on macOS, but targeting a different platform,
# don't use Accelerate.
if os.getenv('_PYTHON_HOST_PLATFORM', None):
return False
r_accelerate = re.compile("Accelerate")
extra_link_args = info.get('extra_link_args', '')
for arg in extra_link_args:
if r_accelerate.search(arg):
return True
return False
9 changes: 4 additions & 5 deletions numpy/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
from numpy.distutils import log
from distutils.dep_util import newer
from sysconfig import get_config_var

from numpy._build_utils.apple_accelerate import (
uses_accelerate_framework
)
from numpy.compat import npy_load_module
from setup_common import * # noqa: F403

Expand Down Expand Up @@ -405,11 +409,6 @@ def configuration(parent_package='',top_path=None):
from numpy.distutils.system_info import (get_info, blas_opt_info,
lapack_opt_info)

# Accelerate is buggy, disallow it. See also numpy/linalg/setup.py
for opt_order in (blas_opt_info.blas_order, lapack_opt_info.lapack_order):
if 'accelerate' in opt_order:
opt_order.remove('accelerate')

config = Configuration('core', parent_package, top_path)
local_dir = config.local_path
codegen_dir = join(local_dir, 'code_generators')
Expand Down
65 changes: 65 additions & 0 deletions numpy/core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -6194,6 +6194,71 @@ def test_dot_array_order(self):
assert_equal(np.dot(b, a), res)
assert_equal(np.dot(b, b), res)

def test_accelerate_framework_sgemv_fix(self):

def aligned_array(shape, align, dtype, order='C'):
d = dtype(0)
N = np.prod(shape)
tmp = np.zeros(N * d.nbytes + align, dtype=np.uint8)
address = tmp.__array_interface__["data"][0]
for offset in range(align):
if (address + offset) % align == 0:
break
tmp = tmp[offset:offset+N*d.nbytes].view(dtype=dtype)
return tmp.reshape(shape, order=order)

def as_aligned(arr, align, dtype, order='C'):
aligned = aligned_array(arr.shape, align, dtype, order)
aligned[:] = arr[:]
return aligned

def assert_dot_close(A, X, desired):
assert_allclose(np.dot(A, X), desired, rtol=1e-5, atol=1e-7)

m = aligned_array(100, 15, np.float32)
s = aligned_array((100, 100), 15, np.float32)
np.dot(s, m) # this will always segfault if the bug is present

testdata = itertools.product((15,32), (10000,), (200,89), ('C','F'))
for align, m, n, a_order in testdata:
# Calculation in double precision
A_d = np.random.rand(m, n)
X_d = np.random.rand(n)
desired = np.dot(A_d, X_d)
# Calculation with aligned single precision
A_f = as_aligned(A_d, align, np.float32, order=a_order)
X_f = as_aligned(X_d, align, np.float32)
assert_dot_close(A_f, X_f, desired)
# Strided A rows
A_d_2 = A_d[::2]
desired = np.dot(A_d_2, X_d)
A_f_2 = A_f[::2]
assert_dot_close(A_f_2, X_f, desired)
# Strided A columns, strided X vector
A_d_22 = A_d_2[:, ::2]
X_d_2 = X_d[::2]
desired = np.dot(A_d_22, X_d_2)
A_f_22 = A_f_2[:, ::2]
X_f_2 = X_f[::2]
assert_dot_close(A_f_22, X_f_2, desired)
# Check the strides are as expected
if a_order == 'F':
assert_equal(A_f_22.strides, (8, 8 * m))
else:
assert_equal(A_f_22.strides, (8 * n, 8))
assert_equal(X_f_2.strides, (8,))
# Strides in A rows + cols only
X_f_2c = as_aligned(X_f_2, align, np.float32)
assert_dot_close(A_f_22, X_f_2c, desired)
# Strides just in A cols
A_d_12 = A_d[:, ::2]
desired = np.dot(A_d_12, X_d_2)
A_f_12 = A_f[:, ::2]
assert_dot_close(A_f_12, X_f_2c, desired)
# Strides in A cols and X
assert_dot_close(A_f_12, X_f_2, desired)



class MatmulCommon:
"""Common tests for '@' operator and numpy.matmul.
Expand Down
54 changes: 19 additions & 35 deletions numpy/distutils/system_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,22 +375,6 @@ def add_system_root(library_root):
so_ext = get_shared_lib_extension()


def is_symlink_to_accelerate(filename):
accelpath = '/System/Library/Frameworks/Accelerate.framework'
return (sys.platform == 'darwin' and os.path.islink(filename) and
os.path.realpath(filename).startswith(accelpath))


_accel_msg = (
'Found {filename}, but that file is a symbolic link to the '
'MacOS Accelerate framework, which is not supported by NumPy. '
'You must configure the build to use a different optimized library, '
'or disable the use of optimized BLAS and LAPACK by setting the '
'environment variables NPY_BLAS_ORDER="" and NPY_LAPACK_ORDER="" '
'before building NumPy.'
)


def get_standard_file(fname):
"""Returns a list of files named 'fname' from
1) System-wide directory (directory-location of this module)
Expand Down Expand Up @@ -539,6 +523,7 @@ def get_info(name, notfound_action=0):
'blis': blis_info, # use blas_opt instead
'lapack_mkl': lapack_mkl_info, # use lapack_opt instead
'blas_mkl': blas_mkl_info, # use blas_opt instead
'accelerate': accelerate_info, # use blas_opt instead
'openblas64_': openblas64__info,
'openblas64__lapack': openblas64__lapack_info,
'openblas_ilp64': openblas_ilp64_info,
Expand Down Expand Up @@ -1029,9 +1014,6 @@ def _find_lib(self, lib_dir, lib, exts):
for prefix in lib_prefixes:
p = self.combine_paths(lib_dir, prefix + lib + ext)
if p:
# p[0] is the full path to the binary library file.
if is_symlink_to_accelerate(p[0]):
raise RuntimeError(_accel_msg.format(filename=p[0]))
break
if p:
assert len(p) == 1
Expand Down Expand Up @@ -1766,10 +1748,18 @@ def get_atlas_version(**config):

class lapack_opt_info(system_info):
notfounderror = LapackNotFoundError

# List of all known LAPACK libraries, in the default order
lapack_order = ['mkl', 'openblas', 'flame', 'atlas', 'lapack']
lapack_order = ['accelerate', 'mkl', 'openblas', 'flame', 'atlas', 'lapack']
order_env_var_name = 'NPY_LAPACK_ORDER'

def _calc_info_accelerate(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These _calc_info_accelerate blocks are just moved around without any changes, right? It would be nice to undo that to clean up this PR diff. Then I think this patch is good to go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, done =).

info = get_info('accelerate')
if info:
self.set_info(**info)
return True
return False

def _calc_info_mkl(self):
info = get_info('lapack_mkl')
if info:
Expand Down Expand Up @@ -1820,13 +1810,6 @@ def _calc_info_atlas(self):
return True
return False

def _calc_info_accelerate(self):
info = get_info('accelerate')
if info:
self.set_info(**info)
return True
return False

def _get_info_blas(self):
# Default to get the optimized BLAS implementation
info = get_info('blas_opt')
Expand Down Expand Up @@ -1942,9 +1925,17 @@ class lapack64__opt_info(lapack_ilp64_opt_info):
class blas_opt_info(system_info):
notfounderror = BlasNotFoundError
# List of all known BLAS libraries, in the default order
blas_order = ['mkl', 'blis', 'openblas', 'atlas', 'blas']

blas_order = ['accelerate', 'mkl', 'blis', 'openblas', 'atlas', 'blas']
order_env_var_name = 'NPY_BLAS_ORDER'

def _calc_info_accelerate(self):
info = get_info('accelerate')
if info:
self.set_info(**info)
return True
return False

def _calc_info_mkl(self):
info = get_info('blas_mkl')
if info:
Expand Down Expand Up @@ -1979,13 +1970,6 @@ def _calc_info_atlas(self):
return True
return False

def _calc_info_accelerate(self):
info = get_info('accelerate')
if info:
self.set_info(**info)
return True
return False

def _calc_info_blas(self):
# Warn about a non-optimized BLAS library
warnings.warn(BlasOptNotFoundError.__doc__ or '', stacklevel=3)
Expand Down
5 changes: 0 additions & 5 deletions numpy/linalg/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ def configuration(parent_package='', top_path=None):

config.add_subpackage('tests')

# Accelerate is buggy, disallow it. See also numpy/core/setup.py
for opt_order in (blas_opt_info.blas_order, lapack_opt_info.lapack_order):
if 'accelerate' in opt_order:
opt_order.remove('accelerate')

# Configure lapack_lite

src_dir = 'lapack_lite'
Expand Down
0