Skip CBLAS DSP initializtion if OFFLOAD is disabled.
[dense-linear-algebra-libraries/linalg.git] / src / ti / linalg / blasblisacc / src / ti_cblas_initfini.c
1 /******************************************************************************
2  * Copyright (c) 2013-2015, Texas Instruments Incorporated - http://www.ti.com/
3  *   All rights reserved.
4  *
5  *   Redistribution and use in source and binary forms, with or without
6  *   modification, are permitted provided that the following conditions are met:
7  *       * Redistributions of source code must retain the above copyright
8  *         notice, this list of conditions and the following disclaimer.
9  *       * Redistributions in binary form must reproduce the above copyright
10  *         notice, this list of conditions and the following disclaimer in the
11  *         documentation and/or other materials provided with the distribution.
12  *       * Neither the name of Texas Instruments Incorporated nor the
13  *         names of its contributors may be used to endorse or promote products
14  *         derived from this software without specific prior written permission.
15  *
16  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20  *   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26  *   THE POSSIBILITY OF SUCH DAMAGE.
27  *****************************************************************************/ 
28 #include "ti_cblas_acc.h"
29 #include "../../ticblas/ticblas.h"
30 #include <pthread.h>
32 #ifdef TI_CBLAS_FAT_BINARY
33 #include "ti_cblas_kernel.dsp_h"
34 #endif
36 /*==============================================================================
37  * This file contains functions of the ARM wrapper of ARM+DSP CBLAS library.
38  * It has the initialization and finalization routines. 
39  *
40  * The standard CBLAS API for each BLAS function can be found in file 
41  * ti_cblas_cblas_<func_name>.c, such as ti_cblas_cblas_dgemm.c for DGEMM. 
42  *============================================================================*/
44 #define TI_CBLAS_INITFINI_SUCCESS   0
45 #define TI_CBLAS_INITFINI_OCL_ERR   1
46 #define TI_CBLAS_INITFINI_BLI_ERR   2
48 /* Global variables */
49 Context*             ti_cblas_ocl_context = NULL;
50 std::vector<Device>* ti_cblas_ocl_devices = NULL;
51 CommandQueue*        ti_cblas_ocl_Q       = NULL;
52 Program::Binaries*   ti_cblas_ocl_binary  = NULL;
53 Program*             ti_cblas_ocl_program = NULL;
55 int ti_cblas_init_done = 0;       /* flag to check if init is complete */
56 int ti_cblas_disable_debug = 0;   /* runtime toggle to disable debug */
57 int ti_cblas_offload = TI_CBLAS_OFFLOAD_SIZE;  
58 int TI_CBLAS_L1_OFFLOAD = TI_CBLAS_OFFLOAD_NONE;
59 int TI_CBLAS_L2_OFFLOAD = TI_CBLAS_OFFLOAD_NONE;
60 int TI_CBLAS_L3_OFFLOAD = TI_CBLAS_OFFLOAD_NONE;
62 pthread_cond_t   CV;
63 pthread_mutex_t  MUTEX;
65 /*============================================================================
66  * Function purpose: report error encoutered in ARM wrapper code.
67  *============================================================================*/
68 void ti_cblas_error(const char* msg, int code)
69 {
70   fprintf(stderr, "TI CBLAS wrapper ERROR: (%s,%d)\n", msg, code);
71 }
73 /*============================================================================
74  * Function purpose: initialize BLIS on both ARM and DSP
75  *============================================================================*/
76 int ti_blis_init(void)
77 {
78     int r_val = TI_CBLAS_INITFINI_SUCCESS;
79     
80     /* Initialize BLIS on ARM */
81     TI_CBLAS_DEBUG_PRINT("Initializing BLIS on ARM...\n");   
82     bli_init();    
83     TI_CBLAS_DEBUG_PRINT("BLIS initialized on ARM.\n");
85     /* Initialize BLIS on DSP by offloading bli_init() on DSP */
86     TI_CBLAS_DEBUG_PRINT("Initializing BLIS on DSP...\n");   
87     Event e;
88     Kernel* __K;
90     __K =  ti_cblas_get_kernel("ocl_bli_init");
91     try
92     {
93         int err_code;        
94         Buffer buf_err(*ti_cblas_ocl_context, CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR, sizeof(int), &err_code);
95         __K->setArg(0, buf_err);
97         ti_cblas_ocl_Q->enqueueTask(*__K, 0, &e);
98         e.wait();
100         if(err_code != TICBLAS_SUCCESS) {
101           TI_CBLAS_DEBUG_PRINT("Error in offloaded ocl_bli_init with error code %d!\n", err_code);
102           r_val = TI_CBLAS_INITFINI_BLI_ERR;
103         }
104         
105         ti_cblas_delete_kernel(__K);
106         TI_CBLAS_DEBUG_PRINT("BLIS DSP initialization finished.\n");        
107     }
109     catch (Error err)
110     {
111         ti_cblas_delete_kernel(__K);
112         ti_cblas_error(err.what(),err.err());
113         r_val = TI_CBLAS_INITFINI_OCL_ERR;
114     }
116     return r_val;
117 } // ti_blis_init 
120 /*============================================================================
121  * Function purpose: initialize and prepare for CBLAS calls:
122  *    - parse the environment variables 
123  *    - initialize OpenCL 
124  *    - initialize BLIS
125  *
126  * Note: this function is invoked exactly once on startup, when any CBLAS function
127  *       is called the first time. 
128  *============================================================================*/
129 void ti_cblas_init(void)
131     /* Add code for interception */
132 #ifdef TI_CBLAS_DEBUG
133       char *no_debug_env = getenv("TI_CBLAS_NO_DEBUG");
134       if (no_debug_env) {
135         if (atoi(no_debug_env) > 0) {
136           ti_cblas_disable_debug = 1;
137         }
138       }
139 #endif
140     
141       TI_CBLAS_DEBUG_PRINT("ti_cblas_init: Initializing OpenCL on first use..\n");
143       TI_CBLAS_PROFILE_START();
144     
145       /* check environment variables */
146       const char *offload_env = getenv("TI_CBLAS_OFFLOAD");
147       if (!offload_env)  { 
148         TI_CBLAS_DEBUG_PRINT("Using build time default for offload: TI_CBLAS_OFFLOAD=%s\n", TI_CBLAS_OFFLOAD_DEF);
149         offload_env = TI_CBLAS_OFFLOAD_DEF;
150       }
151       else {
152         TI_CBLAS_DEBUG_PRINT("Using runtime override for offloads: TI_CBLAS_OFFLOAD=%s\n", offload_env);
153       }
154       if (offload_env) {
155         ti_cblas_offload = atoi(offload_env);
156         if (ti_cblas_offload == TI_CBLAS_OFFLOAD_NONE) {
157             TI_CBLAS_DEBUG_PRINT("Disabling all offloads\n");
158             return;
159         }
160       }
161   
162 #pragma omp critical (ti_cblas_init_critical)
163   {
164       /* 3-digit value: 012
165        * Left-most digit => L1 (0)
166        * Middle-digit    => L2 (1)
167        * Right-most      => L3 (2)
168        */
169       TI_CBLAS_L1_OFFLOAD = ti_cblas_offload / 100;
170       int tmp_offload = ti_cblas_offload % 100;
171       TI_CBLAS_L2_OFFLOAD = tmp_offload / 10;
172       TI_CBLAS_L3_OFFLOAD = tmp_offload % 10;
173       TI_CBLAS_DEBUG_PRINT("BLAS Offload values: L1=%d, L2=%d, L3=%d\n",  
174       TI_CBLAS_L1_OFFLOAD, TI_CBLAS_L2_OFFLOAD, TI_CBLAS_L3_OFFLOAD);
175       if ((TI_CBLAS_L1_OFFLOAD == TI_CBLAS_OFFLOAD_SIZE)) {
176         TI_CBLAS_ERROR_EXIT("Size-based offload NOT supported for BLAS Level 1 yet.\n");
177       }
178       if ((TI_CBLAS_L2_OFFLOAD == TI_CBLAS_OFFLOAD_SIZE)) {
179         TI_CBLAS_ERROR_EXIT("Size-based offload NOT supported for BLAS Level 2 yet.\n");
180       }
182       /*------------------------------------------------------------------------
183        * Read the offline compiled kernel module
184        *-----------------------------------------------------------------------*/
185       TI_CBLAS_DEBUG_PRINT("Reading Kernels\n");
186       const unsigned char* bin;
187 #ifdef TI_CBLAS_FAT_BINARY
188       bin = (unsigned char *)ti_cblas_kernel_dsp_bin;
189       const size_t bin_length = ti_cblas_kernel_dsp_bin_len;
190 #else
191       const char binary[] = "./ti_cblas_kernel.out";
192       unsigned int bin_length;
193       bin_length = ocl_read_binary(binary, (char*&)bin); 
194 #endif /* FAT_BINARY */
195     
196       /* OpenCL init */
197       TI_CBLAS_DEBUG_PRINT("Initializing OpenCL\n");
198       ti_cblas_ocl_context = new Context(CL_DEVICE_TYPE_ACCELERATOR);
199       ti_cblas_ocl_devices = new std::vector<Device> (ti_cblas_ocl_context->getInfo<CL_CONTEXT_DEVICES>());
200       ti_cblas_ocl_binary = new Program::Binaries(1, std::make_pair(bin, bin_length));
201       ti_cblas_ocl_program = new Program(*ti_cblas_ocl_context, *ti_cblas_ocl_devices, *ti_cblas_ocl_binary);
202       ti_cblas_ocl_program->build(*ti_cblas_ocl_devices);
203       ti_cblas_ocl_Q = new CommandQueue(*ti_cblas_ocl_context, ti_cblas_ocl_devices[0][0], CL_QUEUE_PROFILING_ENABLE);
204     
205 #ifndef TI_CBLAS_FAT_BINARY
206       delete [] bin;
207 #endif /* FAT_BINARY */
208       
209       TI_CBLAS_DEBUG_PRINT("OpenCL initialized\n");
211       /* Initializing pthreads */
212       TI_CBLAS_DEBUG_PRINT("Initializing Pthreads\n");
213       pthread_cond_init (&CV, 0);
214       pthread_mutex_init(&MUTEX, 0);
215       TI_CBLAS_DEBUG_PRINT("Pthreads initialized\n");
216       TI_CBLAS_DEBUG_PRINT("Initializing BLIS\n");
217       if(ti_blis_init() == TI_CBLAS_INITFINI_SUCCESS) {
218         TI_CBLAS_DEBUG_PRINT("BLIS initialized\n");\
219       }
220       else {
221         TI_CBLAS_DEBUG_PRINT("BLIS NOT initialized!\n");\
222       }
223       
224       /* Register auto finalization to be called when program exits */
225       atexit(ti_cblas_auto_finalize);
227       TI_CBLAS_PROFILE_REPORT("Initialization took %8.2f us\n", (float) clock_diff);
228       ti_cblas_init_done = 1;
229       TI_CBLAS_DEBUG_PRINT("ti_cblas_init: Finished initialization\n");
230       
231   } // End of critical section
233   return;
234 } //ti_cblas_init
236 /*============================================================================
237  * Function purpose: finalize BLIS on both ARM and DSP
238  *============================================================================*/
239 int ti_blis_finalize(void)
241     int r_val = TI_CBLAS_INITFINI_SUCCESS;
242     
243     /* Finalize BLIS on ARM */
244     TI_CBLAS_DEBUG_PRINT("Finalizing BLIS on ARM...\n");
245     bli_finalize();
246     TI_CBLAS_DEBUG_PRINT("BLIS finalized on ARM.\n");
248     /* Finalize BLIS on DSP */
249     Event e;
250     Kernel* __K;
252     __K =  ti_cblas_get_kernel("ocl_bli_finalize");
254     TI_CBLAS_DEBUG_PRINT("Finalizing BLIS on DSP...\n");
255     int err_code;        
256     Buffer buf_err(*ti_cblas_ocl_context, CL_MEM_READ_WRITE|CL_MEM_USE_HOST_PTR, sizeof(int), &err_code);
257     __K->setArg(0, buf_err);
258     
259     try
260     {
261         ti_cblas_ocl_Q->enqueueTask(*__K, 0, &e);
262         e.wait();
264         if(err_code != TICBLAS_SUCCESS) {
265           TI_CBLAS_DEBUG_PRINT("Error in offloaded ocl_bli_finalize with error code %d!\n", err_code);
266           r_val = TI_CBLAS_INITFINI_BLI_ERR;
267         }
268         
269         ti_cblas_delete_kernel(__K);
270     }
272     catch (Error err)
273     {
274         ti_cblas_error(err.what(),err.err());
275         r_val = TI_CBLAS_INITFINI_OCL_ERR;
276     }
278     return r_val;
279 } // ti_blis_finalize 
281 /*============================================================================
282  * Function purpose: finalize after all CBLAS calls:
283  *    - finalize BLIS
284  *    - delete OpenCL context
285  *
286  * Note: this function is invoked exactly once on program exit.
287  *============================================================================*/
288 int ti_cblas_finalize(void)
290     /* If ti_cblas_init_done is equal to 0,
291      * then we know that ti_cblas_init was not called,
292      * and so we can return early.
293      */
294     if(ti_cblas_init_done == 0) {
295         return TI_CBLAS_INITFINI_SUCCESS;
296     }
298     int r_val = ti_blis_finalize();
299     /*Using same name as ti_cblas_init critical region. See notes in bli_init*/
300 #pragma omp critical (ti_cblas_init_critical)
301     {
302         // Destroy Pthread
303         pthread_mutex_destroy(&MUTEX);
304         pthread_cond_destroy (&CV);
306         //destroy Command queue, program, devices and context.
307         if(ti_cblas_ocl_Q != NULL)
308         {
309             delete(ti_cblas_ocl_Q);
310             ti_cblas_ocl_Q = NULL;
311         }
312         if(ti_cblas_ocl_program != NULL)
313         {
314             delete(ti_cblas_ocl_program);
315             ti_cblas_ocl_program = NULL;
316         }
317         if(ti_cblas_ocl_binary != NULL)
318         {
319             delete(ti_cblas_ocl_binary);
320             ti_cblas_ocl_binary = NULL;
321         }
322         if(ti_cblas_ocl_devices != NULL)
323         {
324             delete(ti_cblas_ocl_devices);
325             ti_cblas_ocl_devices = NULL;
326         }
327         if(ti_cblas_ocl_context != NULL)
328         {
329             delete(ti_cblas_ocl_context);
330             ti_cblas_ocl_context = NULL;
331         }
332     }
333     
334     return r_val;
335 } // ti_cblas_finalize
338 /*============================================================================
339  * Function purpose: auto-finalize on program exit.
340  *============================================================================*/
341 void ti_cblas_auto_finalize(void)
343     int r_val;
345     r_val = ti_cblas_finalize();
346     if (r_val != TI_CBLAS_INITFINI_SUCCESS)
347     {
348         fprintf(stderr, "Error: ti_cblas_finalize failed with error code %d!\n", r_val);
349     }
350 } //ti_cblas_auto_finalize
353 /*============================================================================
354  * Function purpose: free previously allocated MSMC memory
355  *============================================================================*/
356 void ti_cblas_mem_free(void *ptr)
358     pthread_mutex_lock(&MUTEX);
359     __free_msmc(ptr);
360     pthread_cond_broadcast(&CV);
361     pthread_mutex_unlock(&MUTEX);
365 /*============================================================================
366  * Function purpose: allocate MSMC memory
367  *============================================================================*/
368 void *ti_cblas_mem_alloc(size_t size)
370     void *ptr;
371     pthread_mutex_lock(&MUTEX);
372     
373     /*-------------------------------------------------------------------------
374      * Loop in case of false signal after broadcast.
375      *------------------------------------------------------------------------*/
376     while ((ptr = __malloc_msmc(size)) == 0)
377     {
378         pthread_cond_wait(&CV, &MUTEX);
379     }
381     pthread_mutex_unlock(&MUTEX);
382     
383     return ptr;
384 } //ti_cblas_mem_alloc
387 /*============================================================================
388  * Function purpose: create an OpenCL kernel
389  *============================================================================*/
390 Kernel *ti_cblas_get_kernel(const char *fname)
392     Kernel* __K;
393     
394     __K = new Kernel(*ti_cblas_ocl_program, fname);
396     return __K;
399 /*============================================================================
400  * Function purpose: delete an OpenCL kernel
401  *============================================================================*/
402 int ti_cblas_delete_kernel(Kernel* K)
404     if(K != NULL)
405     {
406         delete(K);
407         K=NULL;
408     }
409     
410     return 0;
413 /* Nothing after this line */