#counter #events #debugging #purpose #filter #foo #key

counters

Simple utility to count events for debugging purposes

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

#9 in #purpose

MIT/Apache

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

~105–320KB