-
Notifications
You must be signed in to change notification settings - Fork 241
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
In Java interpreter ignore subroutines and perform code split based on the AST size #158
Conversation
@izeigerman All your thoughts seems to be reasonable for me. However, R tests still have unacceptable execution time or even hang forever 🙁 . |
Yeah, apparently now it's being killed due to hitting the memory constraint :( I'm now seriously considering to limit the number of models tested for R. PS: it completes within 30m on my local machine, though it's not very reassuring, since more test models will definitely lead to violation of the time constraint. |
Ended up excluding large XGBoost and LGBM models from the R's test suite. |
OK, so removing subroutine wrapper for each tree actually means that XGBoost and LightGBM are not supported in R anymore - generated code for tiny model cannot be executed in a feasible time (I guess R session crashes, but Travis doesn't report anything for some reason and simply hangs forever). I don't think that it's a good alternative for slowness in Java. |
Hm
I couldn't see any evidence to that. Can you please share more context on this one? Which tiny models? Can this be reproduced locally (I couldn't)?
This will introduce a tight coupling between assemblers and interpreters which is IMHO a poor design decision. Btw, tests are passing now (with large boosting models excluded). |
Additionally it doesn't seem like R was designed for interpreting extensive amounts of code. We're pushing its limits already and I have a feeling it's not hard to come up with a large enough model that will blow up the R interpreter. |
I don't believe that 7.5GB is a harsh memory constraint you mentioned. Instead, I think it hangs due to some reason which is similar to
You excluded LightGBM with the following params
Agree! I just gave an example I imagined within 1m which will allow to save the support of LightGBM and XGBoost for R. Of course, I believe we will be able to develop quite smarter and more efficient solution. |
@StrikerRUS , somehow our conversation was very thought provoking. I've just got another idea and preliminary tests look very promising. Please ignore this PR for now and I'll tag you once it's ready to be revisited. Thanks! |
I think it's applicable to practically every programming language, especially interpreted one. 😃
Yes, but wrapping each tree into a function allows to support bigger models. Why should we consciously limit the number of supported models while we know how to overcome it?
Sorry, didn't get it. Which model did you mean? I strongly believe that we should continue wrapping trees into subroutines for R and don't do it for Java somehow. It looks like the best solution we could offer for users. |
Ah, I'm answering to an outdated comment again! 😄 Seems that something is wrong with my browser it doesn't provide new comments without pressing F5.
Wow, it sounds very awesome! Will be happy to see it. Thank you very much for the constructive conversation! |
5bda331
to
bc42710
Compare
Hey @StrikerRUS, this PR is ready to be revisited.
Thanks! |
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.
@izeigerman Thank you very much for rethinking approach! I like it much more than the previous one. Here are some my initial comments before a detailed review.
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.
Great stuff! Here are my comments. Also, I believe code examples should be re-generated due to changes in Java interpreter and ensemble code.
@StrikerRUS Sorry about the delay, this should be good to go. Thanks! |
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.
LGTM! Thanks a lot! Left two minor comments up to you.
(PowExpr, lambda e: [e.base_expr, e.exp_expr]), | ||
(VectorVal, lambda e: e.exprs), | ||
(IfExpr, lambda e: [e.test, e.body, e.orelse]), |
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 think these expr can be wrapped into a tuples too for the consistent interface. Like, ((PowExpr), lambda e: [e.base_expr, e.exp_expr]),
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.
Sorry, I'm not 100% sold on this :D
|
||
|
||
def test_count_all_exprs_types(): | ||
expr = ast.BinVectorNumExpr( |
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.
Maybe even more complicated expr with deeper nesting? 🙂
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.
TBH, I don't see a value in having even deeper nesting since it won't really provide additional coverage. Additionally this will make the test more complex and somewhat obscure its purpose. Thanks for the feedback though.
Thanks for your review 👍 |
After investigating possible solutions for #152, I came to a conclusion that with the existing design it's extremely hard to come up with the optimal algorithm to split code into subroutines on the interpreter side (and not in assemblers).
The primary reason for that is that since we always interpret one expression at a time it's hard to predict both the depth of the current subtree and the number of expressions that are left to interpret in other branches.
I've achieved some progress by splitting expressions into separate subroutines based on the size of the code generated so far (i.e. code size threshold), but more often than not I'll get some stupid subroutines like this one:
That's why I took a simpler approach and attempted to optimize an interpreter that caused trouble in the first place - the R one.
I slightly modified its behavior: when the binary expressions count threshold is exceeded, it no longer split them into separate variable assignments, but moves them into their own subroutines. Although it might not be the most optimal way for simpler models (like linear ones), it helps tremendously with gradient boosting and random forest models.
Since those models are summation of independent estimators, we end up putting every N (5 by default) estimators into their own subroutine, improving this way the execution time.
@StrikerRUS please let me know what you think.