1 /*
2 * Copyright (c) 2013-2021, The OSKAR Developers.
3 * See the LICENSE file at the top-level directory of this distribution.
4 */
5
6 #include "utility/oskar_device.h"
7 #include "utility/oskar_timer.h"
8 #include "utility/oskar_thread.h"
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <time.h>
12
13 #ifndef OSKAR_OS_WIN
14 #include <sys/time.h>
15 #include <unistd.h>
16 #else
17 #include <windows.h>
18 #endif
19
20 #ifdef OSKAR_HAVE_CUDA
21 #include <cuda_runtime_api.h>
22 #endif
23
24 #ifdef OSKAR_HAVE_OPENCL
25 #ifdef __APPLE__
26 #include <OpenCL/opencl.h>
27 #else
28 #include <CL/cl.h>
29 #endif
30 #endif
31
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35
36 struct oskar_Timer
37 {
38 oskar_Mutex* mutex;
39 #ifdef OSKAR_HAVE_CUDA
40 cudaEvent_t start_cuda, end_cuda;
41 #endif
42 double start, elapsed;
43 #ifdef OSKAR_OS_WIN
44 double freq;
45 #endif
46 int type, paused;
47 };
48
oskar_get_wtime(oskar_Timer * timer)49 static double oskar_get_wtime(oskar_Timer* timer)
50 {
51 #if defined(OSKAR_OS_WIN)
52 /* Windows-specific version. */
53 LARGE_INTEGER cntr;
54 QueryPerformanceCounter(&cntr);
55 return (double)(cntr.QuadPart) / timer->freq;
56 #elif _POSIX_MONOTONIC_CLOCK > 0
57 /* Use monotonic clock if available. */
58 struct timespec ts;
59 (void)timer;
60 clock_gettime(CLOCK_MONOTONIC, &ts);
61 return ts.tv_sec + ts.tv_nsec / 1e9;
62 #else
63 /* Use gettimeofday() as fallback. */
64 struct timeval tv;
65 (void)timer;
66 gettimeofday(&tv, 0);
67 return tv.tv_sec + tv.tv_usec / 1e6;
68 #endif
69 }
70
oskar_timer_create(int type)71 oskar_Timer* oskar_timer_create(int type)
72 {
73 oskar_Timer* timer = 0;
74 #ifdef OSKAR_OS_WIN
75 LARGE_INTEGER freq;
76 #endif
77 timer = (oskar_Timer*) calloc(1, sizeof(oskar_Timer));
78 timer->mutex = oskar_mutex_create();
79 #ifdef OSKAR_OS_WIN
80 QueryPerformanceFrequency(&freq);
81 timer->freq = (double)(freq.QuadPart);
82 #endif
83 timer->type = type;
84 timer->paused = 1;
85 #ifdef OSKAR_HAVE_CUDA
86 if (timer->type == OSKAR_TIMER_CUDA)
87 {
88 cudaEventCreate(&timer->start_cuda);
89 cudaEventCreate(&timer->end_cuda);
90 }
91 #endif
92 return timer;
93 }
94
oskar_timer_free(oskar_Timer * timer)95 void oskar_timer_free(oskar_Timer* timer)
96 {
97 if (!timer) return;
98 #ifdef OSKAR_HAVE_CUDA
99 if (timer->type == OSKAR_TIMER_CUDA)
100 {
101 cudaEventDestroy(timer->start_cuda);
102 cudaEventDestroy(timer->end_cuda);
103 }
104 #endif
105 oskar_mutex_free(timer->mutex);
106 free(timer);
107 }
108
oskar_timer_elapsed(oskar_Timer * timer)109 double oskar_timer_elapsed(oskar_Timer* timer)
110 {
111 /* If timer is paused, return immediately with current elapsed time. */
112 if (timer->paused) return timer->elapsed;
113
114 #ifdef OSKAR_HAVE_CUDA
115 if (timer->type == OSKAR_TIMER_CUDA)
116 {
117 float millisec = 0.0f;
118 oskar_mutex_lock(timer->mutex);
119
120 /* Get elapsed time since start. */
121 cudaEventRecord(timer->end_cuda, 0);
122 cudaEventSynchronize(timer->end_cuda);
123 cudaEventElapsedTime(&millisec, timer->start_cuda, timer->end_cuda);
124
125 /* Increment elapsed time and restart. */
126 timer->elapsed += millisec / 1000.0;
127 cudaEventRecord(timer->start_cuda, 0);
128 }
129 else
130 #endif
131 #ifdef OSKAR_HAVE_OPENCL
132 if (timer->type == OSKAR_TIMER_CL)
133 {
134 /* Get elapsed time since start. */
135 /* Note that clGetEventProfilingInfo() seems to be broken
136 * in various ways, at least on macOS. So just enqueue a marker,
137 * wait on its event and use the native timer instead. */
138 cl_event event = 0;
139 clEnqueueMarkerWithWaitList(oskar_device_queue_cl(), 0, NULL, &event);
140 clWaitForEvents(1, &event);
141
142 /* Increment elapsed time and restart. */
143 oskar_mutex_lock(timer->mutex);
144 const double now = oskar_get_wtime(timer);
145 timer->elapsed += (now - timer->start);
146 timer->start = now;
147 }
148 else
149 #endif
150 {
151 oskar_mutex_lock(timer->mutex);
152 const double now = oskar_get_wtime(timer);
153
154 /* Increment elapsed time and restart. */
155 timer->elapsed += (now - timer->start);
156 timer->start = now;
157 }
158 oskar_mutex_unlock(timer->mutex);
159 return timer->elapsed;
160 }
161
oskar_timer_pause(oskar_Timer * timer)162 void oskar_timer_pause(oskar_Timer* timer)
163 {
164 if (timer->paused) return;
165 (void)oskar_timer_elapsed(timer);
166 timer->paused = 1;
167 }
168
oskar_timer_reset(oskar_Timer * timer)169 void oskar_timer_reset(oskar_Timer* timer)
170 {
171 oskar_mutex_lock(timer->mutex);
172 timer->paused = 1;
173 timer->start = 0.0;
174 timer->elapsed = 0.0;
175 oskar_mutex_unlock(timer->mutex);
176 }
177
oskar_timer_resume(oskar_Timer * timer)178 void oskar_timer_resume(oskar_Timer* timer)
179 {
180 if (!timer->paused) return;
181 oskar_timer_restart(timer);
182 }
183
oskar_timer_restart(oskar_Timer * timer)184 void oskar_timer_restart(oskar_Timer* timer)
185 {
186 timer->paused = 0;
187 #ifdef OSKAR_HAVE_CUDA
188 if (timer->type == OSKAR_TIMER_CUDA)
189 {
190 cudaEventRecord(timer->start_cuda, 0);
191 return;
192 }
193 #endif
194 timer->start = oskar_get_wtime(timer);
195 }
196
oskar_timer_start(oskar_Timer * timer)197 void oskar_timer_start(oskar_Timer* timer)
198 {
199 timer->elapsed = 0.0;
200 oskar_timer_restart(timer);
201 }
202
203 #ifdef __cplusplus
204 }
205 #endif
206