-
Notifications
You must be signed in to change notification settings - Fork 32
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
Single threaded split #85
base: master
Are you sure you want to change the base?
Single threaded split #85
Conversation
Codecov Report
@@ Coverage Diff @@
## master #85 +/- ##
==========================================
- Coverage 96.96% 96.74% -0.22%
==========================================
Files 10 10
Lines 1053 1076 +23
==========================================
+ Hits 1021 1041 +20
- Misses 32 35 +3
Continue to review full report at Codecov.
|
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.
Looks good in general! Thanks!
Made a few comments.
Waiting for @ogrisel input but maybe it would be interesting to have a new benchmark script comparing the memory usage with parallel_splitting=True
and parallel_splitting=False
.
pygbm/splitting.py
Outdated
def split_indices_single_thread(context, split_info, sample_indices): | ||
"""Split samples into left and right arrays. | ||
|
||
This implementation requires less memory than the parallel version. |
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 add a comment about using Hoare's partition scheme, just for reference
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.
Ok, I didn't know it had a name, I'm ok with adding it, but did you check that it is effectively the same algo?
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 I'm pretty sure it is https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme
pygbm/splitting.py
Outdated
---------- | ||
context : SplittingContext | ||
The splitting context | ||
split_ingo : SplitInfo |
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.
split_info (my fault ^^)
can you also fix it at the other occurrence please?
pygbm/splitting.py
Outdated
i += 1 | ||
j -= 1 | ||
return (sample_indices[:i], | ||
sample_indices[i:]) |
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.
return sample_indices[:i], sample_indices[i:]
?
pygbm/grower.py
Outdated
@@ -336,6 +339,7 @@ def split_next(self): | |||
node = heappop(self.splittable_nodes) | |||
|
|||
tic = time() | |||
split_indices = split_indices_parallel if self.parallel_splitting else split_indices_single_thread |
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 we should fork in SplittingContext
instead of in the grower. We probably don't need grower.parallel_splitting
.
That is, call SplittingContext.split_indice()
in the grower, and in splitting.py do:
def split_indice(...)
if self.parallel_splitting:
return ...
else:
return ...
tests/test_splitting.py
Outdated
@@ -287,6 +288,12 @@ def test_split_indices(): | |||
assert samples_left.shape[0] == si_root.n_samples_left | |||
assert samples_right.shape[0] == si_root.n_samples_right | |||
|
|||
samples_left_single_thread, samples_right_single_thread = split_indices_single_thread( |
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 instead parametrize this test with parallel_split=True
and parallel_split=False
, so that both methods go through exactly the same tests (though I agree your current test also covers this implicitly)
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.
Some more comments on top of @NicolasHug's review.
benchmarks/bench_higgs_boson.py
Outdated
@@ -85,7 +85,7 @@ def load_data(): | |||
max_leaf_nodes=n_leaf_nodes, | |||
n_iter_no_change=None, | |||
random_state=0, | |||
verbose=1) | |||
verbose=1, parallel_splitting=False) |
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 you can expose this a commandline flag to make it easy to profile with mprof run benchmarks/bench_higgs_boson.py --disable-parallel-splitting
(using memory_profiler
).
d8585bb
to
e84cdc3
Compare
@@ -304,6 +312,31 @@ def split_indices(context, split_info, sample_indices): | |||
return (sample_indices[:right_child_position], | |||
sample_indices[right_child_position:]) | |||
|
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.
PEP8: you need 2 blank lines to separate top level functions.
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.
Few more comments,
Also please see the pep8 issues on travis, or locally run
flake8 pygbm tests examples benchmarks
@@ -304,6 +312,31 @@ def split_indices(context, split_info, sample_indices): | |||
return (sample_indices[:right_child_position], | |||
sample_indices[right_child_position:]) | |||
|
|||
@njit(parallel=False) |
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.
You can just use @njit
, parallelism is off by default
@njit(parallel=False) | |
@njit |
sample_indices[i], sample_indices[j] = sample_indices[j], sample_indices[i] | ||
i += 1 | ||
j -= 1 | ||
return (sample_indices[:i], sample_indices[i:]) |
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.
no need for parenthesis
@@ -261,7 +262,7 @@ def test_split_indices(): | |||
n_bins_per_feature, | |||
all_gradients, all_hessians, | |||
l2_regularization, min_hessian_to_split, | |||
min_samples_leaf, min_gain_to_split) | |||
min_samples_leaf, min_gain_to_split, True) |
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.
Please parametrize this test with True/False
instead and remove the checks below
@@ -304,6 +312,31 @@ def split_indices(context, split_info, sample_indices): | |||
return (sample_indices[:right_child_position], | |||
sample_indices[right_child_position:]) | |||
|
|||
@njit(parallel=False) | |||
def _split_indices_single_threaded(context, split_info, sample_indices): |
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.
def _split_indices_single_threaded(context, split_info, sample_indices): | |
def _split_indices_single_threaded(context, split_info, sample_indices): | |
# single-threaded partition into left and right arrays (Hoare's partition scheme) |
(continues from #79, will rebase after merge)
Implement what is discussed in #83.
TODO: