-
-
Notifications
You must be signed in to change notification settings - Fork 27
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
File output for benchmark results. #42
base: main
Are you sure you want to change the base?
Conversation
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.
A few obvious bits to fix, will in the morning/8 hrs.
@nvzqz I have some free time this weekend to make any changes to this PR you'd like. |
a633453
to
2068160
Compare
Instead of overwriting the specified file on each run, This would solve your filename problem by introducing a simple single-file format for collecting multiple benchmark results.
|
@nopeslide said:
I really like this idea, as long as the file is truncated at the beginning of the multi-bench run: # first run
cargo bench -q -p examples -- --file=result.json
# second run
cargo bench -q -p examples -- --file=result.json
# result.json should only contain the results from the 2nd run,
# as the 2nd run truncated the file prior to running the multi-bench series. I am not sure if you have the state or necessary data to do the truncation a single time at the start of the run, but, hopefully you do. I think is a good suggestion also from the point of view of dropping all the data into a single file rather than multiple file names based upon the test-name etc. I think it would be surprising to users if the file did not get truncated at first, and would continue to grow throughout multiple independent command-line invocations. Thank you for writing the PR @OliverKillane |
I cloned the branch by @OliverKillane and made the mods to do the append: But, it doesn't appear there is a hook or place where information about the first This would be a little bit unusual in my opinion, but it's not the end of the world. |
2068160
to
f5e7954
Compare
Thanks for the comments @nopeslide and @cameronelliott . One file is simpler and imo therefore better.
@nvzqz has yet to comment, so I'll hold off having fun with changes to this PR until there is an agreed output/design. In the meantime we can all enjoy out own forks , or someone add yet another file output PR 😂. |
A quick note, maybe you mentioned this above, or are already aware of it: When I do a clean checkout of your fork @OliverKillane and run like so, I get an error about
I haven't looked into it further, so far. Maybe I am overlooking something obvious? |
@cameronelliott I am getting this also. Adding to PR tasks # produces result, but also 'error: Unrecognized option: 'file'' message
cargo bench -q -p examples -- --file=x.x
# perfectly fine
DIVAN_WRITE_FILE=x.x cargo bench -q -p examples |
@cameronelliott removing from PR description this is a bug for other divan options. Example:git switch main
cargo bench -p examples -- --bytes-format=decimal
...output
error: Unrecognized option: 'bytes-format'
error: bench failed, to rerun pass `-p examples --bench util` Root Cause:When passing arguments by After all the divan benchmark binaries are run, one more runs: Running benches/util.rs (target/release/deps/util-276e87b7c0578c78)` This should not be run, in fact the comment says so (here). Because SolutionSee #47 |
Hey @OliverKillane, you probably saw already, but I submitted a PR to your repo with these features: (simple, but maybe helpful)
|
Append when writing Json, also write Jsonlines newline-delimited JSON
I see @cameronelliott & I've approved. We still might want to add the following:
I need to sleep now. Thanks for the contrib |
Re: truncation. From my perspective, I say let's skip it. Re: include benchmark binary name in file. I'd also vote to skip this too. Re: Add jsonlines output explainer to json help @OliverKillane Nice work! This PR is just what I was looking for! We got some Json! Onward and upward! 🚀 |
Hey @nopeslide @OliverKillane, I tried running the Json output through a Json to Rust type creator. It works, but the types are based upon the names of the benchmarks. This basically means you cannot use strongly typed structs for manipulating the data on the reader/ingress side. This makes working with the Json in memory difficult and unwieldy. This means you can't use schema converters as is typically done in Rust/Js/Go/etc/etc Tomorrow I should wrap a PR proposal that output using a single type, so schema converters can work. This also makes CSV output it trivial. (I will include this) If we go with the current tree output:
My proposal will allow:
Hopefully you are both okay to review this and give me feedback when I get it ready (~24 hours) |
@cameronelliott Keeping representing as a tree allows us to keep nesting information easily available and is main difference with this PR from #29 Divan benchmarks can be nested at different depths, and by parameters, when converting to a flat approach (e.g. vector of rows as you've suggested) the benchmark key becomes the issue:
If we want easy parsing to rust input as appears to be your rust test case, then I suggest the following:
For other outputs, e.g. html view, nesting is also important. |
I agree the straight json trees can have a lot of value. But! I also think flattened json can have a lot of value too, many developers use json to types converters. json_typegen json-to-go Would you accept a PR or potentially accept a PR with an additional format 'json_flattened' ? This way developers/consumers would have their choice about the easiest way for them to consume the json:
|
It really depends how @nvzqz wants this done. I think keeping the internal representation as a tree, and just flattening for a flat output (as will be needed for csv in future) is best. Flattening the tree is super easy, so I've pushed this:
cargo bench -q -p examples -- --file=x.x --format=jsonflat Can see the keys, here from python # in interpreter
import json
lines = []
with open('examples/x.x', 'r') as f:
for l in f: # each line is json
lines.append(json.loads(line))
lines[0]['benchmarks'].keys() To get:
|
Json uses name/value pairs. If the test name path gets stored in the 'name' part of json. End-users can not practically use tools to convert json to their language types.
@OliverKillane thanks for doing that. I've submited a PR to you to do the following:
what needs to happen to support the dozens or hundreds of tools for converting json to concrete types for languages like Python, Go, Rust, Js, Ts, etc, etc? : |
To others reading along, I think the json flat output could use more review and possibly more tweaks. There is the option of star or snowflake like schemas, or just flattening down the json a little more. But what is in this PR, if @OliverKillane accepts my PR to his fork, is now usable by those who are using json-to-types conversion tools for rapid development, ie, using Go, Python, Rust or a dozen other languages. It would be good to hear from the maintainers on vision and goals before investing more time into this. (And maybe merging this PR first) Big thanks to @OliverKillane for pushing this forward, and writing code to do flatjson also. |
Generate jsonflat friendly to json-to-types conversion
Amen, I've sent an email to @nvzqz for some input. Thanks for the PRs to this PR @cameronelliott |
Two issues have revealed themselves using this PR by @OliverKillane with my mini-PRs for flat-json. (Also, thanks again to Oliver, for submitting this useful PR)
This PR is still usable if the consumer is assuming the correct units, but that seems like a poor possibly problematic assumption in the long term. (let's make robust Json, yeah?) Idea: It seems like making the units part of the name/value name, might be sufficient. Instead of The output of defaults values can, again, be handled by consumers, but, again, it seems like we could do better by pruning all those default/zero branches and the See this example:
|
One other nit that is worth mentioning, is the names on the internal data structures are not a great fit for some of the output:
The fastest and slowest labels there really should be |
Also, I think it is worthwhile mentioned there is an issue with outputting counters. Is it possible to get this in the output:
Which is not valid Json. |
@cameronelliott I think the sufix should be inside the data somehow?, not to sure how I would fell about it being part of the name. |
@FilipAndersson245 The problem I see with putting it in the value part of the JSON, is that it would mean turning the JSON numbers into strings. I suppose that’s a valid option. But it means for reading those values becomes extremely more complex on the parsing/reading side. You no longer have ‘20’ but now “20 ns” |
wouldent it be better to have something like
I agree that the time should only be a digit. |
@FilipAndersson245 @cameronelliott The information we need for times is:
I don't think we need this on a per-result basis
Furthermore I don't think we even need unit selection, just use that divan records in (picoseconds). Currently this is done in the Given the aim of the json output should be for simple, easy to load (i.e. into other scripts) output of benchmarks (not pretty or human readable json). Hence having all times output be picoseconds is the simplest. |
I agree that picosecond output would probably be both best and simplest, converting to different units is simple to do on consumer side. |
I think this is actually valid json:
If this is causing issues with parsers you are using on your end, maybe we can change the format here to appease them? |
This is currently the implementation chosen. The description is included in all json generated, for example for the json output provides: {
"description" : "Generated by Divan: Time is in picoseconds.",
...
} And flatjson provides: {
"description" : "Generated by Divan: Time is in picoseconds, only benchmarks with results (not empty or ignored) are present.",
...
} Idea being: the tool's output documents itself, for those to busy to look at the (currently nonexistent) docs Could remove and document elsewhere, no preference from me. |
Aha, sorry I missed that :) |
@nvzqz would love to see this merged! |
This PR is dragging on, at 86 days open now. I'm new to open source contribution, so not sure on etiquette/want review but not to be rude about this (@nvzqz might be busy, ill, on holiday?), or if this is not normal? Any suggestions @cameronelliott @FilipAndersson245 ? Is there a process I've missed? I'll keep using my fork, but I can't promise to be quick with rebasing against updates in divan. |
Sorry to everyone for the delay. I've been dealing with other priorities in my life that have made it difficult to spend time on open source. I have a pending review going that I promise to submit by Sunday. Thanks for the patience. ❤️ |
@nvzqz no stress, I understand - just good to know its not been lost😂, your review is much appreciated & I'll get to improvements as soon as its submitted! |
Any chance this still might get looked at? 😀 |
@OliverKillane my plan is to push some changes to your branch this week and then do a squash commit with everyone's changes. The route I decided to go with is a bit different from your changes but based on your work. I'm currently leaning towards starting with a single canonical flat JSON format. I'll do an alpha/beta While I recognize the benefit of a hierarchical/recursive/nested structure in dynamic languages, it doesn't seem to work well in most JSON-consuming environments. |
My plan is to have the tree hierarchy end at the function name. So parameters such as thread count (which is dynamic) wouldn't be considered as part of the hierarchy and instead be information in an attribute that could be differentiated across other runs of the same benchmark. |
Wonderful! Thanks for working on this @nvzqz |
@OliverKillane what did you require from the nested hierarchy that caused you to lean towards that direction? |
@nvzqz Was just subjective personal preference for making the file output a very simple copy of the terminal output. My very simple use case was Json OutputPros
Cons
I have no quantitative basis for one design choice over the other, so long as it is simple/easy output, this works. For the internal
|
Goals
Done with
StatTree
that can be passed to file outputs after all benches are completeTreePainter
Wrapped the tree painter with the
StatsCollector
, calls to make parents inStatsCollector
propagate to bothTreePainter
and the tree constructionTreePainter
,Stats
representations to simplify reviewCloses #10
Similar to PR #29 However:
alloc_tallies
informationPotential Improvements
Stats
is messy / uses a custom enum_map forcounters
andalloc_tallies
, can be simplified (decided not to this PR to reduce change size (I'm lazy) )TreePainter
code given we can now merge together some of the methods for starting and finishing leaves, and optionally track depth on the stack, rather than passing around a single depth counter. (once again, minimal change via laziness)Don't think the above are enough to avoid PR approval, however:
Changes
--format
and--file
options provided to specify file. File path must be specified (rather than placing inOUT_DIR
as with WIP: JSON output with--write
flag #29