diff --git a/Cargo.toml b/Cargo.toml index 7bd35e0c2..47bf2a18d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "arrayfire" description = "ArrayFire is a high performance software library for parallel computing with an easy-to-use API. Its array based function set makes parallel programming simple. ArrayFire's multiple backends (CUDA, OpenCL and native CPU) make it platform independent and highly portable. A few lines of code in ArrayFire can replace dozens of lines of parallel computing code, saving you valuable time and lowering development costs. This crate provides Rust bindings for ArrayFire library." -version = "3.0.0" +version = "3.1.3" documentation = "http://arrayfire.github.io/arrayfire-rust/arrayfire/index.html" homepage = "https://github.com/arrayfire/arrayfire" repository = "https://github.com/arrayfire/arrayfire-rust" @@ -27,6 +27,10 @@ path = "src/lib.rs" name = "helloworld" path = "examples/helloworld.rs" +[[example]] +name = "unified" +path = "examples/unified.rs" + [[example]] name = "pi" path = "examples/pi.rs" diff --git a/README.md b/README.md index b18e4ef06..b390d727d 100644 --- a/README.md +++ b/README.md @@ -112,5 +112,4 @@ You might see something along the lines of : dyld: Library not loaded: @rpath/libafopencl.3.dylib ``` -This is related to this [Rust issue](https://github.com/rust-lang/rust/issues/25185) -A workaround for now is to add the location of libaf*.dylib to your LD_LIBRARY_PATH. +You need to add the location of libaf.{dylib, so, dll} to your LD_LIBRARY_PATH. diff --git a/arrayfire b/arrayfire index 63807f442..97bed1593 160000 --- a/arrayfire +++ b/arrayfire @@ -1 +1 @@ -Subproject commit 63807f4421385af94dc77e3332f04a2a64a3b1ed +Subproject commit 97bed1593b733308cba6d0c3112a56ff21a93903 diff --git a/build.conf b/build.conf index 83e1a7677..1d63c876b 100644 --- a/build.conf +++ b/build.conf @@ -1,15 +1,10 @@ { - "use_backend": "cpu", - "use_lib": false, "lib_dir": "/usr/local/lib", "inc_dir": "/usr/local/include", "build_type": "Release", "build_threads": "4", - "build_cuda": "OFF", - "build_opencl": "ON", - "build_cpu": "ON", "build_examples": "OFF", "build_test": "OFF", "build_graphics": "ON", @@ -31,5 +26,5 @@ "cuda_sdk": "/usr/local/cuda", "opencl_sdk": "/usr", - "sdk_lib_dir": "lib" + "sdk_lib_dir": "lib64" } diff --git a/build.rs b/build.rs index 682d83b14..39b23ec51 100644 --- a/build.rs +++ b/build.rs @@ -13,10 +13,6 @@ use std::convert::AsRef; #[allow(dead_code)] #[derive(RustcDecodable)] struct Config { - // below variable dictates which - // backend library is used by rust wrapper - use_backend: String, - // Use the existing lib if it exists use_lib: bool, lib_dir: String, @@ -25,9 +21,6 @@ struct Config { // Build related build_type: String, build_threads: String, - build_cuda: String, - build_opencl: String, - build_cpu: String, build_examples: String, build_test: String, build_graphics: String, @@ -66,6 +59,13 @@ fn fail(s: &str) -> ! { panic!("\n{}\n\nbuild script failed, must exit now", s) } +fn file_exists(location: &str) -> bool { + match fs::metadata(location) { + Ok(f) => f.is_file(), + Err(_) => false, + } +} + fn run(cmd: &mut Command, program: &str) { println!("running: {:?}", cmd); let status = match cmd.status() { @@ -233,9 +233,6 @@ fn run_cmake_command(conf: &Config, build_dir: &std::path::PathBuf) { run(cmake_cmd.arg("..").arg("-G").arg("Visual Studio 12 2013 Win64") .args(&[format!("-DCMAKE_BUILD_TYPE:STRING={}", conf.build_type), - format!("-DBUILD_CPU:BOOL={}", conf.build_cpu), - format!("-DBUILD_CUDA:BOOL={}", conf.build_cuda), - format!("-DBUILD_OPENCL:BOOL={}", conf.build_opencl), format!("-DBUILD_EXAMPLES:BOOL={}", conf.build_examples), format!("-DBUILD_TEST:BOOL={}", conf.build_test), format!("-DBOOST_ROOT={}", conf.boost_dir), @@ -312,9 +309,6 @@ fn run_cmake_command(conf: &Config, build_dir: &std::path::PathBuf) { run(cmake_cmd.arg("..") .args(&[format!("-DCMAKE_BUILD_TYPE:STRING={}", conf.build_type), - format!("-DBUILD_CPU:BOOL={}", conf.build_cpu), - format!("-DBUILD_CUDA:BOOL={}", conf.build_cuda), - format!("-DBUILD_OPENCL:BOOL={}", conf.build_opencl), format!("-DBUILD_EXAMPLES:BOOL={}", conf.build_examples), format!("-DBUILD_TEST:BOOL={}", conf.build_test), format!("-DCMAKE_INSTALL_PREFIX:STRING={}", "package")]) @@ -332,6 +326,16 @@ fn run_cmake_command(conf: &Config, build_dir: &std::path::PathBuf) { .arg(format!("install")), "make"); } +fn backend_exists(name: &str) -> bool{ + let win_backend = name.to_string() + ".dll"; + let osx_backend = name.to_string() + ".dylib"; + let linux_backend = name.to_string() + ".so"; + + return file_exists(&win_backend) + || file_exists(&osx_backend) + || file_exists(&linux_backend) +} + fn blob_backends(conf: &Config, build_dir: &std::path::PathBuf) -> (Vec, Vec) { let mut backend_dirs :Vec= Vec::new(); let mut backends :Vec = Vec::new(); @@ -342,11 +346,10 @@ fn blob_backends(conf: &Config, build_dir: &std::path::PathBuf) -> (Vec, backend_dirs.push(build_dir.join("package/lib").to_str().to_owned().unwrap().to_string()); } - if conf.use_backend == "cpu" { - backends.push("afcpu".to_string()); - } else if conf.use_backend == "cuda" { - backends.push("afcuda".to_string()); - backends.push("nvvm".to_string()); + let lib_dir = PathBuf::from(backend_dirs.last().unwrap()); + + // blob in cuda deps + if backend_exists(&lib_dir.join("libafcuda").to_string_lossy()) { if cfg!(windows) { backend_dirs.push(format!("{}\\lib\\x64", conf.cuda_sdk)); backend_dirs.push(format!("{}\\nvvm\\lib\\x64", conf.cuda_sdk)); @@ -354,8 +357,10 @@ fn blob_backends(conf: &Config, build_dir: &std::path::PathBuf) -> (Vec, backend_dirs.push(format!("{}/{}", conf.cuda_sdk, conf.sdk_lib_dir)); backend_dirs.push(format!("{}/nvvm/{}", conf.cuda_sdk, conf.sdk_lib_dir)); } - } else if conf.use_backend == "opencl" { - backends.push(("afopencl".to_string())); + } + + //blob in opencl deps + if backend_exists(&lib_dir.join("libafopencl").to_string_lossy()) { if ! cfg!(target_os = "macos"){ backends.push("OpenCL".to_string()); } @@ -366,6 +371,10 @@ fn blob_backends(conf: &Config, build_dir: &std::path::PathBuf) -> (Vec, } } + if backend_exists(&lib_dir.join("libaf").to_string_lossy()) { + backends.push("af".to_string()); + } + if conf.build_graphics=="ON" { backends.push("forge".to_string()); if !conf.use_lib { diff --git a/examples/unified.rs b/examples/unified.rs new file mode 100644 index 000000000..d01c5723a --- /dev/null +++ b/examples/unified.rs @@ -0,0 +1,56 @@ +extern crate arrayfire as af; + +use af::*; + +#[allow(unused_must_use)] +fn test_backend(){ + info(); + + let num_rows: u64 = 10; + let num_cols: u64 = 10; + let dims = Dim4::new(&[num_rows, num_cols, 1, 1]); + + println!("Create a 10-by-10 matrix of random floats on the compute device"); + let a = match randu(dims, Aftype::F32) { + Ok(value) => value, + Err(error) => panic!("{}", error), + }; + print(&a); +} + + +#[allow(unused_must_use)] +fn main() { + println!("There are {:?} available backends", get_backend_count().unwrap()); + let available = get_available_backends().unwrap(); + + if available.contains(&AfBackend::AF_BACKEND_CPU){ + println!("Evaluating CPU Backend..."); + let err = set_backend(AfBackend::AF_BACKEND_CPU); + println!("There are {} CPU compute devices", device_count().unwrap()); + match err { + Ok(_) => test_backend(), + Err(e) => println!("CPU backend error: {}", e), + }; + } + + if available.contains(&AfBackend::AF_BACKEND_CUDA){ + println!("Evaluating CUDA Backend..."); + let err = set_backend(AfBackend::AF_BACKEND_CUDA); + println!("There are {} CUDA compute devices", device_count().unwrap()); + match err { + Ok(_) => test_backend(), + Err(e) => println!("CUDA backend error: {}", e), + }; + } + + if available.contains(&AfBackend::AF_BACKEND_OPENCL){ + println!("Evaluating OpenCL Backend..."); + let err = set_backend(AfBackend::AF_BACKEND_OPENCL); + println!("There are {} OpenCL compute devices", device_count().unwrap()); + match err { + Ok(_) => test_backend(), + Err(e) => println!("OpenCL backend error: {}", e), + }; + } +} diff --git a/src/defines.rs b/src/defines.rs index 9f6198ad2..030dbeabf 100644 --- a/src/defines.rs +++ b/src/defines.rs @@ -47,6 +47,31 @@ pub enum AfError { ERR_UNKNOWN = 999 } +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum AfBackend { + /// Default backend order: OpenCL -> CUDA -> CPU + AF_BACKEND_DEFAULT = 0, + /// CPU a.k.a sequential algorithms + AF_BACKEND_CPU = 1, + /// CUDA Compute Backend + AF_BACKEND_CUDA = 2, + /// OpenCL Compute Backend + AF_BACKEND_OPENCL = 4 +} + +impl Display for AfBackend { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + let text = match *self { + AfBackend::AF_BACKEND_OPENCL => "OpenCL", + AfBackend::AF_BACKEND_CUDA => "Cuda", + AfBackend::AF_BACKEND_CPU => "CPU", + AfBackend::AF_BACKEND_DEFAULT => "Default", + }; + write!(f, "{}", text) + } +} + impl Display for AfError { fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { write!(f, "{}", self.description()) diff --git a/src/device/mod.rs b/src/device/mod.rs index 631e2afcb..2c3668658 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -1,21 +1,19 @@ extern crate libc; -use defines::AfError; -use self::libc::c_int; +use defines::{AfError, AfBackend}; +use self::libc::{c_int, c_uint, uint8_t}; + extern { fn af_get_version(major: *mut c_int, minor: *mut c_int, patch: *mut c_int) -> c_int; - fn af_info() -> c_int; - fn af_get_device_count(nDevices: *mut c_int) -> c_int; - fn af_get_dbl_support(available: *mut c_int, device: c_int) -> c_int; - fn af_set_device(device: c_int) -> c_int; - + fn af_set_backend(bknd: uint8_t) -> c_int; + fn af_get_backend_count(num_backends: *mut c_uint) -> c_int; + fn af_get_available_backends(backends: *mut c_int) -> c_int; fn af_get_device(device: *mut c_int) -> c_int; - fn af_sync(device: c_int) -> c_int; } @@ -135,3 +133,51 @@ pub fn sync(device: i32) -> Result<(), AfError> { } } } + +/// Toggle backends between cuda, opencl or cpu +/// +/// # Parameters +/// +/// - `backend` to which to switch to +pub fn set_backend(backend: AfBackend) -> Result<(), AfError> { + unsafe { + let err_val = af_set_backend(backend as uint8_t); + match err_val { + 0 => Ok(()), + _ => Err(AfError::from(err_val)), + } + } +} + +/// Get the available backend count +#[allow(unused_mut)] +pub fn get_backend_count() -> Result { + unsafe { + let mut temp: u32 = 0; + let err_val = af_get_backend_count(&mut temp as *mut c_uint); + match err_val { + 0 => Ok(temp), + _ => Err(AfError::from(err_val)), + } + } +} + + +/// Get the available backends +#[allow(unused_mut)] +pub fn get_available_backends() -> Result, AfError> { + unsafe { + let mut temp: i32 = 0; + let err_val = af_get_available_backends(&mut temp as *mut c_int); + match err_val { + 0 => { + let mut b = Vec::new(); + if temp & 0b0100 == 0b0100 { b.push(AfBackend::AF_BACKEND_OPENCL); } + if temp & 0b0010 == 0b0010 { b.push(AfBackend::AF_BACKEND_CUDA); } + if temp & 0b0001 == 0b0001 { b.push(AfBackend::AF_BACKEND_CPU); } + Ok(b) + }, + _ => Err(AfError::from(err_val)), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index aef7e2c32..15e60ada1 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,10 +34,12 @@ pub use data::{reorder, shift, moddims, flat, flip}; pub use data::{select, selectl, selectr, replace, replace_scalar}; mod data; -pub use device::{get_version, info, device_count, is_double_available, set_device, get_device, sync}; +pub use device::{get_version, info, device_count, is_double_available + , set_device, get_device, sync, get_backend_count, set_backend + , get_available_backends}; mod device; -pub use defines::{Aftype, AfError, ColorMap, YCCStd}; +pub use defines::{Aftype, AfError, AfBackend, ColorMap, YCCStd}; pub use defines::{InterpType, BorderType, MatchType, NormType}; pub use defines::{Connectivity, ConvMode, ConvDomain, ColorSpace, MatProp}; mod defines;