1 /***************************************************************************
2                                  ocl_timer.h
3                              -------------------
4                                W. Michael Brown
5 
6   Class for timing OpenCL routines
7 
8  __________________________________________________________________________
9     This file is part of the Geryon Unified Coprocessor Library (UCL)
10  __________________________________________________________________________
11 
12     begin                : Jan Fri 22 2010
13     copyright            : (C) 2010 by W. Michael Brown
14     email                : brownw@ornl.gov
15  ***************************************************************************/
16 
17 /* -----------------------------------------------------------------------
18    Copyright (2010) Sandia Corporation.  Under the terms of Contract
19    DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
20    certain rights in this software.  This software is distributed under
21    the Simplified BSD License.
22    ----------------------------------------------------------------------- */
23 
24 #ifndef OCL_TIMER_H
25 #define OCL_TIMER_H
26 
27 #include "ocl_macros.h"
28 #include "ocl_device.h"
29 
30 #ifdef CL_VERSION_1_2
31 #define UCL_OCL_MARKER(cq,event) clEnqueueMarkerWithWaitList(cq,0,nullptr,event)
32 #else
33 #define UCL_OCL_MARKER clEnqueueMarker
34 #endif
35 
36 namespace ucl_opencl {
37 
38 /// Class for timing OpenCL events
39 class UCL_Timer {
40  public:
UCL_Timer()41   inline UCL_Timer() : start_event(nullptr), stop_event(nullptr), _total_time(0.0f),
42                        _initialized(false), has_measured_time(false) { }
UCL_Timer(UCL_Device & dev)43   inline UCL_Timer(UCL_Device &dev) : start_event(nullptr), stop_event(nullptr), _total_time(0.0f),
44                                       _initialized(false), has_measured_time(false)
45     { init(dev); }
46 
~UCL_Timer()47   inline ~UCL_Timer() { clear(); }
48 
49   /// Clear any data associated with timer
50   /** \note init() must be called to reuse timer after a clear() **/
clear()51   inline void clear() {
52     if (_initialized) {
53       CL_DESTRUCT_CALL(clReleaseCommandQueue(_cq));
54       _initialized=false;
55       _total_time=0.0;
56     }
57     has_measured_time = false;
58   }
59 
60   /// Initialize default command queue for timing
init(UCL_Device & dev)61   inline void init(UCL_Device &dev) { init(dev,dev.cq()); }
62 
63   /// Initialize command queue for timing
init(UCL_Device & dev,command_queue & cq)64   inline void init(UCL_Device &dev, command_queue &cq) {
65     clear();
66     _cq=cq;
67     clRetainCommandQueue(_cq);
68     _initialized=true;
69     has_measured_time = false;
70   }
71 
72   /// Start timing on default command queue
start()73   inline void start() {
74     UCL_OCL_MARKER(_cq,&start_event);
75     has_measured_time = false;
76   }
77 
78   /// Stop timing on default command queue
stop()79   inline void stop() {
80     UCL_OCL_MARKER(_cq,&stop_event);
81     has_measured_time = true;
82   }
83 
84   /// Block until the start event has been reached on device
sync_start()85   inline void sync_start() {
86     CL_SAFE_CALL(clWaitForEvents(1,&start_event));
87     has_measured_time = false;
88   }
89 
90   /// Block until the stop event has been reached on device
sync_stop()91   inline void sync_stop() {
92     CL_SAFE_CALL(clWaitForEvents(1,&stop_event));
93     has_measured_time = true;
94   }
95 
96   /// Set the time elapsed to zero (not the total_time)
zero()97   inline void zero() {
98     has_measured_time = false;
99     UCL_OCL_MARKER(_cq,&start_event);
100     UCL_OCL_MARKER(_cq,&stop_event);
101   }
102 
103   /// Set the total time to zero
zero_total()104   inline void zero_total() { _total_time=0.0; }
105 
106   /// Add time from previous start and stop to total
107   /** Forces synchronization **/
add_to_total()108   inline double add_to_total()
109     { double t=time(); _total_time+=t; return t/1000.0; }
110 
111   /// Add a user specified time to the total (ms)
add_time_to_total(const double t)112   inline void add_time_to_total(const double t) { _total_time+=t; }
113 
114   /// Return the time (ms) of last start to stop - Forces synchronization
time()115   inline double time() {
116     if(!has_measured_time) return 0.0;
117     cl_ulong tstart,tend;
118     CL_SAFE_CALL(clWaitForEvents(1,&stop_event));
119     CL_SAFE_CALL(clGetEventProfilingInfo(stop_event,
120                                          CL_PROFILING_COMMAND_START,
121                                          sizeof(cl_ulong), &tend, nullptr));
122     CL_SAFE_CALL(clGetEventProfilingInfo(start_event,
123                                          CL_PROFILING_COMMAND_END,
124                                          sizeof(cl_ulong), &tstart, nullptr));
125     clReleaseEvent(start_event);
126     clReleaseEvent(stop_event);
127     has_measured_time = false;
128     return (tend-tstart)*1e-6;
129   }
130 
131   /// Return the time (s) of last start to stop - Forces synchronization
seconds()132   inline double seconds() { return time()*1e-3; }
133 
134   /// Return the total time in ms
total_time()135   inline double total_time() { return _total_time; }
136 
137   /// Return the total time in seconds
total_seconds()138   inline double total_seconds() { return _total_time*1e-3; }
139 
140  private:
141   cl_event start_event, stop_event;
142   cl_command_queue _cq;
143   double _total_time;
144   bool _initialized;
145   bool has_measured_time;
146 };
147 
148 } // namespace
149 
150 #endif
151