From 0621759c6c759c8c12ae6554230f57824afcf5c1 Mon Sep 17 00:00:00 2001 From: Jason Ramapuram Date: Tue, 20 Oct 2015 12:55:54 +0200 Subject: [PATCH 1/6] Add unified API support and fix the build mechanism appropriately. Also add a unified example --- Cargo.toml | 6 +++++- README.md | 3 +-- arrayfire | 2 +- build.conf | 7 +------ build.rs | 49 +++++++++++++++++++++++++++------------------ examples/unified.rs | 42 ++++++++++++++++++++++++++++++++++++++ src/defines.rs | 13 ++++++++++++ src/device/mod.rs | 41 +++++++++++++++++++++++++++++-------- src/lib.rs | 4 ++-- 9 files changed, 127 insertions(+), 40 deletions(-) create mode 100644 examples/unified.rs 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..c607f62eb 160000 --- a/arrayfire +++ b/arrayfire @@ -1 +1 @@ -Subproject commit 63807f4421385af94dc77e3332f04a2a64a3b1ed +Subproject commit c607f62eb9eb7b9a316b5cb49d57c4e5cafbafe0 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..f414b2b49 --- /dev/null +++ b/examples/unified.rs @@ -0,0 +1,42 @@ +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 5-by-3 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 err = set_backend(AfBackend::AF_BACKEND_CPU); + match err { + Ok(_) => test_backend(), + Err(e) => println!("CPU backend not available: {}", e), + }; + + let err = set_backend(AfBackend::AF_BACKEND_CUDA); + match err { + Ok(_) => test_backend(), + Err(e) => println!("CUDA backend not available: {}", e), + }; + + let err = set_backend(AfBackend::AF_BACKEND_OPENCL); + match err { + Ok(_) => test_backend(), + Err(e) => println!("OpenCL backend not available: {}", e), + }; +} diff --git a/src/defines.rs b/src/defines.rs index 9f6198ad2..b4ad5b84c 100644 --- a/src/defines.rs +++ b/src/defines.rs @@ -47,6 +47,19 @@ pub enum AfError { ERR_UNKNOWN = 999 } +#[repr(C)] +#[derive(Clone, Copy, Debug)] +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 = 3 +} + 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..2fcb69bb5 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -1,21 +1,18 @@ 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_device(device: *mut c_int) -> c_int; - fn af_sync(device: c_int) -> c_int; } @@ -135,3 +132,31 @@ 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)), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index aef7e2c32..18ac82184 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,10 +34,10 @@ 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}; 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; From 6514984788b4af1b176bb5e219f48f6d3e973146 Mon Sep 17 00:00:00 2001 From: Jason Ramapuram Date: Tue, 20 Oct 2015 12:59:49 +0200 Subject: [PATCH 2/6] fix typo --- examples/unified.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/unified.rs b/examples/unified.rs index f414b2b49..bba9037cf 100644 --- a/examples/unified.rs +++ b/examples/unified.rs @@ -10,7 +10,7 @@ fn test_backend(){ let num_cols: u64 = 10; let dims = Dim4::new(&[num_rows, num_cols, 1, 1]); - println!("Create a 5-by-3 matrix of random floats on the compute device"); + 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), From 03d741530e126a6e0e00564dd126675198cfa93e Mon Sep 17 00:00:00 2001 From: Jason Ramapuram Date: Thu, 22 Oct 2015 15:25:26 +0200 Subject: [PATCH 3/6] fixes in order to determine exact backend need to be ported to rust bindings --- arrayfire | 2 +- src/defines.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arrayfire b/arrayfire index c607f62eb..97bed1593 160000 --- a/arrayfire +++ b/arrayfire @@ -1 +1 @@ -Subproject commit c607f62eb9eb7b9a316b5cb49d57c4e5cafbafe0 +Subproject commit 97bed1593b733308cba6d0c3112a56ff21a93903 diff --git a/src/defines.rs b/src/defines.rs index b4ad5b84c..69a3d4895 100644 --- a/src/defines.rs +++ b/src/defines.rs @@ -57,7 +57,7 @@ pub enum AfBackend { /// CUDA Compute Backend AF_BACKEND_CUDA = 2, /// OpenCL Compute Backend - AF_BACKEND_OPENCL = 3 + AF_BACKEND_OPENCL = 4 } impl Display for AfError { From 328f97156c542a332da86c3d6733efc813af0f1c Mon Sep 17 00:00:00 2001 From: Jason Ramapuram Date: Fri, 23 Oct 2015 11:36:59 +0200 Subject: [PATCH 4/6] add get_available_backends, add display & equality comparison for enum and update example --- examples/unified.rs | 47 ++++++++++++++++++++++++++++----------------- src/defines.rs | 14 +++++++++++++- src/device/mod.rs | 21 ++++++++++++++++++++ src/lib.rs | 4 +++- 4 files changed, 66 insertions(+), 20 deletions(-) diff --git a/examples/unified.rs b/examples/unified.rs index bba9037cf..1fd780b76 100644 --- a/examples/unified.rs +++ b/examples/unified.rs @@ -21,22 +21,33 @@ fn test_backend(){ #[allow(unused_must_use)] fn main() { - println!("There are {} available backends", get_backend_count().unwrap()); - let err = set_backend(AfBackend::AF_BACKEND_CPU); - match err { - Ok(_) => test_backend(), - Err(e) => println!("CPU backend not available: {}", e), - }; - - let err = set_backend(AfBackend::AF_BACKEND_CUDA); - match err { - Ok(_) => test_backend(), - Err(e) => println!("CUDA backend not available: {}", e), - }; - - let err = set_backend(AfBackend::AF_BACKEND_OPENCL); - match err { - Ok(_) => test_backend(), - Err(e) => println!("OpenCL backend not available: {}", e), - }; + 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); + 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); + 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); + match err { + Ok(_) => test_backend(), + Err(e) => println!("OpenCL backend error: {}", e), + }; + } } diff --git a/src/defines.rs b/src/defines.rs index b4ad5b84c..17459e716 100644 --- a/src/defines.rs +++ b/src/defines.rs @@ -48,7 +48,7 @@ pub enum AfError { } #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum AfBackend { /// Default backend order: OpenCL -> CUDA -> CPU AF_BACKEND_DEFAULT = 0, @@ -60,6 +60,18 @@ pub enum AfBackend { AF_BACKEND_OPENCL = 3 } +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 2fcb69bb5..2c3668658 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -12,6 +12,7 @@ extern { 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; } @@ -160,3 +161,23 @@ pub fn get_backend_count() -> Result { } } } + + +/// 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 18ac82184..15e60ada1 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,9 @@ 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, get_backend_count, set_backend}; +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, AfBackend, ColorMap, YCCStd}; From caced4f46088fc6dc7ae45daa14643e6da356e0b Mon Sep 17 00:00:00 2001 From: Jason Ramapuram Date: Fri, 23 Oct 2015 11:53:18 +0200 Subject: [PATCH 5/6] add get device count to each backend for example --- examples/unified.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/unified.rs b/examples/unified.rs index 1fd780b76..21ef8b283 100644 --- a/examples/unified.rs +++ b/examples/unified.rs @@ -26,6 +26,7 @@ fn main() { if available.contains(&AfBackend::AF_BACKEND_CPU){ println!("Evaluating CPU Backend..."); + println!("There are {} CPU compute devices", device_count().unwrap()); let err = set_backend(AfBackend::AF_BACKEND_CPU); match err { Ok(_) => test_backend(), @@ -35,6 +36,7 @@ fn main() { if available.contains(&AfBackend::AF_BACKEND_CUDA){ println!("Evaluating CUDA Backend..."); + println!("There are {} CUDA compute devices", device_count().unwrap()); let err = set_backend(AfBackend::AF_BACKEND_CUDA); match err { Ok(_) => test_backend(), @@ -44,6 +46,7 @@ fn main() { if available.contains(&AfBackend::AF_BACKEND_OPENCL){ println!("Evaluating OpenCL Backend..."); + println!("There are {} OpenCL compute devices", device_count().unwrap()); let err = set_backend(AfBackend::AF_BACKEND_OPENCL); match err { Ok(_) => test_backend(), From 0b325189444eb3f3bdbdee93802716811289a61d Mon Sep 17 00:00:00 2001 From: Jason Ramapuram Date: Fri, 23 Oct 2015 12:07:29 +0200 Subject: [PATCH 6/6] the get backend count should go after the set backend --- examples/unified.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/unified.rs b/examples/unified.rs index 21ef8b283..d01c5723a 100644 --- a/examples/unified.rs +++ b/examples/unified.rs @@ -26,8 +26,8 @@ fn main() { if available.contains(&AfBackend::AF_BACKEND_CPU){ println!("Evaluating CPU Backend..."); - println!("There are {} CPU compute devices", device_count().unwrap()); 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), @@ -36,8 +36,8 @@ fn main() { if available.contains(&AfBackend::AF_BACKEND_CUDA){ println!("Evaluating CUDA Backend..."); - println!("There are {} CUDA compute devices", device_count().unwrap()); 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), @@ -46,8 +46,8 @@ fn main() { if available.contains(&AfBackend::AF_BACKEND_OPENCL){ println!("Evaluating OpenCL Backend..."); - println!("There are {} OpenCL compute devices", device_count().unwrap()); 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),