ocl_algebra/
lib.rs

1extern crate ocl;
2
3mod kernel;
4
5use ocl::{Platform, Device, ProQue, Buffer};
6use ocl::core;
7use ocl::core::{DeviceInfo};
8
9pub struct Context{
10    pub compute_units: u32,
11    pro_que:       ocl::ProQue,
12}
13
14
15pub struct Matrix {
16    pub rows: usize,
17    pub cols: usize,
18    pub data: Vec<f32>,
19}
20
21impl Context {
22    pub fn mul_matrix_scalar(&mut self, matrix: &Matrix, scalar: f32) -> Matrix {
23        let ref mut ocl_pq = self.pro_que;
24        
25        // set work dimension, we can work with 1
26        ocl_pq.set_dims([matrix.rows*matrix.cols]);
27        
28        // copy matrix to device
29        let source_buffer = Buffer::new(
30            &ocl_pq.queue().clone(),
31            Some(core::MEM_READ_WRITE | core::MEM_COPY_HOST_PTR), 
32            ocl_pq.dims().clone(), 
33            Some(&matrix.data)).unwrap();
34        
35        // prepare result matrix
36        let mut result = vec![0.0f32; matrix.cols*matrix.rows];
37        let result_buffer: Buffer<f32> = ocl_pq.create_buffer().unwrap();
38
39        // Create a kernel with arguments corresponding to those in the kernel:
40        let kernel = ocl_pq.create_kernel("mul_matrix_scalar").unwrap()
41            .arg_scl(scalar)
42            .arg_buf(&source_buffer)
43            .arg_buf(&result_buffer);
44
45        // Enqueue kernel:
46        kernel.enq().unwrap(); // send kernel to device and run it
47
48        // Read results from the device into result_buffer's local vector:
49        result_buffer.read(&mut result).enq().unwrap();
50        
51        // return matrix
52        Matrix{rows: matrix.rows,cols: matrix.cols, data: result}
53    }
54    
55    pub fn mul_matrix_matrix(&mut self, matrix_a: &Matrix, matrix_b: &Matrix) -> Matrix {
56        let ref mut ocl_pq = self.pro_que;
57        
58        // set work dimension for matrix_a
59        ocl_pq.set_dims([matrix_a.rows,matrix_a.cols]);
60        
61        // copy matrix_a to device
62        let matrix_a_buffer = Buffer::new(
63            &ocl_pq.queue().clone(),
64            Some(core::MEM_READ_WRITE | core::MEM_COPY_HOST_PTR), 
65            ocl_pq.dims().clone(), 
66            Some(&matrix_a.data)).unwrap();
67
68        // set work dimension for matrix_b
69        ocl_pq.set_dims([matrix_b.rows,matrix_b.cols]);
70        // copy matrix_b to device
71        let matrix_b_buffer = Buffer::new(
72            &ocl_pq.queue().clone(),
73            Some(core::MEM_READ_WRITE | core::MEM_COPY_HOST_PTR), 
74            ocl_pq.dims().clone(), 
75            Some(&matrix_b.data)).unwrap();
76        
77        // set work dimension for result matrix
78        ocl_pq.set_dims([matrix_a.rows,matrix_b.cols]);
79        // prepare result matrix
80        
81        let mut result = vec![0.0f32; matrix_a.rows*matrix_b.cols];
82        let result_buffer: Buffer<f32> = ocl_pq.create_buffer().unwrap();
83
84        // Create a kernel with arguments corresponding to those in the kernel:
85        let kernel = ocl_pq.create_kernel("mul_matrix_matrix").unwrap()
86            .arg_buf(&matrix_a_buffer)
87            .arg_buf(&matrix_b_buffer)
88            .arg_buf(&result_buffer)
89            .arg_scl(matrix_a.rows as i32)
90            .arg_scl(matrix_a.cols as i32)
91            .arg_scl(matrix_b.cols as i32);
92        //panic!("{}",kernel.get_lws().to_len());
93
94        // Enqueue kernel:
95        kernel.enq().unwrap(); // send kernel to device and run it
96
97        // Read results from the device into result_buffer's local vector:
98        result_buffer.read(&mut result).enq().unwrap();
99        
100        // return matrix
101        Matrix{rows: matrix_a.rows, cols: matrix_b.cols, data: result}
102    }
103}
104
105pub fn new() -> Option<Context> {
106    // wich Device should we choose?
107    // the one with the most Compute units!
108    let mut compute_units = 0;
109    let mut ocl_device = None;
110    let platforms = Platform::list();
111    for p_idx in 0..platforms.len() {
112        let platform = &platforms[p_idx];
113        let devices = Device::list_all(platform);
114        for d_idx in 0..devices.len() {
115            let device = devices[d_idx];
116            let deviceinforesult = core::get_device_info(&device, DeviceInfo::MaxComputeUnits);
117            let units = deviceinforesult.to_string().parse().unwrap();
118            if units > compute_units {
119                ocl_device = Some(device);
120                compute_units = units;
121            }
122        }
123    }
124    // something went wrong no, opencl not installed
125    if compute_units == 0 {
126        return None
127    }
128    let que = ProQue::builder()
129              .device(ocl_device.unwrap())
130              .src(kernel::OCL_KERNEL)
131              .build().expect("Build ProQue");
132    Some(Context{
133        compute_units: compute_units,
134        pro_que : que,
135    })
136}
137
138#[test]
139fn single_test() {
140    let eps = 1.0e-6;
141    let mut c = new().unwrap();
142    let m0 = Matrix{rows: 2, cols: 2, data: vec![1.0, 2.0, 3.0, 4.0]};
143    let m1 = Matrix{rows: 2, cols: 1, data: vec![4.0, 5.0]};
144    
145    /* matrix multiplication
146       [1 2] * [4] = [14]
147       [3 4]   [5]   [32] */
148    let m = c.mul_matrix_matrix(&m0,&m1);
149    assert!((m.data[0] - 14.0f32).abs() < eps);
150    assert!((m.data[1] - 32.0f32).abs() < eps);
151    
152    /* matrix scalar multiplication 
153       [1 2] * 1.5 = [1.5 3]
154       [3 4]         [4.5 6]*/
155    let m = c.mul_matrix_scalar(&m0,1.5);
156    assert!( (m.data[0] - 1.5f32) < eps);
157    assert!( (m.data[1] - 3.0f32) < eps);
158    assert!( (m.data[2] - 4.5f32) < eps);
159    assert!( (m.data[3] - 6.0f32) < eps);
160}