[SPARK-3022] [SPARK-3041] [mllib] Call findBins once per level + unordered feature bug fix


DecisionTree improvements:
(1) TreePoint representation to avoid binning multiple times
(2) Bug fix: isSampleValid indexed bins incorrectly for unordered categorical features
(3) Timing for DecisionTree internals

Details:

(1) TreePoint representation to avoid binning multiple times

[https://issues.apache.org/jira/browse/SPARK-3022]

Added private[tree] TreePoint class for representing binned feature values.

The input RDD of LabeledPoint is converted to the TreePoint representation initially and then cached.  This avoids the previous problem of re-computing bins multiple times.

(2) Bug fix: isSampleValid indexed bins incorrectly for unordered categorical features

[https://issues.apache.org/jira/browse/SPARK-3041]

isSampleValid used to treat unordered categorical features incorrectly: It treated the bins as if indexed by featured values, rather than by subsets of values/categories.
* exhibited for unordered features (multi-class classification with categorical features of low arity)
* Fix: Index bins correctly for unordered categorical features.

(3) Timing for DecisionTree internals

Added tree/impl/TimeTracker.scala class which is private[tree] for now, for timing key parts of DT code.
Prints timing info via logDebug.

CC: @mengxr @manishamde @chouqin  Very similar update, with one bug fix.  Many apologies for the conflicting update, but I hope that a few more optimizations I have on the way (which depend on this update) will prove valuable to you: SPARK-3042 and SPARK-3043
I agree with @chouqin that TreePoint simply stores feature values (exactly the same data as LabeledPoint) for ordered categorical features.  We could save some space by not making a copy of those (up to 2x the storage).  The main issue is that indexing would be a little trickier since we do not separate out these various features.

@chouqin  About the arr structure, I am doing that in my next updates, where I eliminate that entire part of the aggregation step.  I.e., I eliminate the call to findBinsForLevel() and keep the binMappedRDD.aggregate().