8000 ENH: enable OpenBLAS on windows. · Pull Request #9645 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: enable OpenBLAS on windows. #9645

8000
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 13 commits into from Sep 30, 2017
203 changes: 184 additions & 19 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,195 @@
skip_tags: true
clone_depth: 1
# As config was originally based on an example by Olivier Grisel. Thanks!
# https://github.com/ogrisel/python-appveyor-demo/blob/master/appveyor.yml
clone_depth: 50

os: Visual Studio 2015
Copy link
Member

Choose a reason for hiding this comment

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

We still want to test MSVC as well, it needs to stay working. So instead of removing it all, I suggest to use a build matrix with a mix of the two build configs (without increasing the total number of builds).

Copy link
Author

Choose a reason for hiding this comment

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

Which MSVC are you concerned about? This configuration should test 2008, 2010, and 2015. The "os" has nothing to do with that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ralf - I'm guessing you mean a lapack-lite build, without OpenBLAS?

Copy link
Member

Choose a reason for hiding this comment

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

No, I didn't mean lapack-lite, that's okay to only test on Linux I think. The install and build_script part here explicitly add MINGW to the path and in the whole file there are only references to Cygwin and MinGW. So if MSVC is still being used, it's very much non-obvious (plus it's unclear what the purpose of the MinGW stuff is).

So either something is wrong (like MSVC being dropped) or this needs a lot of comments explaining what is going on.

Copy link
Author

Choose a reason for hiding this comment

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

MinGW is used to compile the OpenBLAS library. MSVC is used to compile everything else.

Copy link
Member
@pv pv Sep 9, 2017

Choose a reason for hiding this comment

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

IIUC, all the compilers are installed in the appveyor VM regardless of this setting, and Python distutils generally finds MSVC based on registry entries rather than PATH.

If present, MSVC is used by default over mingw C compilers, even if the latter is on PATH.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's my understanding too.

Copy link
Member

Choose a reason for hiding this comment

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

@pv @matthew-brett not sure if you're arguing against including more documentation or not. My comment is not particular to the line os: Visual Studio 2015. There's a lot of opaque stuff in this PR that simply needs explaining.

Copy link
Member

Choose a reason for hiding this comment

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

No, I'm just explaining why it works.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, more documentation would be good.

I hate to be annoying, but I have previously done the same work in the windows-wheel-builder repo - here's a PR : numpy/windows-wheel-builder#3 in case it's helpful.

# No reason for us to restrict the number concurrent jobs
max_jobs: 100

cache:
- '%LOCALAPPDATA%\pip\Cache'

environment:
global:
MINGW_32: C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin
MINGW_64: C:\mingw-w64\x86_64-6.3.0-posix-seh-rt_v5-rev1\mingw64\bin
OPENBLAS_32: https://3f23b170c54c2533c070-1c8a9b3114517dc5fe17b7c3f8c63a43.ssl.cf2.rackcdn.com/openblas-5f998ef_gcc7_1_0_win32.zip
OPENBLAS_64: https://3f23b170c54c2533c070-1c8a9b3114517dc5fe17b7c3f8c63a43.ssl.cf2.rackcdn.com/openblas-5f998ef_gcc7_1_0_win64.zip
APPVEYOR_SAVE_CACHE_ON_ERROR: true
APPVEYOR_SKIP_FINALIZE_ON_EXIT: true
TEST_TIMEOUT: 1000

matrix:
- PY_MAJOR_VER: 2
PYTHON_ARCH: "x86"
- PY_MAJOR_VER: 3
PYTHON_ARCH: "x86_64"
- PY_MAJOR_VER: 3
PYTHON_ARCH: "x86"
- PYTHON: C:\Python36
PYTHON_VERSION: 3.6
PYTHON_ARCH: 32
TEST_MODE: fast

build_script:
# If there's a newer build queued for the same PR, cancel this one
- PYTHON: C:\Python27-x64
PYTHON_VERSION: 2.7
PYTHON_ARCH: 64
TEST_MODE: fast

- PYTHON: C:\Python34-x64
PYTHON_VERSION: 3.4
PYTHON_ARCH: 64
TEST_MODE: fast

- PYTHON: C:\Python36-x64
PYTHON_VERSION: 3.6
PYTHON_ARCH: 64
TEST_MODE: full

- PYTHON: C:\Python27
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if the tag builds are actually needed for Numpy. The production wheels are built by a different CI config, this one is used only for integration testing.

Copy link
Author

Choose a reason for hiding this comment

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

I don't think they're needed but they could be used to compare differences between the released wheels and the wheels used for testing. A major advantage of this appveyor configuration is that you can actually download the built wheels and inspect them yourself.

PYTHON_VERSION: 2.7
PYTHON_ARCH: 32
SKIP_NOTAG: true
TEST_MODE: full

- PYTHON: C:\Python34
PYTHON_VERSION: 3.4
PYTHON_ARCH: 32
SKIP_NOTAG: true
TEST_MODE: full

- PYTHON: C:\Python35-x64
PYTHON_VERSION: 3.5
PYTHON_ARCH: 64
SKIP_NOTAG: true
TEST_MODE: full

- PYTHON: C:\Python35
PYTHON_VERSION: 3.5
PYTHON_ARCH: 32
SKIP_NOTAG: true
TEST_MODE: full

init:
- "ECHO %PYTHON% %PYTHON_VERSION% %PYTHON_ARCH%"
- "ECHO \"%APPVEYOR_SCHEDULED_BUILD%\""
# If there is a newer build queued for the same PR, cancel this one.
# The AppVeyor 'rollout builds' option is supposed to serve the same
# purpose but it is problematic because it tends to cancel builds pushed
# directly to master instead of just PR builds (or the converse).
# credits: JuliaLang developers.
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
throw "There are newer queued builds for this pull request, failing early." }
- ps: Start-FileDownload "https://repo.continuum.io/miniconda/Miniconda$env:PY_MAJOR_VER-latest-Windows-$env:PYTHON_ARCH.exe" C:\Miniconda.exe; echo "Finished downloading miniconda"
- cmd: C:\Miniconda.exe /S /D=C:\Py
- SET PATH=C:\Py;C:\Py\Scripts;C:\Py\Library\bin;%PATH%
- conda config --set always_yes yes
- conda update conda
- conda install cython nose pytz
- pip install . -vvv
Write-Host "There are newer queued builds for this pull request, skipping build."
Exit-AppveyorBuild
}
- ps: |
If (($env:SKIP_NOTAG -eq "true") -and ($env:APPVEYOR_REPO_TAG -ne "true")) {
Write-Host "Skipping build, not at a tag."
Exit-AppveyorBuild
}

install:
# Show size of cache
- C:\cygwin\bin\du -hs "%LOCALAPPDATA%\pip\Cache"
# Prepend newly installed Python to the PATH of this build (this cannot be
# done from inside the powershell script as it would require to restart
# the parent CMD process).
- SET PATH=%PYTHON%;%PYTHON%\Scripts;%PATH%

# Check that we have the expected version and architecture for Python
- python --version
- >-
%CMD_IN_ENV%
python -c "import sys,platform,struct;
print(sys.platform, platform.machine(), struct.calcsize('P') * 8, )"

# Install "openblas.a" to PYTHON\lib
# Library provided by Matthew Brett at https://github.com/matthew-brett/build-openblas
- ps: |
$PYTHON_ARCH = $env:PYTHON_ARCH
$PYTHON = $env:PYTHON
If ($PYTHON_ARCH -eq 32) {
$OPENBLAS = $env:OPENBLAS_32
} Else {
$OPENBLAS = $env:OPENBLAS_64
}
$clnt = new-object System.Net.WebClient
Copy link
Member

Choose a reason for hiding this comment

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

A comment here or above ps would be useful. I think it's "downloads an already built OpenBLAS library from the path given by $OPENBLAS". That build was done from repo XXX.

Copy link
Author
@ghost ghost Sep 10, 2017

Choose a reason for hiding this comment

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

I actually don't know the repo but matthew is in charge of these builds.

Copy link
Author

Choose a reason for hiding this comment

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

Copy link
F438
Author

Choose a reason for hiding this comment

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

I updated the comment above the ps block.

$file = "$(New-TemporaryFile).zip"
$tmpdir = New-TemporaryFile | %{ rm $_; mkdir $_ }
$destination = "$PYTHON\lib\openblas.a"

echo $file
echo $tmpdir
echo $OPENBLAS

$clnt.DownloadFile($OPENBLAS,$file)
Get-FileHash $file | Format-List

Expand-Archive $file $tmpdir

rm $tmpdir\$PYTHON_ARCH\lib\*.dll.a
$lib = ls $tmpdir\$PYTHON_ARCH\lib\*.a | ForEach { ls $_ } | Select-Object -first 1
echo $lib

cp $lib $destination
ls $destination

# Upgrade to the latest pip.
- '%CMD_IN_ENV% python -m pip install -U pip setuptools wheel'

# Install the numpy test dependencies.
- '%CMD_IN_ENV% pip install -U --timeout 5 --retries 2 -r tools/ci/appveyor/requirements.txt'

build_script:
# Here, we add MinGW to the path to be able to link an OpenBLAS.dll
# We then use the import library from the DLL to compile with MSVC
- ps: |
$PYTHON_ARCH = $env:PYTHON_ARCH
If ($PYTHON_ARCH -eq 32) {
$MINGW = $env:MINGW_32
} Else {
$MINGW = $env:MINGW_64
}
$env:Path += ";$MINGW"
$env:NPY_NUM_BUILD_JOBS = "4"
mkdir dist
pip wheel -v -v -v --wheel-dir=dist .

# For each wheel that pip has placed in the "dist" directory
# First, upload the wheel to the "artifacts" tab and then
# install the wheel. If we have only built numpy (as is the case here),
# then there will be one wheel to install.

# This method is more representative of what will be distributed,
# because it actually tests what the built wheels will be rather than
# what 'setup.py install' will do and at it uploads the wheels so that
# they can be inspected.

ls dist -r | Foreach-Object {
appveyor PushArtifact $_.FullName
pip install $_.FullName
}

test_script:
- python runtests.py -v -n

after_build:
# Remove old or huge cache files to hopefully not exceed the 1GB cache limit.
#
# If the cache limit is reached, the cache will not be updated (of not even
# created in the first run). So this is a trade of between keeping the cache
# current and having a cache at all.
# NB: This is done only `on_success` since the cache in uploaded only on
# success anyway.
- C:\cygwin\bin\find "%LOCALAPPDATA%\pip" -type f -mtime +360 -delete
- C:\cygwin\bin\find "%LOCALAPPDATA%\pip" -type f -size +10M -delete
- C:\cygwin\bin\find "%LOCALAPPDATA%\pip" -empty -delete
# Show size of cache
- C:\cygwin\bin\du -hs "%LOCALAPPDATA%\pip\Cache"

on_finish:
# We can get a nice display of test results in the "test" tab with py.test
# For now, this does nothing.
- ps: |
If (Test-Path .\junit-results.xml) {
(new-object net.webclient).UploadFile(
"https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)",
(Resolve-Path .\junit-results.xml)
)
}
$LastExitCode = 0
2 changes: 1 addition & 1 deletion numpy/distutils/tests/test_system_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def have_compiler():
try:
if not compiler.initialized:
compiler.initialize() # MSVC is different
except DistutilsError:
except (DistutilsError, ValueError):
Copy link
Author

Choose a reason for hiding this comment

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

BTW this is because MSVC 2008 is a crappy compiler.

return False
cmd = [compiler.cc]
try:
Expand Down
4 changes: 3 additions & 1 deletion numpy/f2py/tests/test_block_docstring.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from __future__ import division, absolute_import, print_function

import textwrap
import sys
from . import util

from numpy.testing import run_module_suite, assert_equal
from numpy.testing import run_module_suite, assert_equal, dec

class TestBlockDocString(util.F2PyTest):
code = """
Expand All @@ -15,6 +16,7 @@ class TestBlockDocString(util.F2PyTest):
END
"""

@dec.knownfailureif(sys.platform=='win32', msg='Fails with MinGW64 Gfortran (Issue #9673)')
def test_block_docstring(self):
expected = "'i'-array(2,3)\n"
assert_equal(self.module.block.__doc__, expected)
Expand Down
2 changes: 2 additions & 0 deletions numpy/f2py/tests/test_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import math
import textwrap
import sys

from numpy import array
from numpy.testing import run_module_suite, assert_, assert_equal, dec
Expand Down Expand Up @@ -119,6 +120,7 @@ def mth(self):
r = t(a.mth)
assert_(r == 9, repr(r))

@dec.knownfailureif(sys.platform=='win32', msg='Fails with MinGW64 Gfortran (Issue #9673)')
def test_string_callback(self):

def callback(code):
Expand Down
5 changes: 3 additions & 2 deletions numpy/f2py/tests/test_common.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
from __future__ import division, absolute_import, print_function

import os

from numpy.testing import run_module_suite, assert_array_equal, dec
import sys
import numpy as np
from . import util

from numpy.testing import run_module_suite, assert_array_equal, dec

def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))

class TestCommonBlock(util.F2PyTest):
sources = [_path('src', 'common', 'block.f')]

@dec.knownfailureif(sys.platform=='win32', msg='Fails with MinGW64 Gfortran (Issue #9673)')
def test_common_block(self):
self.module.initcb()
assert_array_equal(self.module.block.long_bn,
Expand Down
9 changes: 5 additions & 4 deletions numpy/f2py/tests/util.py
F438
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
import textwrap
import re
import random
import numpy.f2py

from numpy.compat import asbytes, asstr
import numpy.f2py
from numpy.testing import SkipTest, temppath
from numpy.testing import SkipTest, temppath, dec
from importlib import import_module

try:
from hashlib import md5
Expand Down Expand Up @@ -146,8 +147,7 @@ def build_module(source_files, options=[], skip=[], only=[], module_name=None):
os.unlink(fn)

# Import
__import__(module_name)
return sys.modules[module_name]
return import_module(module_name)


@_memoize
Expand Down Expand Up @@ -319,6 +319,7 @@ class F2PyTest(object):
module = None
module_name = None

@dec.knownfailureif(sys.platform=='win32', msg='Fails with MinGW64 Gfortran (Issue #9673)')
def setup(self):
if self.module is not None:
return
Expand Down
4 changes: 4 additions & 0 deletions numpy/ma/tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import pickle
import operator
import itertools
import sys
from functools import reduce


Expand Down Expand Up @@ -46,6 +47,7 @@
ravel, repeat, reshape, resize, shape, sin, sinh, sometrue, sort, sqrt,
subtract, sum, take, tan, tanh, transpose, where, zeros,
)
from numpy.testing import dec

pi = np.pi

Expand Down Expand Up @@ -3605,6 +3607,8 @@ def test_varstd(self):
assert_almost_equal(np.sqrt(mXvar0[k]),
mX[:, k].compressed().std())

@dec.knownfailureif(sys.platform=='win32' and sys.version_info < (3, 6),
msg='Fails on Python < 3.6 (Issue #9671)')
@suppress_copy_mask_on_assignment
def test_varstd_specialcases(self):
# Test a special case for var
Expand Down
21 changes: 12 additions & 9 deletions numpy/testing/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,18 @@ def test_error_message(self):
try:
self._assert_func(np.array([1, 2]), np.matrix([1, 2]))
except AssertionError as e:
self.assertEqual(
str(e),
"\nArrays are not equal\n\n"
"(shapes (2,), (1, 2) mismatch)\n"
" x: array([1, 2])\n"
" y: [repr failed for <matrix>: The truth value of an array "
"with more than one element is ambiguous. Use a.any() or "
"a.all()]")

msg = str(e)
msg2 = msg.replace("shapes (2L,), (1L, 2L)", "shapes (2,), (1, 2)")
msg_reference = "\nArrays are not equal\n\n" \
"(shapes (2,), (1, 2) mismatch)\n" \
" x: array([1, 2])\n" \
" y: [repr failed for <matrix>: The truth value of an array " \
"with more than one element is ambiguous. Use a.any() or " \
"a.all()]"
try:
self.assertEqual(msg, msg_reference)
except AssertionError:
self.assertEqual(msg2, msg_reference)
Copy link
Member
@pv pv Sep 9, 2017

Choose a reason for hiding this comment

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

The try: except is not needed. You can make it simpler via msg = msg.replace(...)


class TestArrayAlmostEqual(_GenericTest, unittest.TestCase):

Expand Down
6 changes: 6 additions & 0 deletions tools/ci/appveyor/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
cython
nose
pytest-timeout
pytest-xdist
pytest-env
pytest-faulthandler
0