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

Added Benchmarking; Added Sampler to Prelude for Benchmarking #1071

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

Riley-Kilgore
Copy link
Member

No description provided.

@Riley-Kilgore
Copy link
Member Author

After having these two types unified as Generator, I think it actually may make sense to have two separate prelude types Fuzzer and Sampler. The changes involved are minimal either way, but it seems to make the Fuzzer API somewhat uncomfortable if we are leveraging Generator due to the extra (and unused) parameter.

A lot of users won't be exposed to this, as they will simply use the pre-defined Fuzzers in the Fuzz library, but for users crafting their own Fuzzers, do we want to introduce breaking changes?

@@ -182,6 +183,7 @@ impl fmt::Display for Token {
Token::Once => "once",
Token::Validator => "validator",
Token::Via => "via",
Token::Benchmark => "benchmark",
Copy link
Member

Choose a reason for hiding this comment

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

Let's call it bench; not so much because it's shorter but because it's quite often used in benchmarking terminology (bench refer to the individual tests of a benchmark, which aggregates all benches).

pub name: String,
pub on_test_failure: OnTestFailure,
pub program: Program<Name>,
pub fuzzer: Fuzzer<Name>,
Copy link
Member

Choose a reason for hiding this comment

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

That last one is suspicious? It ought not to be a Fuzzer here I believe.

Comment on lines 533 to 544
pub enum Prng {
Seeded { choices: Vec<u8>, uplc: PlutusData },
Replayed { choices: Vec<u8>, uplc: PlutusData },
Seeded {
choices: Vec<u8>,
uplc: PlutusData,
iteration: usize,
},
Replayed {
choices: Vec<u8>,
uplc: PlutusData,
iteration: usize,
},
}
Copy link
Member

Choose a reason for hiding this comment

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

Why is changing the PRNG necessary? I don't think that it is right? The size parameter should be holistic for an entire PRNG and influence all random choices down the line.

@KtorZ
Copy link
Member

KtorZ commented Dec 17, 2024

A lot of users won't be exposed to this, as they will simply use the pre-defined Fuzzers in the Fuzz library, but for users crafting their own Fuzzers, do we want to introduce breaking changes?

That'll certainly require quite a lot of change in the Fuzz library, which I think we can avoid by making the context be provided as a closure instead of passed as an extra parameter. So, fundamentally, have:

Sampler<a> = fn(Int) -> Fuzzer<a>

instead of

Fuzzer<a> = fn(Void, PRNG) -> Option<(PRNG, a)>
Sampler<a> = fn(Int, PRNG) -> Option<(PRNG, a)>

@Riley-Kilgore Riley-Kilgore changed the title Add Benchmarking command with CSV output; Add ScaledFuzzer to prelude and PBT runner Added Benchmarking; Added Sampler to Prelude for Benchmarking Dec 17, 2024
Copy link
Member

Choose a reason for hiding this comment

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

This could probably be unified with the Fuzzer parser (by passing the expected keyword as a parameter to the parser?)

@@ -633,6 +642,43 @@ impl<'comments> Formatter<'comments> {
.append("}")
}

#[allow(clippy::too_many_arguments)]
fn definition_benchmark<'a>(
Copy link
Member

Choose a reason for hiding this comment

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

I believe this could be unified with the formatting of fuzzer definitions as well.

pub name: String,
pub on_test_failure: OnTestFailure,
pub program: Program<Name>,
pub sampler: Fuzzer<Name>,
Copy link
Member

Choose a reason for hiding this comment

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

Should be Sampler<Name> ?

@@ -379,7 +382,7 @@ impl PropertyTest {
let mut counterexample = Counterexample {
value,
choices: next_prng.choices(),
cache: Cache::new(|choices| {
cache: Cache::new(move |choices| {
Copy link
Member

Choose a reason for hiding this comment

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

Curious? Seems like an artifact from previous (reverted) changes.

pub fn benchmark(
self,
seed: u32,
n: usize,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
n: usize,
max_iterations: usize,

Calling it n kind of suggests that it is what's going to be incremented.

location: arg.arg.location,
expected: inferred_inner_type.clone(),
given: provided_inner_type.clone(),
situation: Some(UnifyErrorSituation::FuzzerAnnotationMismatch),
Copy link
Member

Choose a reason for hiding this comment

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

Probably want to either:

  • Rework the annotation / situation here so that the error message look proper
  • Adjust the message to be more generic and fit both Fuzzer and Sampler

Comment on lines 497 to 505
let (inferred_annotation, inferred_inner_type) = match infer_sampler(
environment,
provided_inner_type.clone(),
&typed_via.tipo(),
&arg.via.location(),
) {
Ok(result) => Ok(result),
Err(err) => Err(err),
}?;
Copy link
Member

Choose a reason for hiding this comment

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

The match seems excessive / useless here?

Comment on lines 361 to 369
let (inferred_annotation, inferred_inner_type) = match infer_fuzzer(
environment,
provided_inner_type.clone(),
&typed_via.tipo(),
&arg.via.location(),
)?;
) {
Ok(result) => Ok(result),
Err(err) => Err(err),
}?;
Copy link
Member

Choose a reason for hiding this comment

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

Unnecessary match?

@@ -402,8 +427,7 @@ where
seed,
property_max_success,
} => {
let tests =
self.collect_tests(verbose, match_tests, exact_match, options.tracing)?;
let tests = self.collect_tests(false, match_tests, exact_match, options.tracing)?;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let tests = self.collect_tests(false, match_tests, exact_match, options.tracing)?;
let tests = self.collect_tests(verbose, match_tests, exact_match, options.tracing)?;

} => {
// todo - collect benchmarks
let tests =
self.collect_benchmarks(false, match_tests, exact_match, options.tracing)?;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
self.collect_benchmarks(false, match_tests, exact_match, options.tracing)?;
self.collect_benchmarks(verbose, match_tests, exact_match, options.tracing)?;

Comment on lines 505 to 522
// Write benchmark results to CSV
use std::fs::File;
use std::io::Write;

let mut writer = File::create(&output).map_err(|error| {
vec![Error::FileIo {
error,
path: output.clone(),
}]
})?;

// Write CSV header
writeln!(writer, "test_name,module,memory,cpu").map_err(|error| {
vec![Error::FileIo {
error,
path: output.clone(),
}]
})?;
Copy link
Member

Choose a reason for hiding this comment

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

Avoid writing to file directly; instead, default to stdout and let user redirect to file if needed. We should stick to the same behavior as for tests: have a pretty input for ANSI-capable terminals, but default to JSON otherwise so that we can pipe the output into scripts and processing pipelines.

Comment on lines 1 to 7
fn simple_sampler(): Sampler<Int> {
fn(n: Int) {
fn(prng: PRNG) {
n
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This looks like it should NOT compile; if it does, then something's wrong with the typechecker (which I'll more thoroughly review eventually).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

2 participants