Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the statistic function in the profiler #376

Merged
merged 1 commit into from
Jul 8, 2024

Conversation

ThreeMonth03
Copy link
Collaborator

According to the pr 343,
I refer the cprofiler(https://docs.python.org/3/library/profile.html),
and add the statistic function in the callprofiler.

Copy link
Collaborator Author

@ThreeMonth03 ThreeMonth03 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yungyuc
Here is the pr of the issue 343,
but it is not ready for code review.

Comment on lines 155 to 200
std::string report() const
{
std::ostringstream ostm;
ostm << " " << total_call_count() << " function calls in " << total_time() << " seconds" << std::endl;
ostm << std::endl;
ostm << std::setw(40) << "Function Name" << std::setw(25) << "Call Count" << std::setw(25) << "Total Time (s)" << std::setw(25) << "Per Call (s)" << std::setw(25) << "Cumulative Time (s)" << std::setw(25) << "Per Call (s)" << std::endl;
for (auto it = m_entry.begin(); it != m_entry.end(); ++it)
{
ostm
<< it->first << " : "
<< "count = " << it->second.count() << " , "
<< "time = " << it->second.time() << " (second)"
<< std::endl;
ostm << std::setw(40) << it->first << std::setw(25) << it->second.count() << std::setw(25) << it->second.time() << std::setw(25) << it->second.time() / it->second.count() << std::setw(25) << it->second.ctime() << std::setw(25) << it->second.ctime() / it->second.count() << std::endl;
}
return ostm.str();
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that enhancement of the report function make the CI fail.
I will fix the modification later.

Copy link
Collaborator Author

@ThreeMonth03 ThreeMonth03 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yungyuc I'm ready for reviewing the code.

Comment on lines 75 to 117
void CallProfiler::print_statistics(const RadixTreeNode<CallerProfile> & node, std::ostream & outstream)
{
TimeRegistry::me().clear();

std::queue<const RadixTreeNode<CallerProfile> *> nodes_buffer;
for (const auto & child : node.children())
{
nodes_buffer.push(child.get());
}

// BFS algorithm and put the node information into TimeRegistry.
while (!nodes_buffer.empty())
{
const int nodes_buffer_size = nodes_buffer.size();
for (int i = 0; i < nodes_buffer_size; ++i)
{
const RadixTreeNode<CallerProfile> * current_node = nodes_buffer.front();
nodes_buffer.pop();
TimeRegistry::me().add(current_node->data().caller_name, current_node->data().total_time.count() / 1e9, current_node->data().total_time.count() / 1e9, current_node->data().call_count);

for (const auto & child : current_node->children())
{
nodes_buffer.push(child.get());
TimeRegistry::me().add(current_node->data().caller_name, 0, -child->data().total_time.count() / 1e9, 0);
}
}
}

// Print the statistics.
outstream << TimeRegistry::me().detailed_report() << std::endl;

// Reset the TimeRegistry.
TimeRegistry::me().clear();
}
Copy link
Collaborator Author

@ThreeMonth03 ThreeMonth03 Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I orginize the data, and write into TimeRegistry.
Since TimeRegistry is singleton pattern,
I'm not sure whether the modification of TimeRegistrymay affect the other function.

Comment on lines 169 to 200
std::string detailed_report() const
{
std::ostringstream ostm;
/// Header
ostm
<< " " << total_call_count()
<< " function calls in " << total_time()
<< " seconds" << std::endl;
ostm << std::endl;
ostm
<< std::setw(40) << "Function Name"
<< std::setw(25) << "Call Count"
<< std::setw(25) << "Total Time (s)"
<< std::setw(25) << "Per Call (s)"
<< std::setw(25) << "Cumulative Time (s)"
<< std::setw(25) << "Per Call (s)"
<< std::endl;

/// Body
for (auto it = m_entry.begin(); it != m_entry.end(); ++it)
{
ostm
<< std::setw(40) << it->first
<< std::setw(25) << it->second.count()
<< std::setw(25) << it->second.time()
<< std::setw(25) << it->second.time() / it->second.count()
<< std::setw(25) << it->second.ctime()
<< std::setw(25) << it->second.ctime() / it->second.count()
<< std::endl;
}
return ostm.str();
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I add a new function to make the report clearly.
Additionally, by remaining the old feature std::string report() const,
I think we could prevent from rewriting the pytest.

Copy link
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be mindful to coding styles.

  • Properly add blank lines.
  • Use Python tests for printed results, not C++.
  • Avoid output arguments.

@@ -27,7 +27,7 @@
*/

#include <modmesh/toggle/RadixTree.hpp>

#include <modmesh/toggle/profile.hpp>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to keep a blank line between include and the following constructs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix

@@ -212,5 +212,124 @@ TEST_F(CallProfilerTest, cancel)
EXPECT_EQ(radix_tree().get_unique_node(), 0);
}

std::string line2 = "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of tests, which assert for printed texts, should be in Python, not in C++.

@@ -291,6 +297,7 @@ class CallProfiler

private:
void print_profiling_result(const RadixTreeNode<CallerProfile> & node, const int depth, std::ostream & outstream) const;
static void print_statistics(const RadixTreeNode<CallerProfile> & node, std::ostream & outstream);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Output arguments are hard to maintain. Please avoid output arguments.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yungyuc for std::ostream, isn't it a common pattern to use as an output argument?
We can return a string directly, but I think using a stream is more efficient. In addition, we have flexibility to put std::cout or std::stringstream here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if you notice it or not, we also have void print_profiling_result(const RadixTreeNode<CallerProfile> & node, const int depth, std::ostream & outstream) const; already.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yungyuc for std::ostream, isn't it a common pattern to use as an output argument? We can return a string directly, but I think using a stream is more efficient. In addition, we have flexibility to put std::cout or std::stringstream here.

It is common to use ostream as an output argument, but it is better to do it in an operator>>() overload.

I can tolerate output arguments when they are with a private function. It turned out that I did not notice that the helpers are private member functions. Then I am fine with it. But not because std::ostream.

@yungyuc yungyuc added enhancement New feature or request performance Profiling, runtime, and memory consumption labels Jun 25, 2024
Copy link
Collaborator Author

@ThreeMonth03 ThreeMonth03 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Properly add blank lines.
  • Use Python tests for printed results, not C++.

@yungyuc I'm ready for next review.

@@ -27,7 +27,7 @@
*/

#include <modmesh/toggle/RadixTree.hpp>

#include <modmesh/toggle/profile.hpp>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix

Comment on lines 85 to 91
path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
"data", "profiler_python_schema.json")
with open(path, 'r') as schema_file:
path = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
"data",
"profiler_python_schema.json",
)
with open(path, "r") as schema_file:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I format the file by vscode extension.
I don't rewrite the code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert the style change. Do not mix style change with logic change in one PR.

A style change is a refactoring change and should use its own PR.

Comment on lines 199 to 285

def test_get_stat(self):
time1 = 0.5
time2 = 0.1
time3 = 0.2

@profile_function
def bar():
busy_loop(time1)

@profile_function
def foo():
busy_loop(time2)
bar()

@profile_function
def baz():
busy_loop(time3)
foo()

modmesh.call_profiler.reset()
bar()
bar()
foo()
baz()
root_stat = modmesh.call_profiler.stat()

# Check the number of lines
stat_line_list = root_stat.split("\n")
self.assertEqual(len(stat_line_list), 7)

# Check the first line
words = stat_line_list[0].split()
self.assertEqual(words[0], "7")
self.assertEqual(words[1], "function")
self.assertEqual(words[2], "calls")
self.assertEqual(words[3], "in")
ref_total_time = time1 * 4 + time2 * 2 + time3
self.assertTrue(abs(float(words[4]) - ref_total_time) <= 6e-4)
self.assertEqual(words[5], "seconds")

# Check the second line
self.assertEqual(stat_line_list[1], "")

# Check the third line
ref_line3 = (
" Function Name"
+ " Call Count"
+ " Total Time (s)"
+ " Per Call (s)"
+ " Cumulative Time (s)"
+ " Per Call (s)"
)
self.assertEqual(stat_line_list[2], ref_line3)

# Check remaining lines
stat_dict = {}
for line in stat_line_list[3:-1]:
words = line.split()
stat_dict[words[0]] = {
"call_count": int(words[1]),
"total_time": float(words[2]),
"total_per_call": float(words[3]),
"cumulative_time": float(words[4]),
"cumulative_per_call": float(words[5]),
}
bar_dict = stat_dict["bar"]
self.assertEqual(bar_dict["call_count"], 4)
self.assertTrue(bar_dict["total_time"] - (time1 * 4) <= 3e-4)
self.assertTrue(bar_dict["total_per_call"] - time1 <= 3e-4)
self.assertTrue(bar_dict["cumulative_time"] - (time1 * 4) <= 3e-4)
self.assertTrue(bar_dict["cumulative_per_call"] - time1 <= 3e-4)

foo_dict = stat_dict["foo"]
self.assertEqual(foo_dict["call_count"], 2)
self.assertTrue(foo_dict["total_time"] - (time1 + time2) * 2 <= 3e-4)
self.assertTrue(foo_dict["total_per_call"] - (time1 + time2) <= 3e-4)
self.assertTrue(foo_dict["cumulative_time"] - (time2 * 2) <= 3e-4)
self.assertTrue(foo_dict["cumulative_per_call"] - time2 <= 3e-4)

baz_dict = stat_dict["baz"]
ref_total_time = time1 + time2 + time3
self.assertEqual(baz_dict["call_count"], 1)
self.assertTrue(baz_dict["total_time"] - ref_total_time <= 3e-4)
self.assertTrue(baz_dict["total_per_call"] - ref_total_time <= 3e-4)
self.assertTrue(baz_dict["cumulative_time"] - time3 <= 3e-4)
self.assertTrue(baz_dict["cumulative_per_call"] - time3 <= 3e-4)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I move the gtest to pytest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It now looks nicer and easy to understand. Thanks.

Copy link
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Revert unnecessary changes like style (non-logical) to avoid confusion.
  • Rebase and squash.
  • Use assertLessEqual() and alikes.
  • Fix indentation.
  • Take the long lambda for CallProfiler.result() to a static member function in WrapCallProfiler.

Comment on lines 85 to 91
path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
"data", "profiler_python_schema.json")
with open(path, 'r') as schema_file:
path = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
"data",
"profiler_python_schema.json",
)
with open(path, "r") as schema_file:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert the style change. Do not mix style change with logic change in one PR.

A style change is a refactoring change and should use its own PR.

self.assertEqual(words[2], "calls")
self.assertEqual(words[3], "in")
ref_total_time = time1 * 4 + time2 * 2 + time3
self.assertTrue(abs(float(words[4]) - ref_total_time) <= 6e-4)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use assertLessEqual() and alikes.

Comment on lines 199 to 285

def test_get_stat(self):
time1 = 0.5
time2 = 0.1
time3 = 0.2

@profile_function
def bar():
busy_loop(time1)

@profile_function
def foo():
busy_loop(time2)
bar()

@profile_function
def baz():
busy_loop(time3)
foo()

modmesh.call_profiler.reset()
bar()
bar()
foo()
baz()
root_stat = modmesh.call_profiler.stat()

# Check the number of lines
stat_line_list = root_stat.split("\n")
self.assertEqual(len(stat_line_list), 7)

# Check the first line
words = stat_line_list[0].split()
self.assertEqual(words[0], "7")
self.assertEqual(words[1], "function")
self.assertEqual(words[2], "calls")
self.assertEqual(words[3], "in")
ref_total_time = time1 * 4 + time2 * 2 + time3
self.assertTrue(abs(float(words[4]) - ref_total_time) <= 6e-4)
self.assertEqual(words[5], "seconds")

# Check the second line
self.assertEqual(stat_line_list[1], "")

# Check the third line
ref_line3 = (
" Function Name"
+ " Call Count"
+ " Total Time (s)"
+ " Per Call (s)"
+ " Cumulative Time (s)"
+ " Per Call (s)"
)
self.assertEqual(stat_line_list[2], ref_line3)

# Check remaining lines
stat_dict = {}
for line in stat_line_list[3:-1]:
words = line.split()
stat_dict[words[0]] = {
"call_count": int(words[1]),
"total_time": float(words[2]),
"total_per_call": float(words[3]),
"cumulative_time": float(words[4]),
"cumulative_per_call": float(words[5]),
}
bar_dict = stat_dict["bar"]
self.assertEqual(bar_dict["call_count"], 4)
self.assertTrue(bar_dict["total_time"] - (time1 * 4) <= 3e-4)
self.assertTrue(bar_dict["total_per_call"] - time1 <= 3e-4)
self.assertTrue(bar_dict["cumulative_time"] - (time1 * 4) <= 3e-4)
self.assertTrue(bar_dict["cumulative_per_call"] - time1 <= 3e-4)

foo_dict = stat_dict["foo"]
self.assertEqual(foo_dict["call_count"], 2)
self.assertTrue(foo_dict["total_time"] - (time1 + time2) * 2 <= 3e-4)
self.assertTrue(foo_dict["total_per_call"] - (time1 + time2) <= 3e-4)
self.assertTrue(foo_dict["cumulative_time"] - (time2 * 2) <= 3e-4)
self.assertTrue(foo_dict["cumulative_per_call"] - time2 <= 3e-4)

baz_dict = stat_dict["baz"]
ref_total_time = time1 + time2 + time3
self.assertEqual(baz_dict["call_count"], 1)
self.assertTrue(baz_dict["total_time"] - ref_total_time <= 3e-4)
self.assertTrue(baz_dict["total_per_call"] - ref_total_time <= 3e-4)
self.assertTrue(baz_dict["cumulative_time"] - time3 <= 3e-4)
self.assertTrue(baz_dict["cumulative_per_call"] - time3 <= 3e-4)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It now looks nicer and easy to understand. Thanks.

@@ -181,6 +181,11 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapCallProfiler : public WrapBase<WrapC
"instance",
[](py::object const &) -> wrapped_type &
{ return wrapped_type::instance(); })
.def("stat", [](CallProfiler & profiler)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation is off.

{
std::stringstream ss;
profiler.print_statistics(ss);
return ss.str(); })
.def("result", [](CallProfiler & profiler)
{
const RadixTreeNode<CallerProfile> * root = profiler.radix_tree().get_root();
Copy link
Member

@yungyuc yungyuc Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation is off, but it is not a major issue needing to be fixed in this PR.

The function is too long to be reasonably placed in a lambda. Please replace the lambda with a static function in this wrapper class. You can reference the wrapping code for SimpleArray.__setitem__():

.def("__setitem__", &property_helper::setitem_parser)

But you do not need to use a separate helper class like property_helper.

@ThreeMonth03 ThreeMonth03 force-pushed the issue343_pr branch 2 times, most recently from 2e1c8a3 to 0e31b7b Compare July 6, 2024 18:39
Copy link
Collaborator Author

@ThreeMonth03 ThreeMonth03 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Revert unnecessary changes like style (non-logical) to avoid confusion.
  • Rebase and squash.
  • Use assertLessEqual() and alikes.
  • Fix indentation.
  • Take the long lambda for CallProfiler.result() to a static member function in WrapCallProfiler.

@yungyuc I'm ready for next review. Thank you very much.

Comment on lines +185 to +193
.def(
"stat",
[](CallProfiler & profiler)
{
std::stringstream ss;
profiler.print_statistics(ss);
return ss.str();
})
.def("result", &result)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fix the wierd indent, and replace the long lambda function with member function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bravo! Thanks a lot.

Comment on lines +264 to +268
self.assertEqual(bar_dict["call_count"], 4)
self.assertGreaterEqual(bar_dict["total_time"], time1 * 4)
self.assertGreaterEqual(bar_dict["total_per_call"], time1)
self.assertGreaterEqual(bar_dict["cumulative_time"], time1 * 4)
self.assertGreaterEqual(bar_dict["cumulative_per_call"], time1)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The measurement error is too large to set a reasonable threshold, so I refer the above test function and use assertGreaterEqual.

@@ -192,3 +192,95 @@ def foo():

modmesh.call_profiler.reset()
foo()

Copy link
Collaborator Author

@ThreeMonth03 ThreeMonth03 Jul 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I rebase and squash another developing branch issue343 by git rebase -i [the history commit].
  2. At the localhost, I run git checkout -b issue343_pr under the master branch to revert the latest solvcon /modmesh.
  3. I merge the developing branch issue343 to this branch issue343_pr.
  4. I forcely push the commit to origin/issue343_pr to modify the history.

I'm not sure whether I'm wrong to reduce and delete the unnecessary commit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your history contains commits from @tigercosmos @j8xixo12 @YenPeiChen07. That does not look right. Please rebase against master, remove commits not belonging to you, and squash small commits.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your history contains commits from @tigercosmos @j8xixo12 @YenPeiChen07. That does not look right. Please rebase against master, remove commits not belonging to you, and squash small commits.

Fix.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good now. Thanks.

Copy link
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code is getting better. Some maintenance issues remain:

  • Commit history is not clean. Please rebase and squash to make it linear and avoid unnecessarily small commits. It is OK to leave a single commit after squashing, which will be trivially linear.
  • TimeRegistry::detailed_report() function body should not be included in class declaration.

Comment on lines +185 to +193
.def(
"stat",
[](CallProfiler & profiler)
{
std::stringstream ss;
profiler.print_statistics(ss);
return ss.str();
})
.def("result", &result)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bravo! Thanks a lot.

@@ -192,3 +192,95 @@ def foo():

modmesh.call_profiler.reset()
foo()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your history contains commits from @tigercosmos @j8xixo12 @YenPeiChen07. That does not look right. Please rebase against master, remove commits not belonging to you, and squash small commits.

@@ -156,6 +166,39 @@ class TimeRegistry
return ostm.str();
}

std::string detailed_report() const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function body is long. If we do not give it an implementation file (.cpp), it should be taken outside the class declaration.

(If you are not sure what does it mean, see #377 (comment) .)

@ThreeMonth03 ThreeMonth03 force-pushed the issue343_pr branch 2 times, most recently from 26a0708 to 3f18375 Compare July 7, 2024 22:59
Copy link
Collaborator Author

@ThreeMonth03 ThreeMonth03 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Commit history is not clean. Please rebase and squash to make it linear and avoid unnecessarily small commits. It is OK to leave a single commit after squashing, which will be trivially linear.
  • TimeRegistry::detailed_report() function body should not be included in class declaration.

@yungyuc I'm ready for next review.

@@ -192,3 +192,95 @@ def foo():

modmesh.call_profiler.reset()
foo()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your history contains commits from @tigercosmos @j8xixo12 @YenPeiChen07. That does not look right. Please rebase against master, remove commits not belonging to you, and squash small commits.

Fix.

Comment on lines +1 to +69
/*
* Copyright (c) 2023, Yung-Yu Chen <yyc@solvcon.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#include <modmesh/toggle/profile.hpp>

namespace modmesh
{

std::string TimeRegistry::detailed_report() const
{
std::ostringstream ostm;
/// Header
ostm
<< std::setw(40) << total_call_count()
<< " function calls in " << total_time()
<< " seconds" << std::endl;
ostm
<< std::endl
<< std::setw(40) << "Function Name"
<< std::setw(25) << "Call Count"
<< std::setw(25) << "Total Time (s)"
<< std::setw(25) << "Per Call (s)"
<< std::setw(25) << "Cumulative Time (s)"
<< std::setw(25) << "Per Call (s)"
<< std::endl;

/// Body
for (auto it = m_entry.begin(); it != m_entry.end(); ++it)
{
ostm
<< std::setw(40) << it->first
<< std::setw(25) << it->second.count()
<< std::setw(25) << it->second.time()
<< std::setw(25) << it->second.time() / it->second.count()
<< std::setw(25) << it->second.ctime()
<< std::setw(25) << it->second.ctime() / it->second.count()
<< std::endl;
}
return ostm.str();
}

} /* end namespace modmesh */

// vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had tried to define the function in profile.hpp, but it returned the error with multiple definitions of function. Therefore, I add a new file profile.cpp to avoid the error message.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When putting outside the declaration in the header/include file, a function needs to be made inline to avoid multiple definition when the header file is included in multiple compilation units: https://learn.microsoft.com/en-us/cpp/cpp/overview-of-member-functions?view=msvc-170 .

In this PR it is OK to use either inline or an implementation .cpp file for the definition of detailed_report(). When using the implementation file, it would be nice to follow the guideline of IWYU, but we can worry about it in the future.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When putting outside the declaration in the header/include file, a function needs to be made inline to avoid multiple definition when the header file is included in multiple compilation units: https://learn.microsoft.com/en-us/cpp/cpp/overview-of-member-functions?view=msvc-170 .

In this PR it is OK to use either inline or an implementation .cpp file for the definition of detailed_report(). When using the implementation file, it would be nice to follow the guideline of IWYU, but we can worry about it in the future.

Thanks, it is a very useful suggestion.

Copy link
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ThreeMonth03 to make everything in a clear commit 3f18375 . It can use a complete and concise commit log. You can take a look at https://github.com/solvcon/modmesh/pull/360/commits for an example for writing the commit log. Your current log does not help a maintainer to understand what you did:

Recover the report and add detailed report in RegistryEntry

Fix the CI warning.

Fix the CI warning.

Expose the info of gtest.

Fix the reading bug of function.

Split the long line and remove redundant comment.

Fix the format, and test by python

Reduce the long line for python

Add the tolerance for time threshold

Add the tolerance for time threshold

Add the tolerance for time threshold

Fix the endl

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

modify review code

Comment on lines +1 to +69
/*
* Copyright (c) 2023, Yung-Yu Chen <yyc@solvcon.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#include <modmesh/toggle/profile.hpp>

namespace modmesh
{

std::string TimeRegistry::detailed_report() const
{
std::ostringstream ostm;
/// Header
ostm
<< std::setw(40) << total_call_count()
<< " function calls in " << total_time()
<< " seconds" << std::endl;
ostm
<< std::endl
<< std::setw(40) << "Function Name"
<< std::setw(25) << "Call Count"
<< std::setw(25) << "Total Time (s)"
<< std::setw(25) << "Per Call (s)"
<< std::setw(25) << "Cumulative Time (s)"
<< std::setw(25) << "Per Call (s)"
<< std::endl;

/// Body
for (auto it = m_entry.begin(); it != m_entry.end(); ++it)
{
ostm
<< std::setw(40) << it->first
<< std::setw(25) << it->second.count()
<< std::setw(25) << it->second.time()
<< std::setw(25) << it->second.time() / it->second.count()
<< std::setw(25) << it->second.ctime()
<< std::setw(25) << it->second.ctime() / it->second.count()
<< std::endl;
}
return ostm.str();
}

} /* end namespace modmesh */

// vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When putting outside the declaration in the header/include file, a function needs to be made inline to avoid multiple definition when the header file is included in multiple compilation units: https://learn.microsoft.com/en-us/cpp/cpp/overview-of-member-functions?view=msvc-170 .

In this PR it is OK to use either inline or an implementation .cpp file for the definition of detailed_report(). When using the implementation file, it would be nice to follow the guideline of IWYU, but we can worry about it in the future.

@@ -192,3 +192,95 @@ def foo():

modmesh.call_profiler.reset()
foo()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good now. Thanks.

Add detailed report function in RegistryEntry

Write the pytest for statistic function
@ThreeMonth03
Copy link
Collaborator Author

ThreeMonth03 commented Jul 8, 2024

Thanks @ThreeMonth03 to make everything in a clear commit 3f18375 . It can use a complete and concise commit log. You can take a look at https://github.com/solvcon/modmesh/pull/360/commits for an example for writing the commit log. Your current log does not help a maintainer to understand what you did:

Recover the report and add detailed report in RegistryEntry

Fix the CI warning.

Fix the CI warning.

Expose the info of gtest.

Fix the reading bug of function.

Split the long line and remove redundant comment.

Fix the format, and test by python

Reduce the long line for python

Add the tolerance for time threshold

Add the tolerance for time threshold

Add the tolerance for time threshold

Fix the endl

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

modify review code

Fix.
@yungyuc I've modified the comment messages.

@ThreeMonth03 ThreeMonth03 reopened this Jul 8, 2024
@ThreeMonth03
Copy link
Collaborator Author

ThreeMonth03 commented Jul 8, 2024

Thanks @ThreeMonth03 to make everything in a clear commit 3f18375 . It can use a complete and concise commit log. You can take a look at https://github.com/solvcon/modmesh/pull/360/commits for an example for writing the commit log. Your current log does not help a maintainer to understand what you did:

Recover the report and add detailed report in RegistryEntry

Fix the CI warning.

Fix the CI warning.

Expose the info of gtest.

Fix the reading bug of function.

Split the long line and remove redundant comment.

Fix the format, and test by python

Reduce the long line for python

Add the tolerance for time threshold

Add the tolerance for time threshold

Add the tolerance for time threshold

Fix the endl

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

Fix the review commit

modify review code

Fix. @yungyuc I've modified the comment messages.

@yungyuc The github action is finished, and you could review the code when you are available.

@yungyuc
Copy link
Member

yungyuc commented Jul 8, 2024

LGTM. Thanks, @ThreeMonth03 .

@yungyuc yungyuc merged commit 14b3236 into solvcon:master Jul 8, 2024
26 checks passed
@yungyuc yungyuc linked an issue Jul 8, 2024 that may be closed by this pull request
2 tasks
@ThreeMonth03 ThreeMonth03 deleted the issue343_pr branch July 14, 2024 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request performance Profiling, runtime, and memory consumption
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Prototype profiler object serialization using JSON
3 participants