Remove ThreadShare to avoid unnecessary copies and reduce memory usage

Use omp_size_t in parallel for loop
This commit is contained in:
Shikhar Bhardwaj
2017-07-21 18:21:18 +05:30
parent 382e4b7cf7
commit dbda61c30f
5 changed files with 19 additions and 86 deletions
@@ -50,10 +50,10 @@ class ExponentialBackoff
ExponentialBackoff(const size_t firstBackoffEpoch,
const double step,
const double beta) :
firstBackoffEpoch(firstBackoffEpoch),
cutoffEpoch(firstBackoffEpoch),
step(step),
beta(beta)
firstBackoffEpoch(firstBackoffEpoch),
cutoffEpoch(firstBackoffEpoch),
step(step),
beta(beta)
{ /* Nothing to do. */ }
/**
@@ -116,18 +116,6 @@ class ParallelSGD
DecayPolicyType& DecayPolicy() { return decayPolicy; }
private:
/**
* Get the share of datapoint indices to be updated by the thread with given
* thread id.
*
* @param thread_id The id of the current thread. Range 0-OMP_NUM_THREADS.
* @param visitationOrder The random list of datapoint indices for the current
* iteration.
* @return Vector of datapoint indices to be visited by the current thread.
*/
arma::Col<size_t> ThreadShare(size_t threadId,
const arma::Col<size_t>& visitationOrder);
//! The maximum number of allowed iterations.
size_t maxIterations;
@@ -86,16 +86,18 @@ double ParallelSGD<DecayPolicyType>::Optimize(
{
// Each processor gets a subset of the instances.
// Each subset is of size threadShareSize.
arma::Col<size_t> instances = ThreadShare(omp_get_thread_num(),
visitationOrder);
for (size_t j = 0; j < instances.n_elem; ++j)
size_t threadId = omp_get_thread_num();
for (size_t j = threadId * threadShareSize;
j < (threadId + 1) * threadShareSize && j < visitationOrder.n_elem;
++j)
{
// Each instance affects only some components of the decision variable.
// So the gradient is sparse.
arma::sp_mat gradient;
// Evaluate the sparse gradient.
function.Gradient(iterate, instances[j], gradient);
function.Gradient(iterate, visitationOrder[j], gradient);
// Update the decision variable with non-zero components of the
// gradient.
@@ -117,29 +119,6 @@ double ParallelSGD<DecayPolicyType>::Optimize(
return overallObjective;
}
template <typename DecayPolicyType>
arma::Col<size_t> ParallelSGD<DecayPolicyType>::ThreadShare(
size_t threadId, const arma::Col<size_t>& visitationOrder)
{
if (threadId * threadShareSize >= visitationOrder.n_elem)
{
// No data for this thread.
return arma::Col<size_t>();
}
else if ((threadId + 1) * threadShareSize >= visitationOrder.n_elem)
{
// The last few elements.
return visitationOrder.subvec(threadId * threadShareSize ,
visitationOrder.n_elem - 1);
}
else
{
// Equal distribution of threadShareSize examples to each thread.
return visitationOrder.subvec(threadId * threadShareSize,
(threadId + 1) * threadShareSize - 1);
}
}
} // namespace optimization
} // namespace mlpack
@@ -244,7 +244,7 @@ inline double ParallelSGD<ExponentialBackoff>::Optimize(
overallObjective = 0;
#pragma omp parallel for reduction(+:overallObjective)
for (size_t j = 0; j < function.NumFunctions(); ++j)
for (omp_size_t j = 0; j < (omp_size_t) function.NumFunctions(); ++j)
{
overallObjective += function.Evaluate(iterate, j);
}
@@ -278,18 +278,20 @@ inline double ParallelSGD<ExponentialBackoff>::Optimize(
{
// Each processor gets a subset of the instances.
// Each subset is of size threadShareSize.
arma::Col<size_t> instances = ThreadShare(omp_get_thread_num(),
visitationOrder);
for (size_t j = 0; j < instances.n_elem; ++j)
size_t threadId = omp_get_thread_num();
for (size_t j = threadId * threadShareSize;
j < (threadId + 1) * threadShareSize && j < visitationOrder.n_elem;
++j)
{
const size_t numUsers = function.NumUsers();
// Indices for accessing the the correct parameter columns.
const size_t user = data(0, instances[j]);
const size_t item = data(1, instances[j]) + numUsers;
const size_t user = data(0, visitationOrder[j]);
const size_t item = data(1, visitationOrder[j]) + numUsers;
// Prediction error for the example.
const double rating = data(2, instances[j]);
const double rating = data(2, visitationOrder[j]);
double ratingError = rating - arma::dot(iterate.col(user),
iterate.col(item));
-36
View File
@@ -97,42 +97,6 @@ BOOST_AUTO_TEST_CASE(GeneralizedRosenbrockTest)
}
}
/**
* Test if the data points are divided correctly among the threads.
*/
BOOST_AUTO_TEST_CASE(ThreadSharingTest)
{
ConstantStep decayPolicy(0);
// Each thread gets a batch of size 4.
ParallelSGD<ConstantStep> s(0, 4, 1e-10, decayPolicy);
// Generate a random visitation order.
arma::Col<size_t> visitationOrder = arma::linspace<arma::Col<size_t>>(0,
9, 10);
// Lets count how many times each example is handed out in an iteration.
arma::Col<size_t> count(10, arma::fill::zeros);
for (size_t threadId = 0; threadId < 4; ++threadId)
{
arma::Col<size_t> share = s.ThreadShare(threadId, visitationOrder);
for (size_t i = 0; i < share.n_elem; ++i)
count(share(i))++;
// The last thread to have some data.
if (threadId == 2)
BOOST_REQUIRE_EQUAL(share.n_elem, 2);
// Only the first 3 threads get data.
if (threadId > 2)
BOOST_REQUIRE_EQUAL(share.n_elem, 0);
}
// If everything is correct, each count should be 1 for each data point.
CheckMatrices(count, arma::Col<size_t>(10, arma::fill::ones));
}
/**
* Test the correctness of the Exponential backoff stepsize decay policy.
*/