mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 11:10:05 +00:00
Add --time-report to spirv-opt
This patch adds a new option --time-report to spirv-opt. For each pass executed by spirv-opt, the flag prints resource utilization for the pass (CPU time, wall time, RSS and page faults) This fixes issue #1378
This commit is contained in:
parent
735d8a579e
commit
3b594e1630
@ -27,6 +27,7 @@ SPVTOOLS_SRC_FILES := \
|
||||
source/util/bit_stream.cpp \
|
||||
source/util/parse_number.cpp \
|
||||
source/util/string_utils.cpp \
|
||||
source/util/timer.cpp \
|
||||
source/val/basic_block.cpp \
|
||||
source/val/construct.cpp \
|
||||
source/val/function.cpp \
|
||||
|
@ -34,6 +34,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
|
||||
add_definitions(-DSPIRV_LINUX)
|
||||
set(SPIRV_TIMER_ENABLED ON)
|
||||
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
|
||||
add_definitions(-DSPIRV_WINDOWS)
|
||||
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN")
|
||||
@ -42,12 +43,16 @@ elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
|
||||
add_definitions(-DSPIRV_MAC)
|
||||
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android")
|
||||
add_definitions(-DSPIRV_ANDROID)
|
||||
set(SPIRV_TIMER_ENABLED ON)
|
||||
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD")
|
||||
add_definitions(-DSPIRV_FREEBSD)
|
||||
else()
|
||||
message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!")
|
||||
endif()
|
||||
|
||||
if (${SPIRV_TIMER_ENABLED})
|
||||
add_definitions(-DSPIRV_TIMER_ENABLED)
|
||||
endif()
|
||||
|
||||
if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
|
||||
message(STATUS "No build type selected, default to Debug")
|
||||
|
@ -119,6 +119,11 @@ class Optimizer {
|
||||
// output is sent to the |out| output stream.
|
||||
Optimizer& SetPrintAll(std::ostream* out);
|
||||
|
||||
// Sets the option to print the resource utilization of each pass. If |out|
|
||||
// is null, then no output is generated. Otherwise, output is sent to the
|
||||
// |out| output stream.
|
||||
Optimizer& SetTimeReport(std::ostream* out);
|
||||
|
||||
private:
|
||||
struct Impl; // Opaque struct for holding internal data.
|
||||
std::unique_ptr<Impl> impl_; // Unique pointer to internal data.
|
||||
|
@ -221,6 +221,7 @@ set(SPIRV_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/string_utils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/timer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/binary.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cfa.h
|
||||
@ -306,6 +307,12 @@ set(SPIRV_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/instruction.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/val/validation_state.cpp)
|
||||
|
||||
if (${SPIRV_TIMER_ENABLED})
|
||||
set(SPIRV_SOURCES
|
||||
${SPIRV_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util/timer.cpp)
|
||||
endif()
|
||||
|
||||
# The software_version.cpp file includes build-version.inc.
|
||||
# Rebuild the software_version.cpp object file if it is older than
|
||||
# build-version.inc or whenever build-version.inc itself is out of
|
||||
|
@ -215,6 +215,11 @@ Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
|
||||
impl_->pass_manager.SetTimeReport(out);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateNullPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "ir_context.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
#include "util/timer.h"
|
||||
|
||||
namespace spvtools {
|
||||
|
||||
@ -41,8 +42,10 @@ Pass::Status PassManager::Run(ir::IRContext* context) {
|
||||
}
|
||||
};
|
||||
|
||||
SPIRV_TIMER_DESCRIPTION(time_report_stream_, /* measure_mem_usage = */ true);
|
||||
for (const auto& pass : passes_) {
|
||||
print_disassembly("; IR before pass ", pass.get());
|
||||
SPIRV_TIMER_SCOPED(time_report_stream_, (pass ? pass->name() : ""), true);
|
||||
const auto one_status = pass->Run(context);
|
||||
if (one_status == Pass::Status::Failure) return one_status;
|
||||
if (one_status == Pass::Status::SuccessWithChange) status = one_status;
|
||||
|
@ -39,7 +39,10 @@ class PassManager {
|
||||
// The constructed instance will have an empty message consumer, which just
|
||||
// ignores all messages from the library. Use SetMessageConsumer() to supply
|
||||
// one if messages are of concern.
|
||||
PassManager() : consumer_(nullptr), print_all_stream_(nullptr) {}
|
||||
PassManager()
|
||||
: consumer_(nullptr),
|
||||
print_all_stream_(nullptr),
|
||||
time_report_stream_(nullptr) {}
|
||||
|
||||
// Sets the message consumer to the given |consumer|.
|
||||
void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
|
||||
@ -77,6 +80,14 @@ class PassManager {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Sets the option to print the resource utilization of each pass. Output is
|
||||
// written to |out| if that is not null. No output is generated if |out| is
|
||||
// null.
|
||||
PassManager& SetTimeReport(std::ostream* out) {
|
||||
time_report_stream_ = out;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
// Consumer for messages.
|
||||
MessageConsumer consumer_;
|
||||
@ -85,6 +96,9 @@ class PassManager {
|
||||
// The output stream to write disassembly to before each pass, and after
|
||||
// the last pass. If this is null, no output is generated.
|
||||
std::ostream* print_all_stream_;
|
||||
// The output stream to write the resource utilization of each pass. If this
|
||||
// is null, no output is generated.
|
||||
std::ostream* time_report_stream_;
|
||||
};
|
||||
|
||||
inline void PassManager::AddPass(std::unique_ptr<Pass> pass) {
|
||||
|
100
source/util/timer.cpp
Normal file
100
source/util/timer.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2018 Google LLC.
|
||||
//
|
||||
// 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 "util/timer.h"
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace spvutils {
|
||||
|
||||
#if defined(SPIRV_TIMER_ENABLED)
|
||||
|
||||
void PrintTimerDescription(std::ostream* out, bool measure_mem_usage) {
|
||||
if (out) {
|
||||
*out << std::setw(30) << "PASS name" << std::setw(12) << "CPU time"
|
||||
<< std::setw(12) << "WALL time" << std::setw(12) << "USR time"
|
||||
<< std::setw(12) << "SYS time";
|
||||
if (measure_mem_usage) {
|
||||
*out << std::setw(12) << "RSS delta" << std::setw(16) << "PGFault delta";
|
||||
}
|
||||
*out << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not change the order of invoking system calls. We want to make CPU/Wall
|
||||
// time correct as much as possible. Calling functions to get CPU/Wall time must
|
||||
// closely surround the target code of measuring.
|
||||
void Timer::Start() {
|
||||
if (report_stream_) {
|
||||
if (getrusage(RUSAGE_SELF, &usage_before_) == -1)
|
||||
usage_status_ |= kGetrusageFailed;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &wall_before_) == -1)
|
||||
usage_status_ |= kClockGettimeWalltimeFailed;
|
||||
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_before_) == -1)
|
||||
usage_status_ |= kClockGettimeCPUtimeFailed;
|
||||
}
|
||||
}
|
||||
|
||||
// The order of invoking system calls is important with the same reason as
|
||||
// Timer::Start().
|
||||
void Timer::Stop() {
|
||||
if (report_stream_ && usage_status_ == kSucceeded) {
|
||||
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_after_) == -1)
|
||||
usage_status_ |= kClockGettimeCPUtimeFailed;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &wall_after_) == -1)
|
||||
usage_status_ |= kClockGettimeWalltimeFailed;
|
||||
if (getrusage(RUSAGE_SELF, &usage_after_) == -1)
|
||||
usage_status_ = kGetrusageFailed;
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::Report(const char* tag) {
|
||||
if (!report_stream_) return;
|
||||
|
||||
report_stream_->precision(2);
|
||||
*report_stream_ << std::fixed << std::setw(30) << tag;
|
||||
|
||||
if (usage_status_ & kClockGettimeCPUtimeFailed)
|
||||
*report_stream_ << std::setw(12) << "Failed";
|
||||
else
|
||||
*report_stream_ << std::setw(12) << CPUTime();
|
||||
|
||||
if (usage_status_ & kClockGettimeWalltimeFailed)
|
||||
*report_stream_ << std::setw(12) << "Failed";
|
||||
else
|
||||
*report_stream_ << std::setw(12) << WallTime();
|
||||
|
||||
if (usage_status_ & kGetrusageFailed) {
|
||||
*report_stream_ << std::setw(12) << "Failed" << std::setw(12) << "Failed";
|
||||
if (measure_mem_usage_) {
|
||||
*report_stream_ << std::setw(12) << "Failed" << std::setw(12) << "Failed";
|
||||
}
|
||||
} else {
|
||||
*report_stream_ << std::setw(12) << UserTime() << std::setw(12)
|
||||
<< SystemTime();
|
||||
if (measure_mem_usage_) {
|
||||
*report_stream_ << std::fixed << std::setw(12) << RSS() << std::setw(16)
|
||||
<< PageFault();
|
||||
}
|
||||
}
|
||||
*report_stream_ << std::endl;
|
||||
}
|
||||
|
||||
#endif // defined(SPIRV_TIMER_ENABLED)
|
||||
|
||||
} // namespace spvutils
|
387
source/util/timer.h
Normal file
387
source/util/timer.h
Normal file
@ -0,0 +1,387 @@
|
||||
// Copyright (c) 2018 Google LLC.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Contains utils for getting resource utilization
|
||||
|
||||
#ifndef LIBSPIRV_UTIL_TIMER_H_
|
||||
#define LIBSPIRV_UTIL_TIMER_H_
|
||||
|
||||
#if defined(SPIRV_TIMER_ENABLED)
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
// A macro to call spvutils::PrintTimerDescription(std::ostream*, bool). The
|
||||
// first argument must be given as std::ostream*. If it is NULL, the function
|
||||
// does nothing. Otherwise, it prints resource types measured by Timer class.
|
||||
// The second is optional and if it is true, the function also prints resource
|
||||
// type fields related to memory. Otherwise, it does not print memory related
|
||||
// fields. Its default is false. In usual, this must be placed before calling
|
||||
// Timer::Report() to inform what those fields printed by Timer::Report()
|
||||
// indicate (or spvutils::PrintTimerDescription() must be used instead).
|
||||
#define SPIRV_TIMER_DESCRIPTION(...) \
|
||||
spvutils::PrintTimerDescription(__VA_ARGS__)
|
||||
|
||||
// Creates an object of ScopedTimer to measure the resource utilization for the
|
||||
// scope surrounding it as the following example:
|
||||
//
|
||||
// { // <-- beginning of this scope
|
||||
//
|
||||
// /* ... code out of interest ... */
|
||||
//
|
||||
// SPIRV_TIMER_SCOPED(std::cout, tag);
|
||||
//
|
||||
// /* ... lines of code that we want to know its resource usage ... */
|
||||
//
|
||||
// } // <-- end of this scope. The destructor of ScopedTimer prints tag and
|
||||
// the resource utilization to std::cout.
|
||||
#define SPIRV_TIMER_SCOPED(...) \
|
||||
spvutils::ScopedTimer<spvutils::Timer> timer##__LINE__(__VA_ARGS__)
|
||||
|
||||
namespace spvutils {
|
||||
|
||||
// Prints the description of resource types measured by Timer class. If |out| is
|
||||
// NULL, it does nothing. Otherwise, it prints resource types. The second is
|
||||
// optional and if it is true, the function also prints resource type fields
|
||||
// related to memory. Its default is false. In usual, this must be placed before
|
||||
// calling Timer::Report() to inform what those fields printed by
|
||||
// Timer::Report() indicate.
|
||||
void PrintTimerDescription(std::ostream*, bool = false);
|
||||
|
||||
// Status of Timer. kGetrusageFailed means it failed in calling getrusage().
|
||||
// kClockGettimeWalltimeFailed means it failed in getting wall time when calling
|
||||
// clock_gettime(). kClockGettimeCPUtimeFailed means it failed in getting CPU
|
||||
// time when calling clock_gettime().
|
||||
enum UsageStatus {
|
||||
kSucceeded = 0,
|
||||
kGetrusageFailed = 1 << 0,
|
||||
kClockGettimeWalltimeFailed = 1 << 1,
|
||||
kClockGettimeCPUtimeFailed = 1 << 2,
|
||||
};
|
||||
|
||||
// Timer measures the resource utilization for a range of code. The resource
|
||||
// utilization consists of CPU time (i.e., process time), WALL time (elapsed
|
||||
// time), USR time, SYS time, RSS delta, and the delta of the number of page
|
||||
// faults. RSS delta and the delta of the number of page faults are measured
|
||||
// only when |measure_mem_usage| given to the constructor is true. This class
|
||||
// should be used as the following example:
|
||||
//
|
||||
// spvutils::Timer timer(std::cout);
|
||||
// timer.Start(); // <-- set |usage_before_|, |wall_before_|,
|
||||
// and |cpu_before_|
|
||||
//
|
||||
// /* ... lines of code that we want to know its resource usage ... */
|
||||
//
|
||||
// timer.Stop(); // <-- set |cpu_after_|, |wall_after_|, and
|
||||
// |usage_after_|
|
||||
// timer.Report(tag); // <-- print tag and the resource utilization to
|
||||
// std::cout.
|
||||
class Timer {
|
||||
public:
|
||||
Timer(std::ostream* out, bool measure_mem_usage = false)
|
||||
: report_stream_(out),
|
||||
usage_status_(kSucceeded),
|
||||
measure_mem_usage_(measure_mem_usage) {}
|
||||
|
||||
// Sets |usage_before_|, |wall_before_|, and |cpu_before_| as results of
|
||||
// getrusage(), clock_gettime() for the wall time, and clock_gettime() for the
|
||||
// CPU time respectively. Note that this method erases all previous state of
|
||||
// |usage_before_|, |wall_before_|, |cpu_before_|.
|
||||
virtual void Start();
|
||||
|
||||
// Sets |cpu_after_|, |wall_after_|, and |usage_after_| as results of
|
||||
// clock_gettime() for the wall time, and clock_gettime() for the CPU time,
|
||||
// getrusage() respectively. Note that this method erases all previous state
|
||||
// of |cpu_after_|, |wall_after_|, |usage_after_|.
|
||||
virtual void Stop();
|
||||
|
||||
// If |report_stream_| is NULL, it does nothing. Otherwise, it prints the
|
||||
// resource utilization (i.e., CPU/WALL/USR/SYS time, RSS delta) between the
|
||||
// time of calling Timer::Start() and the time of calling Timer::Stop(). If we
|
||||
// cannot get a resource usage because of failures, it prints "Failed" instead
|
||||
// for the resource.
|
||||
void Report(const char* tag);
|
||||
|
||||
// Returns the measured CPU Time (i.e., process time) for a range of code
|
||||
// execution. If kClockGettimeCPUtimeFailed is set by the failure of calling
|
||||
// clock_gettime(), it returns -1.
|
||||
virtual double CPUTime() {
|
||||
if (usage_status_ & kClockGettimeCPUtimeFailed) return -1;
|
||||
return TimeDifference(cpu_before_, cpu_after_);
|
||||
}
|
||||
|
||||
// Returns the measured Wall Time (i.e., elapsed time) for a range of code
|
||||
// execution. If kClockGettimeWalltimeFailed is set by the failure of
|
||||
// calling clock_gettime(), it returns -1.
|
||||
virtual double WallTime() {
|
||||
if (usage_status_ & kClockGettimeWalltimeFailed) return -1;
|
||||
return TimeDifference(wall_before_, wall_after_);
|
||||
}
|
||||
|
||||
// Returns the measured USR Time for a range of code execution. If
|
||||
// kGetrusageFailed is set because of the failure of calling getrusage(), it
|
||||
// returns -1.
|
||||
virtual double UserTime() {
|
||||
if (usage_status_ & kGetrusageFailed) return -1;
|
||||
return TimeDifference(usage_before_.ru_utime, usage_after_.ru_utime);
|
||||
}
|
||||
|
||||
// Returns the measured SYS Time for a range of code execution. If
|
||||
// kGetrusageFailed is set because of the failure of calling getrusage(), it
|
||||
// returns -1.
|
||||
virtual double SystemTime() {
|
||||
if (usage_status_ & kGetrusageFailed) return -1;
|
||||
return TimeDifference(usage_before_.ru_stime, usage_after_.ru_stime);
|
||||
}
|
||||
|
||||
// Returns the measured RSS delta for a range of code execution. If
|
||||
// kGetrusageFailed is set because of the failure of calling getrusage(), it
|
||||
// returns -1.
|
||||
virtual long RSS() const {
|
||||
if (usage_status_ & kGetrusageFailed) return -1;
|
||||
return usage_after_.ru_maxrss - usage_before_.ru_maxrss;
|
||||
}
|
||||
|
||||
// Returns the measured the delta of the number of page faults for a range of
|
||||
// code execution. If kGetrusageFailed is set because of the failure of
|
||||
// calling getrusage(), it returns -1.
|
||||
virtual long PageFault() const {
|
||||
if (usage_status_ & kGetrusageFailed) return -1;
|
||||
return (usage_after_.ru_minflt - usage_before_.ru_minflt) +
|
||||
(usage_after_.ru_majflt - usage_before_.ru_majflt);
|
||||
}
|
||||
|
||||
virtual ~Timer() {}
|
||||
|
||||
private:
|
||||
// Returns the time gap between |from| and |to| in seconds.
|
||||
static double TimeDifference(const timeval& from, const timeval& to) {
|
||||
assert((to.tv_sec > from.tv_sec) ||
|
||||
(to.tv_sec == from.tv_sec && to.tv_usec >= from.tv_usec));
|
||||
return static_cast<double>(to.tv_sec - from.tv_sec) +
|
||||
static_cast<double>(to.tv_usec - from.tv_usec) * .000001;
|
||||
}
|
||||
|
||||
// Returns the time gap between |from| and |to| in seconds.
|
||||
static double TimeDifference(const timespec& from, const timespec& to) {
|
||||
assert((to.tv_sec > from.tv_sec) ||
|
||||
(to.tv_sec == from.tv_sec && to.tv_nsec >= from.tv_nsec));
|
||||
return static_cast<double>(to.tv_sec - from.tv_sec) +
|
||||
static_cast<double>(to.tv_nsec - from.tv_nsec) * .000000001;
|
||||
}
|
||||
|
||||
// Output stream to print out the resource utilization. If it is NULL,
|
||||
// Report() does nothing.
|
||||
std::ostream* report_stream_;
|
||||
|
||||
// Status to stop measurement if a system call returns an error.
|
||||
unsigned usage_status_;
|
||||
|
||||
// Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when
|
||||
// Timer::Start() is called. It is used as the base status of CPU time.
|
||||
timespec cpu_before_;
|
||||
|
||||
// Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when
|
||||
// Timer::Start() is called. It is used as the base status of WALL time.
|
||||
timespec wall_before_;
|
||||
|
||||
// Variable to save the result of getrusage() when Timer::Start() is called.
|
||||
// It is used as the base status of USR time, SYS time, and RSS.
|
||||
rusage usage_before_;
|
||||
|
||||
// Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when
|
||||
// Timer::Stop() is called. It is used as the last status of CPU time. The
|
||||
// resouce usage is measured by subtracting |cpu_before_| from it.
|
||||
timespec cpu_after_;
|
||||
|
||||
// Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when
|
||||
// Timer::Stop() is called. It is used as the last status of WALL time. The
|
||||
// resouce usage is measured by subtracting |wall_before_| from it.
|
||||
timespec wall_after_;
|
||||
|
||||
// Variable to save the result of getrusage() when Timer::Stop() is called. It
|
||||
// is used as the last status of USR time, SYS time, and RSS. Those resouce
|
||||
// usages are measured by subtracting |usage_before_| from it.
|
||||
rusage usage_after_;
|
||||
|
||||
// If true, Timer reports the memory usage information too. Otherwise, Timer
|
||||
// reports only USR time, WALL time, SYS time.
|
||||
bool measure_mem_usage_;
|
||||
};
|
||||
|
||||
// The purpose of ScopedTimer is to measure the resource utilization for a
|
||||
// scope. Simply creating a local variable of ScopedTimer will call
|
||||
// Timer::Start() and it calls Timer::Stop() and Timer::Report() at the end of
|
||||
// the scope by its destructor. When we use this class, we must choose the
|
||||
// proper Timer class (for class TimerType template) in advance. This class
|
||||
// should be used as the following example:
|
||||
//
|
||||
// { // <-- beginning of this scope
|
||||
//
|
||||
// /* ... code out of interest ... */
|
||||
//
|
||||
// spvutils::ScopedTimer<spvutils::Timer> scopedtimer(std::cout, tag);
|
||||
//
|
||||
// /* ... lines of code that we want to know its resource usage ... */
|
||||
//
|
||||
// } // <-- end of this scope. The destructor of ScopedTimer prints tag and
|
||||
// the resource utilization to std::cout.
|
||||
//
|
||||
// The template<class TimerType> is used to choose a Timer class. Currently,
|
||||
// only options for the Timer class are Timer and MockTimer in the unit test.
|
||||
template <class TimerType>
|
||||
class ScopedTimer {
|
||||
public:
|
||||
ScopedTimer(std::ostream* out, const char* tag,
|
||||
bool measure_mem_usage = false)
|
||||
: timer(new TimerType(out, measure_mem_usage)), tag_(tag) {
|
||||
timer->Start();
|
||||
}
|
||||
|
||||
// At the end of the scope surrounding the instance of this class, this
|
||||
// destructor saves the last status of resource usage and reports it.
|
||||
virtual ~ScopedTimer() {
|
||||
timer->Stop();
|
||||
timer->Report(tag_);
|
||||
delete timer;
|
||||
}
|
||||
|
||||
private:
|
||||
// Actual timer that measures the resource utilization. It must be an instance
|
||||
// of Timer class if there is no special reason to use other class.
|
||||
TimerType* timer;
|
||||
|
||||
// A tag that will be printed in front of the trace reported by Timer class.
|
||||
const char* tag_;
|
||||
};
|
||||
|
||||
// CumulativeTimer is the same as Timer class, but it supports a cumulative
|
||||
// measurement as the following example:
|
||||
//
|
||||
// CumulativeTimer *ctimer = new CumulativeTimer(std::cout);
|
||||
// ctimer->Start();
|
||||
//
|
||||
// /* ... lines of code that we want to know its resource usage ... */
|
||||
//
|
||||
// ctimer->Stop();
|
||||
//
|
||||
// /* ... code out of interest ... */
|
||||
//
|
||||
// ctimer->Start();
|
||||
//
|
||||
// /* ... lines of code that we want to know its resource usage ... */
|
||||
//
|
||||
// ctimer->Stop();
|
||||
// ctimer->Report(tag);
|
||||
// delete ctimer;
|
||||
//
|
||||
class CumulativeTimer : public Timer {
|
||||
public:
|
||||
CumulativeTimer(std::ostream* out, bool measure_mem_usage = false)
|
||||
: Timer(out, measure_mem_usage),
|
||||
cpu_time_(0),
|
||||
wall_time_(0),
|
||||
usr_time_(0),
|
||||
sys_time_(0),
|
||||
rss_(0),
|
||||
pgfaults_(0) {}
|
||||
|
||||
// If we cannot get a resource usage because of failures, it sets -1 for the
|
||||
// resource usage.
|
||||
void Stop() override {
|
||||
Timer::Stop();
|
||||
|
||||
if (cpu_time_ >= 0 && Timer::CPUTime() >= 0)
|
||||
cpu_time_ += Timer::CPUTime();
|
||||
else
|
||||
cpu_time_ = -1;
|
||||
|
||||
if (wall_time_ >= 0 && Timer::WallTime() >= 0)
|
||||
wall_time_ += Timer::WallTime();
|
||||
else
|
||||
wall_time_ = -1;
|
||||
|
||||
if (usr_time_ >= 0 && Timer::UserTime() >= 0)
|
||||
usr_time_ += Timer::UserTime();
|
||||
else
|
||||
usr_time_ = -1;
|
||||
|
||||
if (sys_time_ >= 0 && Timer::SystemTime() >= 0)
|
||||
sys_time_ += Timer::SystemTime();
|
||||
else
|
||||
sys_time_ = -1;
|
||||
|
||||
if (rss_ >= 0 && Timer::RSS() >= 0)
|
||||
rss_ += Timer::RSS();
|
||||
else
|
||||
rss_ = -1;
|
||||
|
||||
if (pgfaults_ >= 0 && Timer::PageFault() >= 0)
|
||||
pgfaults_ += Timer::PageFault();
|
||||
else
|
||||
pgfaults_ = -1;
|
||||
}
|
||||
|
||||
// Returns the cumulative CPU Time (i.e., process time) for a range of code
|
||||
// execution.
|
||||
double CPUTime() override { return cpu_time_; }
|
||||
|
||||
// Returns the cumulative Wall Time (i.e., elapsed time) for a range of code
|
||||
// execution.
|
||||
double WallTime() override { return wall_time_; }
|
||||
|
||||
// Returns the cumulative USR Time for a range of code execution.
|
||||
double UserTime() override { return usr_time_; }
|
||||
|
||||
// Returns the cumulative SYS Time for a range of code execution.
|
||||
double SystemTime() override { return sys_time_; }
|
||||
|
||||
// Returns the cumulative RSS delta for a range of code execution.
|
||||
long RSS() const override { return rss_; }
|
||||
|
||||
// Returns the cumulative delta of number of page faults for a range of code
|
||||
// execution.
|
||||
long PageFault() const override { return pgfaults_; }
|
||||
|
||||
private:
|
||||
// Variable to save the cumulative CPU time (i.e., process time).
|
||||
double cpu_time_;
|
||||
|
||||
// Variable to save the cumulative wall time (i.e., elapsed time).
|
||||
double wall_time_;
|
||||
|
||||
// Variable to save the cumulative user time.
|
||||
double usr_time_;
|
||||
|
||||
// Variable to save the cumulative system time.
|
||||
double sys_time_;
|
||||
|
||||
// Variable to save the cumulative RSS delta.
|
||||
long rss_;
|
||||
|
||||
// Variable to save the cumulative delta of the number of page faults.
|
||||
long pgfaults_;
|
||||
};
|
||||
|
||||
} // namespace spvutils
|
||||
|
||||
#else // defined(SPIRV_TIMER_ENABLED)
|
||||
|
||||
#define SPIRV_TIMER_DESCRIPTION(...)
|
||||
#define SPIRV_TIMER_SCOPED(...)
|
||||
|
||||
#endif // defined(SPIRV_TIMER_ENABLED)
|
||||
|
||||
#endif // LIBSPIRV_UTIL_TIMER_H_
|
@ -178,6 +178,13 @@ add_spvtools_unittest(
|
||||
SRCS string_utils_test.cpp
|
||||
LIBS ${SPIRV_TOOLS})
|
||||
|
||||
if (${SPIRV_TIMER_ENABLED})
|
||||
add_spvtools_unittest(
|
||||
TARGET timer
|
||||
SRCS timer_test.cpp
|
||||
LIBS ${SPIRV_TOOLS})
|
||||
endif()
|
||||
|
||||
add_spvtools_unittest(
|
||||
TARGET log
|
||||
SRCS log_test.cpp
|
||||
|
143
test/timer_test.cpp
Normal file
143
test/timer_test.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2018 Google LLC.
|
||||
//
|
||||
// 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 <unistd.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "source/util/timer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using ::spvutils::CumulativeTimer;
|
||||
using ::spvutils::PrintTimerDescription;
|
||||
using ::spvutils::ScopedTimer;
|
||||
using ::spvutils::Timer;
|
||||
|
||||
// A mock class to mimic Timer class for a testing purpose. It has fixed
|
||||
// CPU/WALL/USR/SYS time, RSS delta, and the delta of the number of page faults.
|
||||
class MockTimer : public Timer {
|
||||
public:
|
||||
MockTimer(std::ostream* out, bool measure_mem_usage = false)
|
||||
: Timer(out, measure_mem_usage) {}
|
||||
double CPUTime() override { return 0.019123; }
|
||||
double WallTime() override { return 0.019723; }
|
||||
double UserTime() override { return 0.012723; }
|
||||
double SystemTime() override { return 0.002723; }
|
||||
long RSS() const override { return 360L; }
|
||||
long PageFault() const override { return 3600L; }
|
||||
};
|
||||
|
||||
// This unit test checks whether the actual output of MockTimer::Report() is the
|
||||
// same as fixed CPU/WALL/USR/SYS time, RSS delta, and the delta of the number
|
||||
// of page faults that are returned by MockTimer.
|
||||
TEST(MockTimer, DoNothing) {
|
||||
std::ostringstream buf;
|
||||
|
||||
PrintTimerDescription(&buf);
|
||||
MockTimer timer(&buf);
|
||||
timer.Start();
|
||||
|
||||
// Do nothing.
|
||||
|
||||
timer.Stop();
|
||||
timer.Report("TimerTest");
|
||||
|
||||
EXPECT_EQ(0.019123, timer.CPUTime());
|
||||
EXPECT_EQ(0.019723, timer.WallTime());
|
||||
EXPECT_EQ(0.012723, timer.UserTime());
|
||||
EXPECT_EQ(0.002723, timer.SystemTime());
|
||||
EXPECT_EQ(
|
||||
" PASS name CPU time WALL time USR time"
|
||||
" SYS time\n TimerTest 0.02 0.02"
|
||||
" 0.01 0.00\n",
|
||||
buf.str());
|
||||
}
|
||||
|
||||
// This unit test checks whether the ScopedTimer<MockTimer> correctly reports
|
||||
// the fixed CPU/WALL/USR/SYS time, RSS delta, and the delta of the number of
|
||||
// page faults that are returned by MockTimer.
|
||||
TEST(MockTimer, TestScopedTimer) {
|
||||
std::ostringstream buf;
|
||||
|
||||
{
|
||||
ScopedTimer<MockTimer> scopedtimer(&buf, "ScopedTimerTest");
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
EXPECT_EQ(
|
||||
" ScopedTimerTest 0.02 0.02 0.01"
|
||||
" 0.00\n",
|
||||
buf.str());
|
||||
}
|
||||
|
||||
// A mock class to mimic CumulativeTimer class for a testing purpose. It has
|
||||
// fixed CPU/WALL/USR/SYS time, RSS delta, and the delta of the number of page
|
||||
// faults for each measurement (i.e., a pair of Start() and Stop()). If the
|
||||
// number of measurements increases, it increases |count_stop_| by the number of
|
||||
// calling Stop() and the amount of each resource usage is proportional to
|
||||
// |count_stop_|.
|
||||
class MockCumulativeTimer : public CumulativeTimer {
|
||||
public:
|
||||
MockCumulativeTimer(std::ostream* out, bool measure_mem_usage = false)
|
||||
: CumulativeTimer(out, measure_mem_usage), count_stop_(0) {}
|
||||
double CPUTime() override { return count_stop_ * 0.019123; }
|
||||
double WallTime() override { return count_stop_ * 0.019723; }
|
||||
double UserTime() override { return count_stop_ * 0.012723; }
|
||||
double SystemTime() override { return count_stop_ * 0.002723; }
|
||||
long RSS() const override { return count_stop_ * 360L; }
|
||||
long PageFault() const override { return count_stop_ * 3600L; }
|
||||
|
||||
// Calling Stop() does nothing but just increases |count_stop_| by 1.
|
||||
void Stop() override { ++count_stop_; };
|
||||
|
||||
private:
|
||||
unsigned int count_stop_;
|
||||
};
|
||||
|
||||
// This unit test checks whether the MockCumulativeTimer correctly reports the
|
||||
// cumulative CPU/WALL/USR/SYS time, RSS delta, and the delta of the number of
|
||||
// page faults whose values are fixed for each measurement (i.e., a pair of
|
||||
// Start() and Stop()).
|
||||
TEST(MockCumulativeTimer, DoNothing) {
|
||||
CumulativeTimer* ctimer;
|
||||
std::ostringstream buf;
|
||||
|
||||
{
|
||||
ctimer = new MockCumulativeTimer(&buf);
|
||||
ctimer->Start();
|
||||
|
||||
// Do nothing.
|
||||
|
||||
ctimer->Stop();
|
||||
}
|
||||
|
||||
{
|
||||
ctimer->Start();
|
||||
|
||||
// Do nothing.
|
||||
|
||||
ctimer->Stop();
|
||||
ctimer->Report("CumulativeTimerTest");
|
||||
}
|
||||
|
||||
EXPECT_EQ(
|
||||
" CumulativeTimerTest 0.04 0.04 0.03"
|
||||
" 0.01\n",
|
||||
buf.str());
|
||||
|
||||
if (ctimer) delete ctimer;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
@ -290,6 +290,13 @@ Options (in lexicographical order):
|
||||
--strip-reflect
|
||||
Remove all reflection information. For now, this covers
|
||||
reflection information defined by SPV_GOOGLE_hlsl_functionality1.
|
||||
--time-report
|
||||
Print the resource utilization of each pass (e.g., CPU time,
|
||||
RSS) to standard error output. Currently it supports only Unix
|
||||
systems. This option is the same as -ftime-report in GCC. It
|
||||
prints CPU/WALL/USR/SYS time (and RSS if possible), but note that
|
||||
USR/SYS time are returned by getrusage() and can have a small
|
||||
error.
|
||||
--workaround-1209
|
||||
Rewrites instructions for which there are known driver bugs to
|
||||
avoid triggering those bugs.
|
||||
@ -544,6 +551,8 @@ OptStatus ParseFlags(int argc, const char** argv, Optimizer* optimizer,
|
||||
optimizer->RegisterPass(CreateCCPPass());
|
||||
} else if (0 == strcmp(cur_arg, "--print-all")) {
|
||||
optimizer->SetPrintAll(&std::cerr);
|
||||
} else if (0 == strcmp(cur_arg, "--time-report")) {
|
||||
optimizer->SetTimeReport(&std::cerr);
|
||||
} else if ('\0' == cur_arg[1]) {
|
||||
// Setting a filename of "-" to indicate stdin.
|
||||
if (!*in_file) {
|
||||
|
Loading…
Reference in New Issue
Block a user