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

Simplify return codes; error messages written to stderr, excepting model instantiation failures #994

Merged
merged 4 commits into from
Mar 15, 2021

Conversation

mitzimorris
Copy link
Member

Submisison Checklist

  • Run tests: ./runCmdStanTests.py src/test
  • Declare copyright holder and open-source license: see below

Summary:

Ensures that all error messages are written to stderr, including bad input data.
PR #992 overlooked the output stream passed in to the new_model function; changed it from standard output to standard error, as the only messages written to this output stream are error messages.

Intended Effect:

  • Consistent handling of error messages - all error messages should go to standard error.

  • Simplify return codes - the set of POSIX return codes in stan::services::error_codes aren't actionable for CmdStan users and the interfaces which wrap them.

How to Verify:

Unit tests.

Side Effects:

As for #992, document that errors are not going to standard error, not standard out.

Documentation:

CmdStan User's guide now has section on return codes and common errors.

Copyright and Licensing

Please list the copyright holder for the work you are submitting (this will be you or your assignee, such as a university or company): Columbia University

By submitting this pull request, the copyright holder is agreeing to license the submitted work under the following licenses:

@mitzimorris mitzimorris requested a review from bbbales2 March 14, 2021 03:52
@mitzimorris
Copy link
Member Author

@syclik - any thoughts w/r/t return codes?

@stan-buildbot
Copy link
Contributor


Name Old Result New Result Ratio Performance change( 1 - new / old )
gp_pois_regr/gp_pois_regr.stan 3.42 3.44 0.99 -0.52% slower
low_dim_corr_gauss/low_dim_corr_gauss.stan 0.02 0.02 0.97 -3.58% slower
eight_schools/eight_schools.stan 0.11 0.11 1.04 3.5% faster
gp_regr/gp_regr.stan 0.16 0.16 0.97 -3.49% slower
irt_2pl/irt_2pl.stan 5.25 5.37 0.98 -2.26% slower
performance.compilation 91.41 88.78 1.03 2.88% faster
low_dim_gauss_mix_collapse/low_dim_gauss_mix_collapse.stan 9.03 8.9 1.01 1.37% faster
pkpd/one_comp_mm_elim_abs.stan 31.61 29.78 1.06 5.8% faster
sir/sir.stan 127.37 130.91 0.97 -2.78% slower
gp_regr/gen_gp_data.stan 0.04 0.04 0.95 -5.36% slower
low_dim_gauss_mix/low_dim_gauss_mix.stan 3.1 3.1 1.0 0.06% faster
pkpd/sim_one_comp_mm_elim_abs.stan 0.38 0.37 1.01 1.06% faster
arK/arK.stan 1.89 1.91 0.99 -0.63% slower
arma/arma.stan 0.69 0.94 0.74 -35.92% slower
garch/garch.stan 0.51 0.56 0.91 -10.07% slower
Mean result: 0.974489490743

Jenkins Console Log
Blue Ocean
Commit hash: 7491669


Machine information ProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010

CPU:
Intel(R) Xeon(R) CPU E5-1680 v2 @ 3.00GHz

G++:
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

Clang:
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

Copy link
Member

@bbbales2 bbbales2 left a comment

Choose a reason for hiding this comment

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

Simplifying the error codes seems fine, though it sorta makes me wonder if we shouldn't go remove the return codes in services if they aren't being used.

One question about the output stream.

@@ -219,7 +223,7 @@ int command(int argc, const char *argv[]) {

// Instantiate model
stan::model::model_base &model
= new_model(*var_context, random_seed, &std::cout);
= new_model(*var_context, random_seed, &std::cerr);
Copy link
Member

Choose a reason for hiding this comment

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

Will this make print statements in the Stan model go to stderr?

Copy link
Member Author

Choose a reason for hiding this comment

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

excellent question. will check.

Copy link
Member Author

Choose a reason for hiding this comment

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

answer is no. the generated model.hpp file contains the function new_model; this instantiates the model object.

the instantiated model object is passed into the services methods along with stderr and stdout, and therefore the correct output streams are hooked up to the calls to log_prob et al.

can't connect the c++ dots to understand why calls to rethrow_located in the model constructor go to stderr, but they do.

Copy link
Member Author

Choose a reason for hiding this comment

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

print statements in transformed data block will go to std err, because those are rolled into the model constructor. everything else goes to stdout.

Copy link
Member Author

@mitzimorris mitzimorris Mar 14, 2021

Choose a reason for hiding this comment

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

functions that print stuff will print to stderr when called in transformed data block, otherwise, will print to stdout.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds like this is a cleanup for stanc3. Since it's only transformed data prints, then I think revert this change and just do the return code cleanup here.

Copy link
Member Author

@mitzimorris mitzimorris Mar 15, 2021

Choose a reason for hiding this comment

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

the model instantiation failures caught by validate_dims are printed to whatever output stream is passed in to new_model. I spent an hour yesterday trying to figure out why - I don't see an output stream passed around, and yet, that's where these error messages end up.

the result it that it's necessary to parse stdout to figure out if the data supplied to model is OK. cf https://github.com/stan-dev/cmdstanpy/blob/2a7a9a5cc3a8e37670092c0af11d1e4c6d252381/cmdstanpy/stanfit.py#L230-L244. - this is a quick and dirty parse - it's brittle, might catch too much/too little, etc.

just to be clear, if the user has a print statement in transformed data block, it will go to stderr; otherwise, stdout.
but yes, agree, this is terrible either way.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm I guess we can go ahead and fix this streaming thing then.

I was looking at the generated cpp for a model:

stan::model::model_base& new_model(
        stan::io::var_context& data_context,
        unsigned int seed,
	std::ostream* msg_stream) {
  stan_model* m = new stan_model(data_context, seed, msg_stream);
  return *m;
}

Then I went to look at the stan_model constructor in my generated code and the pstream__ argument is unused there. Where does this argument come in to play anyway?

Copy link
Member

Choose a reason for hiding this comment

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

Or I guess the question is how does this argument end up changing where stuff goes anyway? Is this a problem with how some service function is not using loggers where it should be or something weird?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd love to figure out what's going on here and then address it in a future PR.
for now, can let thing go to stdout for now and just simplify return codes with this PR.

@mitzimorris
Copy link
Member Author

we shouldn't go remove the return codes in services if they aren't being used.

in theory, sounds like a good idea - cf this nice writeup of why it's better to throw exceptions rather than use return codes: http://javierferrer.me/exceptions-vs-error-codes/

but we can't do this (yet) - my understanding is that RStan and PyStan call the services methods directly and they couldn't handle C++ calls throwing exceptions, so we went this way. maybe @syclik can explain more.

Copy link
Member

@bbbales2 bbbales2 left a comment

Choose a reason for hiding this comment

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

Good!

@stan-buildbot
Copy link
Contributor


Name Old Result New Result Ratio Performance change( 1 - new / old )
gp_pois_regr/gp_pois_regr.stan 3.41 3.41 1.0 0.17% faster
low_dim_corr_gauss/low_dim_corr_gauss.stan 0.02 0.02 1.01 0.79% faster
eight_schools/eight_schools.stan 0.11 0.11 1.02 2.19% faster
gp_regr/gp_regr.stan 0.16 0.16 0.99 -0.57% slower
irt_2pl/irt_2pl.stan 5.32 5.28 1.01 0.75% faster
performance.compilation 91.0 88.62 1.03 2.62% faster
low_dim_gauss_mix_collapse/low_dim_gauss_mix_collapse.stan 9.05 8.87 1.02 1.98% faster
pkpd/one_comp_mm_elim_abs.stan 31.69 30.18 1.05 4.77% faster
sir/sir.stan 131.0 128.44 1.02 1.96% faster
gp_regr/gen_gp_data.stan 0.04 0.04 0.98 -1.99% slower
low_dim_gauss_mix/low_dim_gauss_mix.stan 3.06 3.06 1.0 0.16% faster
pkpd/sim_one_comp_mm_elim_abs.stan 0.37 0.38 0.98 -1.97% slower
arK/arK.stan 1.94 1.91 1.01 1.43% faster
arma/arma.stan 0.68 0.63 1.08 7.26% faster
garch/garch.stan 0.57 0.51 1.12 10.76% faster
Mean result: 1.0218088575

Jenkins Console Log
Blue Ocean
Commit hash: 503211b


Machine information ProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010

CPU:
Intel(R) Xeon(R) CPU E5-1680 v2 @ 3.00GHz

G++:
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

Clang:
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

@mitzimorris mitzimorris merged commit a67347b into develop Mar 15, 2021
@mitzimorris mitzimorris changed the title All error messages written to stderr, including model instantiation failures; simplify return codes Simplify return codes; error messages written to stderr, excepting model instantiation failures Mar 16, 2021
@mitzimorris
Copy link
Member Author

changed title to match outcome of discussion.

@syclik
Copy link
Member

syclik commented Mar 18, 2021

but we can't do this (yet) - my understanding is that RStan and PyStan call the services methods directly and they couldn't handle C++ calls throwing exceptions, so we went this way. maybe @syclik can explain more.

I believe RStan and PyStan can handle the exceptions!

I agree that it makes more sense if they throw appropriate exceptions and let each interface handle error handling. In C++, if you don't handle an exception, it'll eventually crash the process. I'm not sure if it's well defined what the outcome is... it's better if we turned that exception into something reasonable (and returned a non-zero exit code).

@mitzimorris
Copy link
Member Author

In C++, if you don't handle an exception, it'll eventually crash the process. I'm not sure if it's well defined what the outcome is... it's better if we turned that exception into something reasonable (and returned a non-zero exit code).

when an uncaught exception causes the process to crash, depending on what the error is, the system returns a termination signal which is platform dependent, c.f this table: https://en.wikipedia.org/wiki/Signal_(IPC)#Default_action

understood that the calling process needs to handle the exception - this is always the case.

writeup of why it's better to throw exceptions rather than use return codes: http://javierferrer.me/exceptions-vs-error-codes/

this writeup corresponds to my experience with CmdStan - the slim set of Posix return codes weren't useful in providing feedback to the user.

@syclik
Copy link
Member

syclik commented Mar 18, 2021

when an uncaught exception causes the process to crash, depending on what the error is, the system returns a termination signal which is platform dependent, c.f this table: https://en.wikipedia.org/wiki/Signal_(IPC)#Default_action

understood that the calling process needs to handle the exception - this is always the case.

Thanks! Sorry about the language there... didn't mean to imply that you didn't understand that.

I was actually wondering what would happen if CmdStan didn't catch the exception and whether it'd be well-defined. Just from the perspective of what happens if we let it propagate (simplifies code)... I learned something new from your response... I didn't realize an uncaught exception results in a SIGTERM! I hadn't found that info in my searching around for what the behavior is.

@mitzimorris
Copy link
Member Author

mitzimorris commented Mar 19, 2021

I'm definitely in favor of letting service methods throw errors instead of return codes.

@WardBrian WardBrian deleted the feature/991-improve-errs branch September 6, 2023 20:17
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.

5 participants