Skip to content

Commit

Permalink
switch to BinaryBuilder binaries (#74)
Browse files Browse the repository at this point in the history
* switch to BinaryBuilder binaries

* fix alignment check for MKL

* latest FFTW build always has threads

* support installing MKL via Conda, and use for testing; bugfix for MKL claiming to be FFTW 3.3.4 but not having sprint_plan

* no need for dlext
  • Loading branch information
stevengj committed Jun 18, 2018
1 parent 35e2c05 commit d2b22f4
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 209 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ os:
- osx
julia:
- nightly
env:
matrix:
- JULIA_FFTW_PROVIDER=FFTW
- JULIA_FFTW_PROVIDER=MKL
notifications:
email: false
# uncomment the following lines to override the default test script
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# FFTW.jl

This package provides Julia bindings to the [FFTW](http://www.fftw.org/) library for
fast Fourier transforms, as well as functionality useful for signal processing.
fast Fourier transforms (FFTs), as well as functionality useful for signal processing.
These functions were formerly a part of Base Julia.

Users with a build of Julia based on Intel's Math Kernel Library (MKL) can take use MKL
for FFTs by setting an environment variable `JULIA_FFTW_PROVIDER` to `MKL` and running
`Pkg.build("FFTW")`.
Alternatively, the FFTs in Intel's Math Kernel Library (MKL) can be used
by setting an environment variable `JULIA_FFTW_PROVIDER` to `MKL` and running
`Pkg.build("FFTW")`. If Julia was built with MKL, then Julia's MKL will be
used for FFTs; otherwise the [Conda.jl package](https://github.com/JuliaPy/Conda.jl)
will be used to download MKL via the [mkl_fft Anaconda package](https://github.com/IntelPython/mkl_fft).
Setting this environment variable only needs to be done for the first build of the package;
after that, the package will remember to use MKL when building and updating.
Note however that MKL provides only a subset of the functionality provided by FFTW. See
Expand Down
3 changes: 2 additions & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ julia 0.7.0-DEV.3449
AbstractFFTs 0.3.0
Reexport
Compat 0.62.0
BinDeps 0.6.0
BinaryProvider 0.3.0
Conda
37 changes: 23 additions & 14 deletions deps/build.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Libdl
import Libdl
const depsfile = joinpath(@__DIR__, "deps.jl")

# If BLAS was compiled with MKL and the user wants MKL-based FFTs, we'll oblige.
# In that case, we have to do this little dance to get around having to use BinDeps
Expand All @@ -13,26 +14,34 @@ else
provider = "FFTW"
open(f -> println(f, provider), settings, "w")
end
if provider == "MKL" && Base.BLAS.vendor() === :mkl
mklpath = Libdl.dlpath("libmkl_rt")
depsfile = joinpath(@__DIR__, "deps.jl")
if provider == "MKL"
if Base.BLAS.vendor() === :mkl
mklpath = Libdl.dlpath("libmkl_rt")
else
using Conda
Conda.add("mkl_fft")
mklpath = joinpath(Conda.lib_dir(Conda.ROOTENV), "libmkl_rt")
end
mklpath = escape_string(mklpath)
isfile(depsfile) && rm(depsfile, force=true)
open(depsfile, "w") do f
println(f, """
# This is an auto-generated file, do not edit
using Libdl
if Libdl.dlopen_e("$mklpath") == C_NULL
error("Unable to load MKL from '$mklpath'.\\n",
"Please rerun Pkg.build(\\"FFTW\\") and restart Julia.")
import Libdl
const libfftw3 = "$mklpath"
const libfftw3f = libfftw3
function check_deps()
if Libdl.dlopen_e(libfftw3) == C_NULL
error("Unable to load MKL from '$mklpath'.\\n",
"Please rerun Pkg.build(\\"FFTW\\") and restart Julia.")
end
end
const libfftw = "$mklpath"
const libfftwf = "$mklpath"
""")
end
elseif provider == "MKL"
error("MKL build requested for FFTW but Julia was not built with MKL.\n",
"To fix this, set ENV[\"JULIA_FFTW_PROVIDER\"] = \"FFTW\" and \n",
"rerun Pkg.build(\"FFTW\").")
elseif provider != "FFTW"
error("Unrecognized JULIA_FFTW_PROVIDER \"$provider\".\n",
"To fix this, set ENV[\"JULIA_FFTW_PROVIDER\"] to \"FFTW\" or \"MKL\"\n",
"and rerun Pkg.build(\"FFTW\").")
else
include("build_fftw.jl")
end
164 changes: 45 additions & 119 deletions deps/build_fftw.jl
Original file line number Diff line number Diff line change
@@ -1,121 +1,47 @@
using Libdl
using BinDeps
using BinDeps: builddir, depsdir, libdir

# Binaries is not a recognized provider on Linux >:/
modified_defaults = false
if !in(BinDeps.Binaries, BinDeps.defaults)
pushfirst!(BinDeps.defaults, BinDeps.Binaries)
modified_defaults = true
end

BinDeps.@setup

const FFTW_VER = v"3.3.6-pl2"

if Sys.iswindows()
const libfftw_name = "libfftw3"
const libfftwf_name = "libfftw3f"
else
const libfftw_name = "libfftw3_threads"
const libfftwf_name = "libfftw3f_threads"
end

# Why can't everyone just agree on what to call this library...
function makealiases(lib)
major = string(FFTW_VER.major)
nover = replace(lib, major => "")
return String[
nover,
join([lib, Libdl.dlext, major], "."),
join([nover, Libdl.dlext, major], "."),
lib * "-" * major,
nover * "-" * major,
]
end

libfftw = library_dependency(libfftw_name, aliases=makealiases(libfftw_name))
libfftwf = library_dependency(libfftwf_name, aliases=makealiases(libfftwf_name))

const URL = "https://github.com/ararslan/fftw-builder/releases/download/v$FFTW_VER/libfftw-$FFTW_VER"

# Mapping of Sys.MACHINE to (url, sha) for precompiled binaries from fftw-builder
const downloads = Dict(
"x86_64-pc-linux-gnu" => ("$URL-linux-x86_64.tar.gz",
"43a049496d47dd1919283a981d21078942d04b067edd75b61cc6aef538098b53"),
"i686-pc-linux-gnu" => ("$URL-linux-i686.tar.gz",
"0b6ec1440ea76f72ed6e5d105a4d634e195b484e847c521ac0812092069f0c74"),
"x86_64-apple-darwin" => ("$URL-osx-x86_64.tar.gz",
"193f20bba844fd2524f70e941aa5203b153bdcd321df2f20fc3a9d15b4aae50a"),
"x86_64-w64-mingw32" => ("$URL-win-x86_64.zip",
"4e9bd1022c5980869b06bb9d10767fd2578dfe47f9323e6f572d0918da8c8a07"),
"i686-w64-mingw32" => ("$URL-win-i686.zip",
"60a65fd689495629a1558858f3a52011f9ce28df8aa756f5b315b17dd8dd8843"),
using BinaryProvider # requires BinaryProvider 0.3.0 or later

# Parse some basic command-line arguments
const verbose = "--verbose" in ARGS
const prefix = Prefix(get([a for a in ARGS if a != "--verbose"], 1, joinpath(@__DIR__, "usr")))
products = [
LibraryProduct(prefix, String["libfftw3"], :libfftw3),
LibraryProduct(prefix, String["libfftw3f"], :libfftw3f),
]

# Download binaries from hosted location
bin_prefix = "https://github.com/JuliaMath/FFTWBuilder/releases/download/v3.3.8+1"

# Listing of files generated by BinaryBuilder:
download_info = Dict(
Linux(:aarch64, :glibc) => ("$bin_prefix/FFTW.aarch64-linux-gnu.tar.gz", "4296ad9af20d4441fd809c6aaa3ee5fa36818b7a2eb3372da7d2ead454b4e570"),
Linux(:aarch64, :musl) => ("$bin_prefix/FFTW.aarch64-linux-musl.tar.gz", "ef6d4e56bd9e405ef2895a857ffbc07cb7abcf450040a2335b83a95f4a431392"),
Linux(:armv7l, :glibc, :eabihf) => ("$bin_prefix/FFTW.arm-linux-gnueabihf.tar.gz", "8f8c69a6eca468465734e1fd58801519cea0f7a8f9e08bba93e39315758f7a7c"),
Linux(:armv7l, :musl, :eabihf) => ("$bin_prefix/FFTW.arm-linux-musleabihf.tar.gz", "48d137fddab6888bdc59893d22728f081578ff0884954f0f6f5df51afff53ece"),
Linux(:i686, :glibc) => ("$bin_prefix/FFTW.i686-linux-gnu.tar.gz", "76d85d81a81752a0e08bc2eec51a568a6000928a550c37a181b51340452b1b5f"),
Linux(:i686, :musl) => ("$bin_prefix/FFTW.i686-linux-musl.tar.gz", "5ee42df3aa002e9511c3cc808f728429e4930bb24df8b461dc137bf49aa71b8f"),
Windows(:i686) => ("$bin_prefix/FFTW.i686-w64-mingw32.tar.gz", "28b96cb5d78c87d16d305a63a838c5027d319c0292f00c925e14f21c744535a8"),
Linux(:powerpc64le, :glibc) => ("$bin_prefix/FFTW.powerpc64le-linux-gnu.tar.gz", "53d305eebb3a152df093d637fe8a4d6288a2b7175bf91ab9242dad62e5e0853a"),
MacOS(:x86_64) => ("$bin_prefix/FFTW.x86_64-apple-darwin14.tar.gz", "7562aed6279ea965435c8a388be1494b9a18f7e00058d0fa260a711bffde1bd5"),
Linux(:x86_64, :glibc) => ("$bin_prefix/FFTW.x86_64-linux-gnu.tar.gz", "70dcc7ad2697121564d5d91da9f2544b0e68b026779a45063039a39cd5585711"),
Linux(:x86_64, :musl) => ("$bin_prefix/FFTW.x86_64-linux-musl.tar.gz", "4bf1c1e7489241c38788bc061f2a091fe72605f67e58411b2933313fa0923877"),
FreeBSD(:x86_64) => ("$bin_prefix/FFTW.x86_64-unknown-freebsd11.1.tar.gz", "ad70aca12821f6df1c67da74fc2f1b4fa009ac14d8570ff1f912876e731185af"),
Windows(:x86_64) => ("$bin_prefix/FFTW.x86_64-w64-mingw32.tar.gz", "6726bff25faeca8e29dfce8be5b0fb7da0a380faa9fdb5a5a6c98ea76d009b2f"),
)

const machine = Sys.isapple() ? "x86_64-apple-darwin" : Sys.MACHINE

if haskey(downloads, machine)
url, sha = downloads[machine]
provides(Binaries, URI(url), [libfftw, libfftwf], SHA=sha, os=BinDeps.OSNAME,
unpacked_dir=joinpath("usr", "lib"), installed_libpath=libdir(libfftw))
scratch = false
elseif Sys.KERNEL === :FreeBSD
provides(BSDPkg, "fftw3", [libfftw, libfftwf], os=:FreeBSD)
scratch = false
else
info("No precompiled binaries found for your system. Building from scratch...")
scratch = true
end

general_config = ["--prefix=" * abspath(builddir(libfftw)),
"--libdir=" * abspath(libdir(libfftw)),
"--bindir=" * abspath(bindir(libfftw))]

fftw_config = ["--enable-shared", "--disable-fortran", "--disable-mpi", "--enable-threads"]
fftw_enable_single = "--enable-single"

if Sys.ARCH === :ppc
append!(fftw_config, ["--enable-altivec", "--enable-fma"])
elseif Sys.ARCH === :x86_64
append!(fftw_config, ["--enable-sse2", "--enable-fma"])
end

if Sys.iswindows()
append!(fftw_config, ["--with-our-malloc", "--with-combined-threads"])
Sys.ARCH === :x86_64 || push!(fftw_config, "--with-incoming-stack-boundary=2")
end

# Make it harder to build from scratch
if scratch
provides(Sources, URI("http://www.fftw.org/fftw-$FFTW_VER.tar.gz"), [libfftw, libfftwf])

provides(BuildProcess, (@build_steps begin
GetSources(libfftw)
CreateDirectory(builddir(libfftw))
@build_steps begin
ChangeDirectory(builddir(libfftw))
FileRule(joinpath(libdir(libfftw), libfftw_name * "." * Libdl.dlext), @build_steps begin
CreateDirectory(libdir(libfftw))
`$(joinpath(srcdir(libfftw), "fftw-$FFTW_VER", "configure")) $general_config $fftw_config`
`$MAKE_CMD`
`$MAKE_CMD install`
end)
FileRule(joinpath(libdir(libfftw), libfftwf_name * "." * Libdl.dlext), @build_steps begin
`$(joinpath(srcdir(libfftw), "fftw-$FFTW_VER", "configure")) $general_config $fftw_config $fftw_enable_single`
`$MAKE_CMD`
`$MAKE_CMD install`
end)
end
end), [libfftw, libfftwf])
end

if Sys.iswindows()
BinDeps.@install Dict([:libfftw3 => :libfftw, :libfftw3f => :libfftwf])
else
BinDeps.@install Dict([:libfftw3_threads => :libfftw, :libfftw3f_threads => :libfftwf])
end

if modified_defaults
popfirst!(BinDeps.defaults)
end
# Install unsatisfied or updated dependencies:
unsatisfied = any(!satisfied(p; verbose=verbose) for p in products)
if haskey(download_info, platform_key())
url, tarball_hash = download_info[platform_key()]
if unsatisfied || !isinstalled(url, tarball_hash; prefix=prefix)
# Download and install binaries
install(url, tarball_hash; prefix=prefix, force=true, verbose=verbose)
end
elseif unsatisfied
# If we don't have a BinaryProvider-compatible .tar.gz to download, complain.
# Alternatively, you could attempt to install from a separate provider,
# build from source or something even more ambitious here.
error("Your platform $(triplet(platform_key())) is not supported by this package!")
end

# Write out a deps.jl file that will contain mappings for our products
write_deps_file(joinpath(@__DIR__, "deps.jl"), products)
25 changes: 7 additions & 18 deletions src/FFTW.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ __precompile__()

module FFTW

using Compat
using LinearAlgebra
using Reexport
using Compat, LinearAlgebra, Reexport
import Libdl
@reexport using AbstractFFTs

import AbstractFFTs: Plan, ScaledPlan,
Expand All @@ -26,28 +25,18 @@ else
end

# MKL provides its own FFTW
fftw_vendor() = BLAS.vendor() === :mkl ? :mkl : :fftw

if fftw_vendor() === :mkl
const libfftw_name = "libmkl_rt"
const libfftwf_name = "libmkl_rt"
elseif Sys.iswindows()
const libfftw_name = "libfftw3"
const libfftwf_name = "libfftw3f"
else
const libfftw_name = "libfftw3_threads"
const libfftwf_name = "libfftw3f_threads"
end
const fftw_vendor = occursin("libmkl_rt", libfftw3) ? :mkl : :fftw

# Threads must be initialized before any FFTW planning routine.
# If FFTW was built with threads, then they must be initialized before any FFTW planning routine.
# -- This initializes FFTW's threads support (defaulting to 1 thread).
# If this isn't called before the FFTW planner is created, then
# FFTW's threads algorithms won't be registered or used at all.
# (Previously, we called fftw_cleanup, but this invalidated existing
# plans, causing Base Julia issue #19892.)
function __init__()
stat = ccall((:fftw_init_threads, libfftw), Int32, ())
statf = ccall((:fftwf_init_threads, libfftwf), Int32, ())
check_deps()
stat = ccall((:fftw_init_threads, libfftw3), Int32, ())
statf = ccall((:fftwf_init_threads, libfftw3f), Int32, ())
if stat == 0 || statf == 0
error("could not initialize FFTW threads")
end
Expand Down
Loading

0 comments on commit d2b22f4

Please sign in to comment.