Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
performance
PERForate
Commits
77a5154e
Commit
77a5154e
authored
Oct 14, 2020
by
Pádraig Ó Conbhuí
Browse files
Add support for passing output options through the environment
parent
7e449d27
Pipeline
#2535
failed with stages
in 1 minute and 24 seconds
Changes
2
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/perforate/scoped_trace.bench.cpp
View file @
77a5154e
...
...
@@ -4,13 +4,62 @@
#include
<catch2/catch.hpp>
#include
<numeric>
#include
<chrono>
#include
<vector>
// Muck up optimization
volatile
extern
size_t
v
;
volatile
size_t
v
=
10
;
// Keep the result of t
template
<
typename
T
>
void
use
(
const
T
&
t
)
{
v
=
std
::
accumulate
(
t
.
begin
(),
t
.
end
(),
size_t
(
0
));
v
=
10
;
}
// Generate a vector using v so it can't be
// optimized by the compiler (heap elision etc.)
static
std
::
vector
<
size_t
>
to_measure
()
{
std
::
vector
<
size_t
>
value
;
value
.
resize
(
v
);
for
(
size_t
i
=
0
;
i
<
v
;
i
++
)
{
value
[
i
]
=
v
*
i
;
}
return
value
;
}
TEST_CASE
(
"perforate::scoped_trace"
,
"[scoped_trace][benchmark]"
)
{
BENCHMARK
(
"std::chrono::high_resolution_clock::now()"
)
{
return
std
::
chrono
::
high_resolution_clock
::
now
();
};
BENCHMARK
(
"scoped_trace overhead"
)
{
PERFORATE_SCOPED_TRACE
(
nullptr
);
};
// Measure the "PERFORATE_SCOPED_TRACE overhead to a loop with PERForate"
// Assume it's around 100ns.
{
PERFORATE_SCOPED_TRACE
(
"perforate::scope_trace loop overhead x1e6"
);
for
(
size_t
i
=
0
;
i
<
1000000
;
i
++
)
{
PERFORATE_SCOPED_TRACE
(
"perforate::scope_trace overhead measured"
);
}
}
// Measure difference between PERFORATE_SCOPE_TRACE inside and outside a loop
{
// Outside loop
{
PERFORATE_SCOPED_TRACE
(
"external measurement x1e6"
);
for
(
size_t
i
=
0
;
i
<
1000000
;
i
++
)
{
use
(
to_measure
());
}
}
// Inside loop
for
(
size_t
i
=
0
;
i
<
1000000
;
i
++
)
{
PERFORATE_SCOPED_TRACE
(
"internal measurement"
);
use
(
to_measure
());
}
}
}
src/perforate/scoped_trace.hpp
View file @
77a5154e
...
...
@@ -2,6 +2,7 @@
#define PERFORATE_TRACE_HPP
#include
<algorithm>
#include
<array>
#include
<atomic>
#include
<chrono>
#include
<iostream>
...
...
@@ -11,8 +12,17 @@
#include
<vector>
#include
<cassert>
#include
<cctype>
#include
<cinttypes>
#include
<cstddef>
#include
<cstdlib>
// Some configuration options
#ifndef PERFORATE_OPTIONS_ENABLED_DEFAULT
#define PERFORATE_OPTIONS_ENABLED_DEFAULT true
#endif // PERFORATE_OPTIONS_ENABLED_DEFAULT
// Optional C++ version specific bits
#if __cplusplus >= 201703L
...
...
@@ -139,30 +149,124 @@ void print_range_stats(Stream &stream, const Stats &stats) {
<<
" call(s)
\n
"
;
}
template
<
typename
Stream
,
typename
StatsPtr
List
>
template
<
typename
Stream
,
typename
StatsPtr
Iterator
>
void
print_range_stats_list
(
Stream
&
stream
,
const
StatsPtrList
&
stats_ptr_list
)
{
StatsPtrIterator
stats_ptrs_begin
,
StatsPtrIterator
stats_ptrs_end
)
{
const
auto
empty
=
[](
const
auto
&
stats
)
{
return
stats
->
range_name
()
==
nullptr
;
};
const
auto
registry_empty
=
std
::
all_of
(
st
d
::
cbegin
(
stats_ptr_list
)
,
std
::
cend
(
stats_ptr
_list
)
,
empty
);
const
auto
registry_empty
=
std
::
all_of
(
st
ats_ptrs_begin
,
stats_ptr
s_end
,
empty
);
if
(
!
registry_empty
)
{
stream
<<
"
\n
------------"
<<
"
\n
Range Stats:"
<<
"
\n
------------
\n
"
;
for
(
const
auto
&
s
:
stats_ptr_list
)
{
if
(
s
->
range_name
()
==
nullptr
)
for
(;
stats_ptrs_begin
!=
stats_ptrs_end
;
++
stats_ptrs_begin
)
{
if
(
(
*
stats_ptrs_begin
)
->
range_name
()
==
nullptr
)
continue
;
print_range_stats
(
stream
,
*
s
);
print_range_stats
(
stream
,
*
*
stats_ptrs_begin
);
}
stream
<<
"
\n
------------
\n
"
;
}
}
template
<
typename
StatsPtrIterator
>
void
sort_range_stats_list
(
StatsPtrIterator
stats_ptrs_begin
,
StatsPtrIterator
stats_ptrs_end
,
const
std
::
string
&
sort_config
)
{
enum
sort_fields
{
none
=
0
,
title
,
total
,
per_call
,
calls
};
// Get "config" strings from sort_config, separated by ","
std
::
array
<
sort_fields
,
4
>
configs
{};
// Start at first non-space character
size_t
pos
=
sort_config
.
find_first_not_of
(
" ,"
,
0
);
size_t
next_pos
=
pos
;
size_t
configs_pos
=
0
;
while
(
pos
!=
sort_config
.
npos
&&
configs_pos
!=
configs
.
size
())
{
// Find the end of this word.
// If there are no more separators, next_pos is the end of the string
next_pos
=
sort_config
.
find_first_of
(
" ,"
,
pos
);
if
(
next_pos
==
sort_config
.
npos
)
next_pos
=
sort_config
.
size
();
// Get this word as a std::string
std
::
string
config_str
=
sort_config
.
substr
(
pos
,
(
next_pos
-
pos
));
// Set the config enum from the parsed config
configs
[
configs_pos
]
=
([
&
]
{
if
(
config_str
==
"none"
)
return
none
;
if
(
config_str
==
"title"
)
return
title
;
if
(
config_str
==
"total"
)
return
total
;
if
(
config_str
==
"per_call"
)
return
per_call
;
if
(
config_str
==
"calls"
)
return
calls
;
return
none
;
}());
configs_pos
++
;
// Find the start of the next word.
// If there are no more words, it will return npos and the loop breaks
pos
=
sort_config
.
find_first_not_of
(
" ,"
,
next_pos
);
}
const
auto
range_gt
=
[
&
](
const
range_stats
*
a
,
const
range_stats
*
b
)
{
for
(
const
auto
c
:
configs
)
{
switch
(
c
)
{
case
none
:
continue
;
case
title
:
if
(
a
->
range_name
()
>
b
->
range_name
())
return
true
;
if
(
a
->
range_name
()
<
b
->
range_name
())
return
false
;
break
;
case
total
:
if
(
a
->
accumulated_time
()
>
b
->
accumulated_time
())
return
true
;
if
(
a
->
accumulated_time
()
<
b
->
accumulated_time
())
return
false
;
break
;
case
per_call
:
{
const
auto
a_call_count
=
std
::
max
<
int64_t
>
(
a
->
call_count
(),
1
);
const
auto
b_call_count
=
std
::
max
<
int64_t
>
(
b
->
call_count
(),
1
);
const
auto
a_per_call
=
a
->
accumulated_time
().
count
()
/
a_call_count
;
const
auto
b_per_call
=
b
->
accumulated_time
().
count
()
/
b_call_count
;
if
(
a_per_call
>
b_per_call
)
return
true
;
if
(
a_per_call
<
b_per_call
)
return
false
;
break
;
}
case
calls
:
if
(
a
->
call_count
()
>
b
->
call_count
())
return
true
;
if
(
a
->
call_count
()
<
b
->
call_count
())
return
false
;
break
;
}
}
return
false
;
};
std
::
sort
(
stats_ptrs_begin
,
stats_ptrs_end
,
range_gt
);
}
class
range_stats_print_on_exit_policy
{
std
::
vector
<
range_stats
*>
m_range_stats_list
{};
...
...
@@ -179,8 +283,46 @@ public:
return
m_tag_range_stats
;
}
static
std
::
string
default_sort_config
()
{
return
"total, title, per_call, calls"
;
}
~
range_stats_print_on_exit_policy
()
{
print_range_stats_list
(
std
::
cout
,
m_range_stats_list
);
const
bool
output_enabled
=
([]
{
const
char
*
output_enabled_env
=
std
::
getenv
(
"PERFORATE_ENABLE"
);
if
(
output_enabled_env
==
nullptr
)
return
PERFORATE_OPTIONS_ENABLED_DEFAULT
;
// Get env as lowercase std::string
std
::
string
output_enabled_str
=
output_enabled_env
;
std
::
transform
(
output_enabled_str
.
begin
(),
output_enabled_str
.
end
(),
output_enabled_str
.
begin
(),
[](
unsigned
char
c
)
{
return
std
::
tolower
(
c
);
});
// Any of these will enable perforate output, anything else
// it will remain silent
if
(
output_enabled_str
==
"1"
)
return
true
;
if
(
output_enabled_str
==
"true"
)
return
true
;
if
(
output_enabled_str
==
"t"
)
return
true
;
return
false
;
}());
if
(
!
output_enabled
)
return
;
const
std
::
string
sort_config
=
([]
{
const
char
*
sort_config_env
=
std
::
getenv
(
"PERFORATE_STATS_SORT"
);
if
(
!
sort_config_env
)
return
default_sort_config
();
return
std
::
string
{
sort_config_env
};
}());
sort_range_stats_list
(
m_range_stats_list
.
begin
(),
m_range_stats_list
.
end
(),
sort_config
);
print_range_stats_list
(
std
::
cout
,
m_range_stats_list
.
cbegin
(),
m_range_stats_list
.
cend
());
}
};
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment