-
Notifications
You must be signed in to change notification settings - Fork 993
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
detect and redirect ifelse usage in j to fifelse #3800
Conversation
Why not compute both |
well, we do know a lot of cases when they won't be Is there a way to compare the objects and only raise |
OK, so if we want to have it in 1.12.4 then best to use this with option by default disabled. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #3800 +/- ##
=======================================
Coverage 99.41% 99.41%
=======================================
Files 71 71
Lines 13247 13252 +5
=======================================
+ Hits 13170 13175 +5
Misses 77 77 ☔ View full report in Codecov by Sentry. |
I'm not so sure... escape valve to |
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.
Yes for 1.12.4. Let's make progress. After all, ifelse
is quite a simple function really.
Warnings could be messages under verbose mode. Because warnings can halt production running under options(warn=2)
.
Could be a level 2 optimization. Then users can turn it off by reducing optimization level. And if so, this should be mentioned in news item.
The words "strictly superior" could be softened/removed in news item.
Comparing to base::ifelse
at runtime would be safest. But that could slow things down. On balance, because ifelse()
is so simple and low risk in vast majority of cases, I don't think we need to do that. But let's postpone that until we have revdep results.
If ifelse()
is opimized to fifelse()
and that's by group, does it introduce a big slowdown due to openmp startup overhead? Like we've seen with uniqueN via forder by group. Need to test, and if so, turn off multithreading below a threshold of length(test)
?
If you don't have time, I can make the changes. Looking forward to getting this in!
I'll essentially be MIA until mid-late next week. Good point re: potential issues w/ large cardinality grouping x openMP start-up, we should benchmark. |
OK found some time this morning. The reason to use Will make that change & then investigate the grouping bit. |
Have run this benchmark:
I really don't know what's going on with base plotting (the labels appear to be all-wrong) but Note the log scale so we can compare absolute differences for each bar (i.e. the absolute differences capture the speed ratio for dt-vs-base). While there may or may not be a per-group cost for Output of |
@mattdowle OK I think this is good to go |
Log scale is very non-intuitive to compare those timings. Anyway it doesn't look safe to merge. library(data.table) # default 20 threads
set.seed(108)
sample_dups = function(N, ratio) {
unq = sample(N, max(N*(1-ratio), 1L))
sample(c(unq, sample(unq, N*ratio)))
}
NN = 1e7
DT = data.table(test=sample(c(TRUE,FALSE), NN, TRUE), yes=rnorm(NN), no=rnorm(NN), grp=sample_dups(NN, 0.1))
system.time(DT[, base::ifelse(test, yes, no), by=grp])
# user system elapsed
# 66.619 0.687 63.239
system.time(DT[, data.table::fifelse(test, yes, no), by=grp])
# user system elapsed
#3054.675 8.496 154.353 @MichaelChirico are you able to reproduce those timings? |
I don't see those timings:
and repeated:
It is slower, though I'm not quite sure what's wrong w my benchmark 🤔 |
Have postponed to next release. Looks good but a bit too risky for this stage of cycle. At least |
Sounds good, as was expected I think. The reason it kept the 1.12.4 Milestone was with the aim to run revdep with this change to get an idea of the potential impact. But that can also easily be done after release. |
Would it be possible to automatically re-route certain parallel loops to serial ones for a certain length condition? A macro is the first thing that comes to mind, something like (?) Lines 75 to 78 in d99b8ae
becomes
where
I don't think that's proper syntax but is something like that possible? Maybe something from here? https://stackoverflow.com/q/12989298/3576984 |
AFAIU looping in macro will be portable only using Lines 147 to 148 in d99b8ae
this would be much cleaner way too. Actually if the above ( |
Yes that looks great. Then we can focus on how to set the threshold -- do we want to be more sophisticated than to pick a fixed threshold? Wouldn't be too hard to write e.g. a Bayesian Optimization script to pick the threshold for a given loop targeting performance improvements at various input sizes (i.e. the insanely sophisticated approach) |
It sounds like best would be to have a new function int useDTthreads(int DTthreads, int64_t Niter, double Pratio) that could be used in a way double p = 0.55; ## some fixed ratio for an algorithm
#pragma omp parallel for num_threads(useDTthreads(getDTthreads(), nx, p))
for (R_len_t i=0; i<nx; i++) { where yet to confirm if or probably cleaner to keep that logic in |
As mentioned by @ColeMiller1 probably we won't want to do this unless we'll fix #4549 |
I'm not so sure about this change. I would wait for a user request. |
Closes #2677
Simply copying the approach used for
strptime
to accomplish this.I think the
ifelse
-->if else
redirection is more generally useful and considered filing a patch tobase
but it won't be a simple drop-in becauseifelse
does the "wrong" thing with attributes (one of the reasons we likefifelse
in the first place) so it would need to be done more carefully to maintain backwards compatibility.I don't think we have to worry about that for usage in
j
.