Skip to content
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

feat: Change locking strategy of Booster, allow for share and unique locks #2760

Merged
merged 55 commits into from
Jul 19, 2020
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
971720f
Add capability to get possible max and min values for a model
JoanFM Feb 3, 2020
3c49f4c
Change implementation to have return value in tree.cpp, change naming…
JoanFM Feb 4, 2020
48a9474
Update include/LightGBM/c_api.h
JoanFM Feb 5, 2020
0d516c1
Change iteration to avoid potential overflow, add bindings to R and P…
JoanFM Feb 5, 2020
f14ca1f
Merge branch 'master' of https://github.com/microsoft/LightGBM into m…
Feb 5, 2020
8c58493
Adjust test values
Feb 5, 2020
019bda8
Consider const correctness and multithreading protection
JoanFM Feb 5, 2020
a794241
Put everything possible as const
JoanFM Feb 5, 2020
70ca8fc
Include shared_mutex, for now as unique_lock
JoanFM Feb 6, 2020
c43bf1c
Update test values
Feb 6, 2020
87ee70f
Put everything possible as const
JoanFM Feb 5, 2020
fd24844
Include shared_mutex, for now as unique_lock
JoanFM Feb 6, 2020
65cde98
Make PredictSingleRow const and share the lock with other reading thr…
Feb 6, 2020
cfa612b
Update test values
Feb 6, 2020
d3a54ae
Add test to check that model is exactly the same in all platforms
JoanFM Feb 8, 2020
8ae8f3e
Try to parse the model to get the expected values
JoanFM Feb 8, 2020
3713151
Try to parse the model to get the expected values
JoanFM Feb 8, 2020
1cd8a9d
Merge branch 'max_min_leafs' of https://github.com/JoanFM/LightGBM in…
JoanFM Feb 8, 2020
c7185a0
Fix implementation, num_leaves can be lower than the leaf_value_ size
JoanFM Feb 9, 2020
fd0bdb8
Do not check for num_leaves to be smaller than actual size and get ba…
JoanFM Feb 9, 2020
d6ca4ff
Merge branch 'max_min_leafs' of https://github.com/JoanFM/LightGBM in…
JoanFM Feb 12, 2020
a753edf
Merge branch 'master' of https://github.com/microsoft/LightGBM into m…
JoanFM Feb 12, 2020
19234e0
Change test order
JoanFM Feb 12, 2020
00bdde1
Add gpu_use_dp option in test
JoanFM Feb 13, 2020
82c677c
Remove helper test method
JoanFM Feb 13, 2020
46092fd
Merge branch 'read_write_locks' of https://github.com/JoanFM/LightGBM…
JoanFM Feb 13, 2020
397667c
Merge branch 'max_min_leafs' of https://github.com/JoanFM/LightGBM in…
JoanFM Feb 13, 2020
46c4658
Remove TODO
JoanFM Feb 14, 2020
9d1a31a
Add preprocessing option to compile with c++17
JoanFM Feb 18, 2020
c548d3f
Update python-package/setup.py
JoanFM Feb 19, 2020
a65de0b
Merge branch 'master' into read_write_locks
JoanFM Feb 20, 2020
90b2d1c
Remove unwanted changes
Feb 20, 2020
a459e67
Merge branch 'master' into read_write_locks
JoanFM Apr 14, 2020
66a6c8c
Move option
Apr 14, 2020
7f476f7
Merge branch 'master' into read_write_locks
JoanFM Jul 11, 2020
f812c31
Fix problems introduced by conflict fix
JoanFM Jul 11, 2020
e6189ac
Avoid switching to c++17 and use yamc mutex library to access shared …
JoanFM Jul 12, 2020
e400481
Add extra yamc include
JoanFM Jul 12, 2020
891f5c4
Change header order
JoanFM Jul 12, 2020
adbcc2d
some lint fix
guolinke Jul 12, 2020
a406a45
change include order and remove some extra blank lines
JoanFM Jul 12, 2020
231c4cd
Further fix lint issues
JoanFM Jul 12, 2020
22d99f3
Update c_api.cpp
JoanFM Jul 12, 2020
c01814b
Further fix lint issues
JoanFM Jul 12, 2020
4acbe97
Move yamc include files to a new yamc folder
JoanFM Jul 12, 2020
46a9f70
Use standard unique_lock
JoanFM Jul 13, 2020
f6fc735
Update windows/LightGBM.vcxproj
JoanFM Jul 13, 2020
22f45a8
Update windows/LightGBM.vcxproj.filters
JoanFM Jul 13, 2020
d255725
Update windows/LightGBM.vcxproj.filters
JoanFM Jul 13, 2020
e650130
Update windows/LightGBM.vcxproj.filters
JoanFM Jul 13, 2020
22945c6
Update windows/LightGBM.vcxproj.filters
JoanFM Jul 13, 2020
78d2fc6
Merge branch 'master' of https://github.com/microsoft/LightGBM into r…
JoanFM Jul 13, 2020
611e3ea
Merge branch 'master' into read_write_locks
JoanFM Jul 16, 2020
e20f230
Merge branch 'master' of https://github.com/microsoft/LightGBM into r…
JoanFM Jul 16, 2020
61c59fe
Fix problems coming from merge conflict resolution
JoanFM Jul 16, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 212 additions & 0 deletions include/LightGBM/utils/yamc/alternate_shared_mutex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
/*
* alternate_shared_mutex.hpp
*
* MIT License
*
* Copyright (c) 2017 yohhoy
JoanFM marked this conversation as resolved.
Show resolved Hide resolved
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef YAMC_ALTERNATE_SHARED_MUTEX_HPP_
#define YAMC_ALTERNATE_SHARED_MUTEX_HPP_

#include <cassert>
#include <chrono>
#include <condition_variable>
#include <mutex>

#include "yamc_rwlock_sched.hpp"

namespace yamc {

/*
* alternate implementation of shared mutex variants
*
* - yamc::alternate::shared_mutex
* - yamc::alternate::shared_timed_mutex
* - yamc::alternate::basic_shared_mutex<RwLockPolicy>
* - yamc::alternate::basic_shared_timed_mutex<RwLockPolicy>
*/
namespace alternate {

namespace detail {

template <typename RwLockPolicy>
class shared_mutex_base {
protected:
typename RwLockPolicy::state state_;
std::condition_variable cv_;
std::mutex mtx_;

void lock() {
std::unique_lock<decltype(mtx_)> lk(mtx_);
RwLockPolicy::before_wait_wlock(state_);
while (RwLockPolicy::wait_wlock(state_)) {
cv_.wait(lk);
}
RwLockPolicy::after_wait_wlock(state_);
RwLockPolicy::acquire_wlock(&state_);
}

bool try_lock() {
std::lock_guard<decltype(mtx_)> lk(mtx_);
if (RwLockPolicy::wait_wlock(state_)) return false;
RwLockPolicy::acquire_wlock(state_);
return true;
}

void unlock() {
std::lock_guard<decltype(mtx_)> lk(mtx_);
RwLockPolicy::release_wlock(&state_);
cv_.notify_all();
}

void lock_shared() {
std::unique_lock<decltype(mtx_)> lk(mtx_);
while (RwLockPolicy::wait_rlock(state_)) {
cv_.wait(lk);
}
RwLockPolicy::acquire_rlock(&state_);
}

bool try_lock_shared() {
std::lock_guard<decltype(mtx_)> lk(mtx_);
if (RwLockPolicy::wait_rlock(state_)) return false;
RwLockPolicy::acquire_rlock(state_);
return true;
}

void unlock_shared() {
std::lock_guard<decltype(mtx_)> lk(mtx_);
if (RwLockPolicy::release_rlock(&state_)) {
cv_.notify_all();
}
}
};

} // namespace detail

template <typename RwLockPolicy>
class basic_shared_mutex : private detail::shared_mutex_base<RwLockPolicy> {
using base = detail::shared_mutex_base<RwLockPolicy>;

public:
basic_shared_mutex() = default;
~basic_shared_mutex() = default;

basic_shared_mutex(const basic_shared_mutex&) = delete;
basic_shared_mutex& operator=(const basic_shared_mutex&) = delete;

using base::lock;
using base::try_lock;
using base::unlock;

using base::lock_shared;
using base::try_lock_shared;
using base::unlock_shared;
};

using shared_mutex = basic_shared_mutex<YAMC_RWLOCK_SCHED_DEFAULT>;

template <typename RwLockPolicy>
class basic_shared_timed_mutex
: private detail::shared_mutex_base<RwLockPolicy> {
using base = detail::shared_mutex_base<RwLockPolicy>;

using base::cv_;
using base::mtx_;
using base::state_;

template <typename Clock, typename Duration>
bool do_try_lockwait(const std::chrono::time_point<Clock, Duration>& tp) {
std::unique_lock<decltype(mtx_)> lk(mtx_);
RwLockPolicy::before_wait_wlock(state_);
while (RwLockPolicy::wait_wlock(state_)) {
if (cv_.wait_until(lk, tp) == std::cv_status::timeout) {
if (!RwLockPolicy::wait_wlock(state_)) // re-check predicate
break;
RwLockPolicy::after_wait_wlock(state_);
return false;
}
}
RwLockPolicy::after_wait_wlock(state_);
RwLockPolicy::acquire_wlock(state_);
return true;
}

template <typename Clock, typename Duration>
bool do_try_lock_sharedwait(
const std::chrono::time_point<Clock, Duration>& tp) {
std::unique_lock<decltype(mtx_)> lk(mtx_);
while (RwLockPolicy::wait_rlock(state_)) {
if (cv_.wait_until(lk, tp) == std::cv_status::timeout) {
if (!RwLockPolicy::wait_rlock(state_)) // re-check predicate
break;
return false;
}
}
RwLockPolicy::acquire_rlock(state_);
return true;
}

public:
basic_shared_timed_mutex() = default;
~basic_shared_timed_mutex() = default;

basic_shared_timed_mutex(const basic_shared_timed_mutex&) = delete;
basic_shared_timed_mutex& operator=(const basic_shared_timed_mutex&) = delete;

using base::lock;
using base::try_lock;
using base::unlock;

template <typename Rep, typename Period>
bool try_lock_for(const std::chrono::duration<Rep, Period>& duration) {
const auto tp = std::chrono::steady_clock::now() + duration;
return do_try_lockwait(tp);
}

template <typename Clock, typename Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& tp) {
return do_try_lockwait(tp);
}

using base::lock_shared;
using base::try_lock_shared;
using base::unlock_shared;

template <typename Rep, typename Period>
bool try_lock_shared_for(const std::chrono::duration<Rep, Period>& duration) {
const auto tp = std::chrono::steady_clock::now() + duration;
return do_try_lock_sharedwait(tp);
}

template <typename Clock, typename Duration>
bool try_lock_shared_until(
const std::chrono::time_point<Clock, Duration>& tp) {
return do_try_lock_sharedwait(tp);
}
};

using shared_timed_mutex = basic_shared_timed_mutex<YAMC_RWLOCK_SCHED_DEFAULT>;

} // namespace alternate
} // namespace yamc

#endif
149 changes: 149 additions & 0 deletions include/LightGBM/utils/yamc/yamc_rwlock_sched.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* yamc_rwlock_sched.hpp
*
* MIT License
*
* Copyright (c) 2017 yohhoy
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef YAMC_RWLOCK_SCHED_HPP_
#define YAMC_RWLOCK_SCHED_HPP_

#include <cassert>
#include <cstddef>

/// default shared_mutex rwlock policy
#ifndef YAMC_RWLOCK_SCHED_DEFAULT
#define YAMC_RWLOCK_SCHED_DEFAULT yamc::rwlock::ReaderPrefer
#endif

namespace yamc {

/*
* readers-writer locking policy for basic_shared_(timed)_mutex<RwLockPolicy>
*
* - yamc::rwlock::ReaderPrefer
* - yamc::rwlock::WriterPrefer
*/
namespace rwlock {

/// Reader prefer scheduling
///
/// NOTE:
// This policy might introduce "Writer Starvation" if readers continuously
// hold shared lock. PThreads rwlock implementation in Linux use this
// scheduling policy as default. (see also PTHREAD_RWLOCK_PREFER_READER_NP)
//
struct ReaderPrefer {
static const std::size_t writer_mask = ~(~std::size_t(0u) >> 1); // MSB 1bit
static const std::size_t reader_mask = ~std::size_t(0u) >> 1;

struct state {
std::size_t rwcount = 0;
};

static void before_wait_wlock(const state&) {}
static void after_wait_wlock(const state&) {}

static bool wait_wlock(const state& s) { return (s.rwcount != 0); }

static void acquire_wlock(state* s) {
assert(!(s->rwcount & writer_mask));
s->rwcount |= writer_mask;
}

static void release_wlock(state* s) {
assert(s->rwcount & writer_mask);
s->rwcount &= ~writer_mask;
}

static bool wait_rlock(const state& s) { return (s.rwcount & writer_mask) != 0; }

static void acquire_rlock(state* s) {
assert((s->rwcount & reader_mask) < reader_mask);
++(s->rwcount);
}

static bool release_rlock(state* s) {
assert(0 < (s->rwcount & reader_mask));
return (--(s->rwcount) == 0);
}
};

/// Writer prefer scheduling
///
/// NOTE:
/// If there are waiting writer, new readers are blocked until all shared lock
/// are released,
// and the writer thread can get exclusive lock in preference to blocked
// reader threads. This policy might introduce "Reader Starvation" if writers
// continuously request exclusive lock.
/// (see also PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)
///
struct WriterPrefer {
static const std::size_t locked = ~(~std::size_t(0u) >> 1); // MSB 1bit
static const std::size_t wait_mask = ~std::size_t(0u) >> 1;

struct state {
std::size_t nwriter = 0;
std::size_t nreader = 0;
};

static void before_wait_wlock(state* s) {
assert((s->nwriter & wait_mask) < wait_mask);
++(s->nwriter);
}

static bool wait_wlock(const state& s) {
return ((s.nwriter & locked) || 0 < s.nreader);
}

static void after_wait_wlock(state* s) {
assert(0 < (s->nwriter & wait_mask));
--(s->nwriter);
}

static void acquire_wlock(state* s) {
assert(!(s->nwriter & locked));
s->nwriter |= locked;
}

static void release_wlock(state* s) {
assert(s->nwriter & locked);
s->nwriter &= ~locked;
}

static bool wait_rlock(const state& s) { return (s.nwriter != 0); }

static void acquire_rlock(state* s) {
assert(!(s->nwriter & locked));
++(s->nreader);
}

static bool release_rlock(state* s) {
assert(0 < s->nreader);
return (--(s->nreader) == 0);
}
};

} // namespace rwlock
} // namespace yamc

#endif
Loading