blob: 8c31c55da8c5afef74fa597221e4b7faa13491df [file] [log] [blame]
/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "tensorflow/core/profiler/utils/event_span.h"
#include <thread> // NOLINT
#include <vector>
#include "absl/strings/match.h"
namespace tensorflow {
namespace profiler {
namespace {
// Returns the disjoint timespans from the given possibly overlapped events. The
// timespans are sorted in the begin time.
std::vector<Timespan> MakeDisjointTimespans(
const std::vector<EventTypeSpan>& overlapped_events) {
std::set<uint64> boundary_times; // uses a set to eliminate duplicated times.
for (const auto& event : overlapped_events) {
boundary_times.insert(event.span.begin_ps());
boundary_times.insert(event.span.end_ps());
}
uint64 prev_time;
bool is_first = true;
std::vector<Timespan> timespans;
for (const auto current_time : boundary_times) {
// current_time will be in ascending order for std::set.
if (is_first) {
is_first = false;
} else {
timespans.push_back(Timespan::FromEndPoints(prev_time, current_time));
}
prev_time = current_time;
}
return timespans;
}
// Assigns an event type to the given timespan. It is the type of the
// event that has the highest preference among all events that include this
// timespan.
EventType AssignEventType(
const Timespan& timespan,
const std::vector<EventTypeSpan>& sorted_overlapped_events) {
EventType event_type = UNKNOWN_TIME;
for (const auto& event : sorted_overlapped_events) {
if (timespan.end_ps() < event.span.begin_ps()) {
// Because sorted_overlapped_events is sorted in the event's begin time,
// we are sure that timespan won't overlap with the rest of events.
break;
}
if (!event.span.Includes(timespan)) continue;
event_type = std::max(event_type, event.type);
}
return event_type;
}
// Compares two EventTypeSpans using their timespans.
bool CmpStartTime(const EventTypeSpan& a, const EventTypeSpan& b) {
return a.span < b.span;
}
// Returns the EventTypeSpans corresponding to the given disjoint timespans.
std::vector<EventTypeSpan> AssignTypesToDisjointTimespans(
const std::vector<Timespan>& disjoint_timespans,
const std::vector<EventTypeSpan>& overlapped_events) {
std::vector<EventTypeSpan> sorted_overlapped_events = overlapped_events;
absl::c_sort(sorted_overlapped_events, CmpStartTime);
std::vector<EventTypeSpan> non_overlapped_events;
non_overlapped_events.reserve(disjoint_timespans.size());
for (const auto& timespan : disjoint_timespans) {
EventType event_type = AssignEventType(timespan, sorted_overlapped_events);
non_overlapped_events.push_back({event_type, timespan});
}
return non_overlapped_events;
}
// Converts from overlapped events to non-overlapped events.
std::vector<EventTypeSpan> ToNonOverlappedEvents(
const std::vector<EventTypeSpan>& overlapped_events) {
std::vector<Timespan> disjoint_timespans =
MakeDisjointTimespans(overlapped_events);
std::vector<EventTypeSpan> non_overlapped_events =
AssignTypesToDisjointTimespans(disjoint_timespans, overlapped_events);
return non_overlapped_events;
}
void CombineStepDetails(const StepDetails& src, StepDetails* dst) {
dst->AppendMarkers(src.Markers());
dst->AppendEvents(src.Events());
}
} // namespace.
EventType ClassifyGpuEvent(absl::string_view event_name) {
if (absl::StartsWithIgnoreCase(event_name, "MEMCPYHtoD"))
return HOST_TO_DEVICE;
if (absl::StartsWithIgnoreCase(event_name, "MEMCPYDtoH"))
return DEVICE_TO_HOST;
if (absl::StartsWithIgnoreCase(event_name, "MEMCPYDtoD"))
return DEVICE_TO_DEVICE;
return DEVICE_COMPUTE;
}
EventType ClassifyCpuEvent(absl::string_view event_name, int64 correlation_id) {
if (absl::StartsWithIgnoreCase(event_name, "MEMCPYHtoD"))
return HOST_TO_DEVICE;
if (absl::StartsWithIgnoreCase(event_name, "MEMCPYHtoH")) return HOST_TO_HOST;
if (correlation_id >= 0 ||
absl::StartsWithIgnoreCase(event_name, "ExecutorState::Process")) {
return HOST_PREPARE;
} else {
if (absl::StartsWithIgnoreCase(event_name, "IteratorGetNext"))
return HOST_WAIT_INPUT;
return HOST_COMPUTE;
}
}
std::string PrintEventType(EventType event_type) {
switch (event_type) {
case UNKNOWN_TIME:
return "unknown_time";
case HOST_COMPUTE:
return "host_compute";
case HOST_COMPILE:
return "host_compile";
case HOST_TO_HOST:
return "host_to_host";
case HOST_TO_DEVICE:
return "host_to_device";
case HOST_PREPARE:
return "host_prepare";
case HOST_WAIT_INPUT:
return "host_wait_input";
case DEVICE_TO_DEVICE:
return "device_to_device";
case DEVICE_TO_HOST:
return "device_to_host";
case DEVICE_COMPUTE:
return "device_compute";
case DEVICE_WAIT_DEVICE:
return "device_wait_device";
case DEVICE_WAIT_HOST:
return "device_wait_host";
default:
return "unexpected";
}
}
void CombineStepEvents(const StepEvents& src, StepEvents* dst) {
for (const auto& step_details : src) {
int64 step_id = step_details.first;
const StepDetails& src_details = step_details.second;
StepDetails* dst_details = &(*dst)[step_id];
CombineStepDetails(src_details, dst_details);
}
}
// Converts from overlapped step-events to non-overlapped step-events.
StepEvents ToNonOverlappedStepEvents(const StepEvents& overlapped_step_events) {
size_t num_steps = overlapped_step_events.size();
std::vector<std::thread> workers;
workers.resize(num_steps);
std::vector<int64> step_ids;
step_ids.resize(num_steps);
std::vector<std::vector<EventTypeSpan>> non_overlapped_events_per_worker;
non_overlapped_events_per_worker.resize(num_steps);
StepEvents non_overlapped_step_events;
int64 i = 0;
// Sets up 1 worker per step to convert overlapped events to non-overlapped
// events.
for (const auto& step_events : overlapped_step_events) {
step_ids[i] = step_events.first;
const auto& step_details = step_events.second;
*non_overlapped_step_events[step_ids[i]].MutableMarkers() =
step_details.Markers();
const std::vector<EventTypeSpan>* overlapped_events =
&step_details.Events();
std::vector<EventTypeSpan>* non_overlapped_events =
&non_overlapped_events_per_worker[i];
workers[i] = std::thread([overlapped_events, non_overlapped_events]() {
*non_overlapped_events = ToNonOverlappedEvents(*overlapped_events);
});
i += 1;
}
// Runs the workers in parallel.
std::for_each(workers.begin(), workers.end(),
[](std::thread& t) { t.join(); });
// Moves non-overlapped events to the corresponding step in the map.
for (i = 0; i < step_ids.size(); i++) {
*non_overlapped_step_events[step_ids[i]].MutableEvents() =
std::move(non_overlapped_events_per_worker[i]);
}
return non_overlapped_step_events;
}
void StepDetails::AddMarker(const StepMarker& m) { markers_.push_back(m); }
void StepDetails::AddEvent(const EventTypeSpan& e) { events_.push_back(e); }
void StepDetails::AppendMarkers(const std::vector<StepMarker>& other_markers) {
markers_.insert(markers_.end(), other_markers.begin(), other_markers.end());
}
void StepDetails::AppendEvents(const std::vector<EventTypeSpan>& other_events) {
events_.insert(events_.end(), other_events.begin(), other_events.end());
}
Timespan StepDetails::StepTime() const {
// If there are multiple step-markers, uses the one that has the maximum
// duration.
Timespan max_steptime;
for (const auto& marker : markers_) {
const Timespan& timespan = marker.span;
if (timespan.duration_ps() > max_steptime.duration_ps())
max_steptime = timespan;
}
return max_steptime;
}
} // namespace profiler
} // namespace tensorflow