-
Notifications
You must be signed in to change notification settings - Fork 6
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 statistics to dump #11
Conversation
timed/src/lib.rs
Outdated
Finish(Duration), | ||
} | ||
|
||
impl Phase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to print to "B" and "E" as these are the only two phases that chrome://tracing takes into account for this event type:
See the specs here: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change it for now, but please keep in mind that the console output shouldn't be less readable on behalf of another feature (a.k.a chrome tracing)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. The main purpose of these structs was originally for chrome tracing, however if it grows to enable another use case, we can parametrize how it prints out. For tracing it can print like "B" and "E". I don't know what other use case you have in mind for printing out these structs, but if there is another we can discuss. As far as it goes for now, they are only useful for chrome tracing.
timed_proc_macros/src/lib.rs
Outdated
@@ -119,18 +119,19 @@ pub fn timed(args: TokenStream, input: TokenStream) -> TokenStream { | |||
block: body, | |||
.. | |||
} = &function; | |||
|
|||
let name = &function.sig.ident; | |||
let name = format!("{}::{}", std::module_path!(), &function.sig.ident); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just tested this out, the output of:
cargo run --package tests --example demo_tracing
is:
[
{ "pid": 0, "ts": 1603479130104696, "ph": "Start", "name": "timed_proc_macros::foo" },
{ "pid": 0, "ts": 1603479130104699, "ph": "Start", "name": "timed_proc_macros::bar" },
{ "pid": 0, "ts": 1603479130104700, "ph": "Start", "name": "timed_proc_macros::baz" },
{ "pid": 0, "ts": 1603479130104752, "ph": "Finish", "name": "timed_proc_macros::baz" },
{ "pid": 0, "ts": 1603479130104756, "ph": "Finish", "name": "timed_proc_macros::bar" },
{ "pid": 0, "ts": 1603479130104760, "ph": "Start", "name": "timed_proc_macros::baz" },
{ "pid": 0, "ts": 1603479130104765, "ph": "Finish", "name": "timed_proc_macros::baz" },
{ "pid": 0, "ts": 1603479130104768, "ph": "Finish", "name": "timed_proc_macros::foo" }
]
It should in fact be demo_tracing::foo
, not timed_proc_macros::foo
You should add this inside: quote!
, I just tried:
let result = quote! {
#(#attrs)*
#vis #sig {
#tracing_begin
let start = std::time::Instant::now();
let res = { #body };
let elapsed = start.elapsed();
let path = std::module_path!();
println!("Module path {:?}", path);
#print_duration
#tracing_end
res
}
};
prints:
Module path "demo_tracing"
timed/src/lib.rs
Outdated
|
||
match stats { | ||
TracingStats::None => {} | ||
TracingStats::Percentage => {} | ||
TracingStats::Statistics => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please sort it descending by total time? I think the most useful out of this is to know which function took longest so you know what you need to optimize.
tests/src/tests_tracing.rs
Outdated
@@ -41,7 +42,7 @@ pub mod foo { | |||
#[test] | |||
#[timed::timed(tracing = true)] | |||
fn test_tracing() { | |||
timed::init_tracing!("Test"); | |||
timed::init_tracing!("Test", TracingStats::Statistics); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add a separate test for this? I would like to keep this one for a single feature.
Thanks for this. I realise this was not fully spec'd out, sorry for the back and forth. |
- moved module_path call to inside quote! - changed phase to B and E - separated statistics test
It's cool man, happy to help :) |
… `tests_tracing.rs`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks good overall, I am happy with the decisions you took. I think removing the macro and using a builder pattern is a better pattern to use.
Also, can you please run cargo fmt
and cargo clippy
?
Last thing is testing: can you think of a way we could test this? Can we somehow use the closures to tracing and statistics to write into a static variable which we can inspect afterwards? I guess it would also be enough to just test the TRACES static and ensure it contains the Hops, just unit test the statistics processing based on simple inputs rather than a whole program.
Writing just a two three skeleton tests that check your newly implemented utilities is enough.
timed/src/lib.rs
Outdated
Finish(Duration), | ||
} | ||
|
||
impl Phase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. The main purpose of these structs was originally for chrome tracing, however if it grows to enable another use case, we can parametrize how it prints out. For tracing it can print like "B" and "E". I don't know what other use case you have in mind for printing out these structs, but if there is another we can discuss. As far as it goes for now, they are only useful for chrome tracing.
timed/src/lib.rs
Outdated
@@ -91,60 +127,99 @@ impl Trace { | |||
} | |||
} | |||
|
|||
pub fn dump(id: &str, processor: fn(&str), stats: &TracingStats) { | |||
let start = std::time::Instant::now(); | |||
pub fn dump(id: &str, options: &TraceOptions) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you can make it take &self and stop sending these params. Just a recommendation, not compulsory. Not sure if there is a performance impact.
timed/src/lib.rs
Outdated
|
||
impl TraceOptions { | ||
pub fn new() -> TraceOptions { | ||
TraceOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this. I think this fits the builder pattern, I think you can straight away go ahead and use this https://docs.rs/derive_builder/0.9.0/derive_builder/
Also, that makes the trace_init() not necessary anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change it, wasn't sure that you'll like it haha
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Last thing is testing: can you think of a way we could test this? Can we somehow use the closures to tracing and statistics to write into a static variable which we can inspect afterwards? I guess it would also be enough to just test the TRACES static and ensure it contains the Hops, just unit test the statistics processing based on simple inputs rather than a whole program.
Definitely, and tbh I think the closures should receive a ChromeTrace
/ Statistics
structs instead of &str
s. This will cause the whole design to 'smell' more event driven, in the sense that the closures will get notified on each new ChromeTrace
entry and will act upon it as they wish.
This will also make it far more testable, asserting each ChromeTrace
in the closure.
I usually work on side projects on the weekends, so feel free to checkout this branch and implement it if you find some time.
timed/src/lib.rs
Outdated
fn drop(&mut self) { | ||
Trace::dump(&self.id, self.processor, &self.stats); | ||
fn print_statistics(processor: &fn(&str), stats_map: &HashMap<String, Vec<Duration>>, total_time_nanos: u128) { | ||
struct FnStats { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not familiar with this idiom. Does this type definition get instantiated at every function invocation? Any reason you do not declare it above without the pub qualifier?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a nice feature that lets you define a struct (or any type) within a scope. I use it when the struct is only meaningful for the current scope and nowhere else, like here: FnStats
only exists so I can sort the statistics later on, and nothing else. It's definition is done at compile time, just like any other struct.
timed/src/lib.rs
Outdated
pub fn finish(self) { | ||
self.dump(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I removed the init_tracing!
macro, I replaced it with let _ = Trace::new(...)
in the build_named
function of TracingOptions
and got empty tracings, this was due to the drop that was called before it should have been called (due to my changes).
This made me think that the drop behaviour was too implicit, that's why I replaced it. Please let me know if you disagree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, _
has a special treatment that it drops immediately. that's why maybe it helps when it's hidden behind a macro. However, calling it _trace
will make it drop at the end of the block, so that should work.
I have mixed feelings, I want a clean api and I think that combining a macro with a pseudo builder pattern is unidiomatic, a bit clunky. By any means, this is not bad feedback, I am just unsure. If we can keep macros simple and clean, all good, but if not, i'd rather keep it simple.
I do think that if you make users init the variables the normal way, most of them will use _
as a name so that makes the api not intuitive, users won't know they can rely on Drop.
I think finish
is a good choice, I don't have a better idea. Forcing users to call finish will ensure they name their variables properly and therefore it doesn't drop prematurely.
Good job with this, really good stuff, thanks a lot.
I got drifted into a major refactor and i dont really have the time to pull this one through with all the tests etc. If you want to discuss it further we can talk on reddit or something. |
Will pick this up tomorrow. Sorry, has a busy period. Thanks for your work so far! |
@yonip23 thanks for your help on this! |
@yonip23 put a PR to add your name as author to the |
This PR addresses (some of) the next issues:
#9
#10
Now when calling
timed::init_tracing!("Test", TracingStats::Statistics);
something like this will be added to your dump: