Blender  V2.93
util_profiling.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2018 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "util/util_profiling.h"
18 #include "util/util_algorithm.h"
19 #include "util/util_foreach.h"
20 #include "util/util_set.h"
21 
23 
24 Profiler::Profiler() : do_stop_worker(true), worker(NULL)
25 {
26 }
27 
29 {
30  assert(worker == NULL);
31 }
32 
34 {
35  uint64_t updates = 0;
36  auto start_time = std::chrono::system_clock::now();
37  while (!do_stop_worker) {
39  foreach (ProfilingState *state, states) {
40  uint32_t cur_event = state->event;
41  int32_t cur_shader = state->shader;
42  int32_t cur_object = state->object;
43 
44  /* The state reads/writes should be atomic, but just to be sure
45  * check the values for validity anyways. */
46  if (cur_event < PROFILING_NUM_EVENTS) {
47  event_samples[cur_event]++;
48  }
49 
50  if (cur_shader >= 0 && cur_shader < shader_samples.size()) {
51  /* Only consider the active shader during events whose runtime significantly depends on it.
52  */
53  if (((cur_event >= PROFILING_SHADER_EVAL) && (cur_event <= PROFILING_SUBSURFACE)) ||
54  ((cur_event >= PROFILING_CLOSURE_EVAL) &&
55  (cur_event <= PROFILING_CLOSURE_VOLUME_SAMPLE))) {
56  shader_samples[cur_shader]++;
57  }
58  }
59 
60  if (cur_object >= 0 && cur_object < object_samples.size()) {
61  object_samples[cur_object]++;
62  }
63  }
64  lock.unlock();
65 
66  /* Relative waits always overshoot a bit, so just waiting 1ms every
67  * time would cause the sampling to drift over time.
68  * By keeping track of the absolute time, the wait times correct themselves -
69  * if one wait overshoots a lot, the next one will be shorter to compensate. */
70  updates++;
71  std::this_thread::sleep_until(start_time + updates * std::chrono::milliseconds(1));
72  }
73 }
74 
75 void Profiler::reset(int num_shaders, int num_objects)
76 {
77  bool running = (worker != NULL);
78  if (running) {
79  stop();
80  }
81 
82  /* Resize and clear the accumulation vectors. */
83  shader_hits.assign(num_shaders, 0);
84  object_hits.assign(num_objects, 0);
85 
87  shader_samples.assign(num_shaders, 0);
88  object_samples.assign(num_objects, 0);
89 
90  if (running) {
91  start();
92  }
93 }
94 
96 {
97  assert(worker == NULL);
98  do_stop_worker = false;
100 }
101 
103 {
104  if (worker != NULL) {
105  do_stop_worker = true;
106 
107  worker->join();
108  delete worker;
109  worker = NULL;
110  }
111 }
112 
114 {
116 
117  /* Add the ProfilingState from the list of sampled states. */
118  assert(std::find(states.begin(), states.end(), state) == states.end());
119  states.push_back(state);
120 
121  /* Resize thread-local hit counters. */
122  state->shader_hits.assign(shader_hits.size(), 0);
123  state->object_hits.assign(object_hits.size(), 0);
124 
125  /* Initialize the state. */
126  state->event = PROFILING_UNKNOWN;
127  state->shader = -1;
128  state->object = -1;
129  state->active = true;
130 }
131 
133 {
135 
136  /* Remove the ProfilingState from the list of sampled states. */
137  states.erase(std::remove(states.begin(), states.end(), state), states.end());
138  state->active = false;
139 
140  /* Merge thread-local hit counters. */
141  assert(shader_hits.size() == state->shader_hits.size());
142  for (int i = 0; i < shader_hits.size(); i++) {
143  shader_hits[i] += state->shader_hits[i];
144  }
145 
146  assert(object_hits.size() == state->object_hits.size());
147  for (int i = 0; i < object_hits.size(); i++) {
148  object_hits[i] += state->object_hits[i];
149  }
150 }
151 
153 {
154  assert(worker == NULL);
155  return event_samples[event];
156 }
157 
158 bool Profiler::get_shader(int shader, uint64_t &samples, uint64_t &hits)
159 {
160  assert(worker == NULL);
161  if (shader_samples[shader] == 0) {
162  return false;
163  }
164  samples = shader_samples[shader];
165  hits = shader_hits[shader];
166  return true;
167 }
168 
169 bool Profiler::get_object(int object, uint64_t &samples, uint64_t &hits)
170 {
171  assert(worker == NULL);
172  if (object_samples[object] == 0) {
173  return false;
174  }
175  samples = object_samples[object];
176  hits = object_hits[object];
177  return true;
178 }
179 
vector< uint64_t > shader_hits
vector< uint64_t > object_hits
vector< uint64_t > event_samples
uint64_t get_event(ProfilingEvent event)
void add_state(ProfilingState *state)
vector< ProfilingState * > states
volatile bool do_stop_worker
void remove_state(ProfilingState *state)
vector< uint64_t > object_samples
thread_mutex mutex
void start()
bool get_shader(int shader, uint64_t &samples, uint64_t &hits)
void reset(int num_shaders, int num_objects)
thread * worker
bool get_object(int object, uint64_t &samples, uint64_t &hits)
vector< uint64_t > shader_samples
bool join()
Definition: util_thread.cpp:56
#define function_bind
#define CCL_NAMESPACE_END
void KERNEL_FUNCTION_FULL_NAME() shader(KernelGlobals *kg, uint4 *input, float4 *output, int type, int filter, int i, int offset, int sample)
static ulong state[N]
unsigned int uint32_t
Definition: stdint.h:83
signed int int32_t
Definition: stdint.h:80
unsigned __int64 uint64_t
Definition: stdint.h:93
ProfilingEvent
@ PROFILING_CLOSURE_EVAL
@ PROFILING_NUM_EVENTS
@ PROFILING_SHADER_EVAL
@ PROFILING_CLOSURE_VOLUME_SAMPLE
@ PROFILING_SUBSURFACE
@ PROFILING_UNKNOWN
std::unique_lock< std::mutex > thread_scoped_lock
Definition: util_thread.h:41