I want to optimize my application, especially the execution speed of certain functions.
Imagine there is a class with some member functions
class Test
{
public:
Test();
virtual ~Test();
int init(int arg1, double arg2);
private:
[...]
and in my constructor I call one of these methods
Test::Test()
{
[...]
int value = init(1, 1.2);
}
How can I measure the execution time of my method init(...)
in a nice and clean way without breaking my program?
At the moment I use following code
Test::Test()
{
[...]
auto start = std::chrono::high_resolution_clock::now();
int value = init(1, 1.2);
auto stop = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = stop - start;
std::cout << duration.count() * 1000 << "ms\n";
}
It works as expected but I think it is pretty messy and I want to have a "cleaner" solution.
Is there a way to have some kind of function which takes a member function and other parameters like so
int value = countTime(function, arg1, arg2);
I don't know whether it is possible to pass the return value from function()
to countTime()
in order to don't interrupt the workflow of my code.
EDIT: This is my TimeMeasure class
namespace tools
{
class TimeMeasure
{
public:
TimeMeasure()
{
m_start = std::chrono::high_resolution_clock::now();
}
virtual ~TimeMeasure()
{
m_stop = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> duration = m_stop - m_start;
std::cout << duration.count() << "ms\n";
}
public:
typedef std::chrono::time_point<std::chrono::high_resolution_clock> HighResClock;
private:
HighResClock m_start;
HighResClock m_stop;
};
template <typename T, typename F, typename... Args>
auto measure(T *t, F &&fn, Args... args)
{
tools::TimeMeasure timeMeasure;
return (t->*fn)(std::forward<Args>(args)...);
}
}
and in my constructor Test()
I use the function measure
this way
Test()
{
[...]
tools::measure(this, Test::init, filepath);
}
int init(const std::string& filepath) const
takes here a string to a file. So in my case it's just one argument
Unfortunately I get a invalid use of non-static member function 'int init(const string&) const'
error
I would wonder if a constructor is not a member function. So why do I get this error?
EDIT 2:
According to OznOg's answer I just forgot to hand in a pointer to my function.
So this would be the correct function call
tools::measure(this, &Test::init, filepath);
You could create a class like:
struct MeasureTime {
MeasureTime() : _start(std::chrono::high_resolution_clock::now()) {}
~MeasureTime() {
auto stop = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = stop - _start;
std::cout << duration.count() * 1000 << "ms\n";
}
private:
std::chrono::time_point<std::chrono::high_resolution_clock> _start;
};
and simply use it in your code:
Test::Test()
{
MeasureTime mt;
[...]
{ //or even this for just the init call
MeasureTime mt2;
int value = init(1, 1.2);
}
}
IMHO it is less intrusive than what you proposed.
If you really want a function, you may try a wrapper like:
template <class T, class F, class... Args>
auto MeasureTimeFn(T *t, F &&fn, Args&&... args) {
MeasureTime timer;
return (t->*fn)(std::forward<Args>(args)...);
}
And call it like:
int value = MeasureTimeFn(this, &Test::init, 1, 1.2);
but not sure it is really much better.
You can try to hide thing with a macro:
#define MEASURE(f, ...) \
MeasureTimeFn(this, &std::remove_reference_t<decltype(*this)>::f, __VA_ARGS__)
this way you can write
int value = MEASURE(init, 1, 1.2);
what is quite like what you asked for, but only works inside member functions, with member functions (non static).
Anyway probably a good place to start with.
* EDIT* If you can modify inheritance of you class, you may try
template<class T>
struct MeasureTool {
template <class F, class... Args>
auto measure(F &&fn, Args&&... args) {
tools::TimeMeasure timeMeasure;
return (static_cast<T*>(this)->*fn)(std::forward<Args>(args)...);
}
};
class Test : public MeasureTool<Test>
{
public:
Test();
virtual ~Test() {}
int init(const std::string &filepath) { _path = filepath; return 0; }
const auto &getPath() const { return _path; }
private:
std::string _path;
};
Test::Test()
{
std::string filepath("/some/where");
int value = measure(&Test::init, filepath);
measure(&Test::getPath);
}
And, this time, seems to match your very first requirement (but is quite intrusive...)
now, it's all in your hands :)