5 releases (breaking)
0.5.0 | Feb 26, 2019 |
---|---|
0.4.0 | Feb 22, 2019 |
0.3.0 | Feb 22, 2019 |
0.2.0 | Feb 22, 2019 |
0.1.0 | Feb 21, 2019 |
#19 in #purposes
19KB
343 lines
Utilities to easily count events for debuging puposes
This can be useful to gather statistics about how often different code paths are run.
Overhead
Counters are relatively low overhead but not free (Cost of looking up a FxHashMap using static string slices as key). Using counters may affect perfomance measurements.
Optimizing out
When using the types DebugCounters
and DebugTable
instead of Counters
and
Table
, the implementation is empty unless the debug_counters
feature flag is
enabled.
This way the code for counting events can be kept while opting out of its overhead
in shipping and profiling build configurations.
Dummy trait implementations
In order to be embedded in structures that implement Serialize
and Deserialize
,
Counters
and DebugCounters
have dummy implementations of the traits that can be
emabled with the dummy_serialization
feature flag.
Similarly, the following traits have dummy implementations:
Eq
,PartialEq
: Always true.Hash
: Does not contribute to the hash.
These dummy implementations are meant to not change the behavior of the embedding structures.
Example
In the example below we have a function do_the_thing
which we determined to
be expensive (using a profiler). We would like to get some insight into how
often the function is run and how often we take the slow and fast paths.
use counters::Counters;
use counters::filters::*;
struct Foo {
counters: Counters,
}
impl Foo {
// This method is not mutable (&self), however we can still update
// the counters because they use internal mutability.
fn do_the_thing(&self, n: u32) -> u32 {
self.counters.event("do_the_thing");
if n % 17 == 0 {
self.counters.event("fast path A");
return self.foo();
}
if n % 56 == 0 {
self.counters.event("fast path B");
return self.bar();
}
self.counters.event("slow path");
return self.baz();
}
fn do_all_of_the_things(&mut self) {
self.counters.reset_all();
for i in 0..100 {
self.do_the_thing(i);
}
// We can use filters to accumulate the values of several counters.
let total_fast_path = self.counters.accumulate(Contains("fast path"));
let slow_path = self.counters.get("do_the_thing") - total_fast_path;
// Set the value of a counter.
self.counters.set("slow path", slow_path);
// This prints the following to stdout:
// slow path: 93
// fast path A: 6
// fast path B: 1
// do_the_thing: 100
self.counters.print_to_stdout(All);
}
// Let's pretend the methods below do interesting things...
fn foo(&self) -> u32 { 0 }
fn bar(&self) -> u32 { 0 }
fn baz(&self) -> u32 { 0 }
}
Dependencies
~96–295KB