Hdbscan: Limitation on the number of cores actively used

Created on 15 Jan 2018  路  2Comments  路  Source: scikit-learn-contrib/hdbscan

Hi!

I have run HDBSCAN on a machine with 16 CPUs and 32GB of RAM. This produces good
results, but I noticed that only 4 cores out of 16 are used for the heavy parts
of the calculation. It would be fantastic if this limitation could be removed.

Gradually increasing core_dist_n_jobs from 1 to 4 reduces the computational
time as one would expect. Setting core_dist_n_jobs to be larger than 4,
however, does not generate further improvements. Running top on the terminal
verifies that the additional cores are not employed.

I suspect that the limitation on the number of cores being used arises because
of lines 397-416 in _hdbscan_boruvka.pyx:

# A shortcut: if we have a lot of points then we can split the points
# into four piles and query them in parallel. On multicore systems
# (most systems) this amounts to a 2x-3x wall clock improvement.
if self.tree.data.shape[0] > 16384 and self.n_jobs > 1:
    datasets = [
        np.asarray(self.tree.data[0:self.num_points//4]),
        np.asarray(self.tree.data[self.num_points//4:
                                                  self.num_points//2]),
        np.asarray(self.tree.data[self.num_points//2:
                                                  3*(self.num_points//4)]),
        np.asarray(self.tree.data[3*(self.num_points//4):
                                                  self.num_points])
        ]

    knn_data = Parallel(n_jobs=self.n_jobs)(
        delayed(_core_dist_query,
                      check_pickle=False)
        (self.core_dist_tree, points,
         self.min_samples + 1)
        for points in datasets)

To test the hypothesis that the above snippet causes the problem, I have
written a Python script that resembles the code from HDBSCAN while performing
a trivial operation. Here it is:

import os
import numpy as np
import time

from sklearn.externals.joblib import Parallel, delayed

os.environ["JOBLIB_TEMP_FOLDER"] = "/tmp"

NUM_POINTS = 10**2
UP_TO = 10**8
MAX_N_JOBS = 16


def add_total(array, up_to):
    total = 0
    for i in range(up_to):
        total += 1
    return array + total


def partition_data(data):
    num_points = data.shape[0]
    datasets = [
        np.asarray(data[0:num_points//4]),
        np.asarray(data[num_points//4: num_points//2]),
        np.asarray(data[num_points//2: 3*(num_points//4)]),
        np.asarray(data[3*(num_points//4): num_points])
        ]
    return datasets


def main():
    data = np.ones(NUM_POINTS)
    datasets= partition_data(data)

    timing = []
    for n_jobs in range(1, MAX_N_JOBS+1):

        print("Trying to use {} CPUs.".format(n_jobs))
        start = time.time()
        Parallel(n_jobs=n_jobs)(
            delayed(add_total, check_pickle=False)(points, UP_TO)
            for points in datasets
            )
        end = time.time()
        timing.append(end-start)

    print(timing)

if __name__ == '__main__':
    main()

One finds that joblib manages to parallelise this script on up to 4 CPUs. As
before, adding more cores yields no apparent benefits.

If useful, I could have a go at making a pull request. The idea would be to
separate the data in n_chunks, where this number is passed as a keyword
argument, and need not be equal to 4 (as it effectively is now). As a bonus
feature, one could have that setting n_chunks=None splits the data in as
many piles as there are core_dist_n_jobs. Would this be a good strategy (@lmcinnes )?

Most helpful comment

I don't supppose any progress has been made on this regard? Hitting some long runtimes with 300K points and 26ish dimensions.

All 2 comments

Great observation. I think the n_chunks approach should work well -- there is an issue with diminishing returns, but as long as you can override to avoid that (which you suggested approach does) it will be fine. I would love to see a PR for this if you have the time. Thank you!

I don't supppose any progress has been made on this regard? Hitting some long runtimes with 300K points and 26ish dimensions.

Was this page helpful?
0 / 5 - 0 ratings