Skip to content
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 export manager and CSV export capability #41

Merged
merged 5 commits into from Mar 19, 2018

Conversation

stevepentland
Copy link
Contributor

The work done here is toward #38 and includes an exporter for CSV and a manager to handle multiple exporters.

It is done to allow multiple exporters (csv, json, etc.) to be used at one time with the only requirement being to add a new enum value to export types and the exporter itself that is responsible for writing. JSON should be a very easy output type to add at this point, and I will move to that next once this PR is closed.

This was referenced Mar 18, 2018
@sharkdp sharkdp self-requested a review March 18, 2018 18:13
Copy link
Owner

@sharkdp sharkdp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much!! This looks great.

I have added a few review comments. Curious to hear what you think.

@@ -15,6 +15,9 @@ indicatif = "0.9"
statistical = "0.1"
atty = "0.2.2"
cfg-if = "0.1.2"
csv = "1.0.0-beta.5"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to rely on a beta version here? Would the latest stable version work, too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check, our use case is pretty simple so I'm hopeful older versions would work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that serde support is not available in pre 1.0.0 versions. I tried 0.15.0 but it is requiring some rather significant changes to serialization, writing, and so on. Not to mention that I can no longer find all the documentation for that version (Encodable trait results in 404). It may be reasonable to use the beta for now, which will also allow keeping serde support betwen json and csv

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, thanks for checking!

@@ -136,6 +137,7 @@ pub fn run_benchmark(
cmd: &str,
shell_spawning_time: TimingResult,
options: &HyperfineOptions,
exporter: &mut Option<Box<ExportManager>>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if we could change the API a little bit. Instead of run_benchmark taking a mutable ExportManager and calling add_result on it, couldn't the function simply return the results (The ExportEntry)?

}

impl CsvExporter {
pub fn new(file_name: String) -> Self {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could the file-handling part be generalized and moved to export/mod.rs? Otherwsise, every exporter type would have to handle this on its own. Instead, each exporter could probably just return a String with the file contents.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could, I wanted to let particular serializer libraries handle output to files in their own way. I can look into refactoring this later to unify file handling however

/// The command that was run
command: String,
/// The mean run time
mean: f64,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use Second instead of f64 here? Does this work with Serialize?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check


/// The ResultExportType enum is used to denote the desired form
/// of exporter to use for a given file.
pub enum ResultExportType {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this enum? ResultExporter is a trait, so we should be able to pass the trait objects directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to take away all reasoning about what would be added as an exporter and leave it up to the manager to create the appropriate type. The internal operation of exporting isn't really important to external calls, only that they want a json, csv, or other file format. It makes a single entry point for all exports instead of making a new exporter and passing it in

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, sounds reasonable.

}

/// The ExportManager handles coordination of multiple ResultExporters
pub trait ExportManager {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need ExportManager to be a trait? Couldn't this just be a struct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I was just trying to extract away all implementation, but I can switch that one out

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer a simple struct. I don't think we need ExportManager as an abstraction/interface here. There is just one of them. I think it would also simplify the code and the readability.

src/main.rs Outdated
Arg::with_name("export-csv")
.long("export-csv")
.takes_value(true)
.multiple(true)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to allow for multiple options here? Wouldn't all these files have the same contents?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They would yes, however as there is no down side to writing to multiple files, I figured offering the option to write to more than one is pretty easy. I don't generally want to try and guess user's desires since we can easily write 1 or more files, in multiple formats.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. However, I don't think that this will be an actual use case. Even if it would, the user could simply copy the file afterwards. Also, I think there is a downside to defining it multiple = true: the generated help text will look like --export-csv <file>... (or similar) which might confuse some users ("do I have to specify multiple files? one for each benchmark command?").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll remove multiples from this one, and from the json branch (rebasing back on these changes anyway)

src/main.rs Outdated
fn run(
commands: &Vec<&str>,
options: &HyperfineOptions,
exporter: &mut Option<Box<ExportManager>>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: I would rather like run to return the results instead of passing a mutable ExportManager.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that'll simplify everything

Manager is now a simple struct, that no longer stores the results,
but instead takes in a Vec<ExportEntry> and has all exporters
write from this collection.

Benchmark and run both return their results, no more mut manager.

Change f64 in ExportEntry to hyperfine::internal::Second.

Remove multiple export files for a single type.
@stevepentland
Copy link
Contributor Author

stevepentland commented Mar 18, 2018

The only things left out from review:

  • file handling: still a per-exporter basis, as they simply create the file according to how the external lib prefers to write output. This can be changed easily once an additional exporter (json) is present
    (Done in JSON branch)
  • csv lib still beta: Seems the best option at this point, once burntsushi puts out a final 1.0.0 release we can switch to that.

@sharkdp sharkdp merged commit ce79d81 into sharkdp:master Mar 19, 2018
@sharkdp
Copy link
Owner

sharkdp commented Mar 19, 2018

Fantastic - thank you!

This was referenced Mar 19, 2018
@stevepentland stevepentland deleted the results-export branch March 19, 2018 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants