Skip to content

Commit ff3ef5f

Browse files
lihuibaColdwings
andauthored
reimplement qspinlock using thread_local variables, and remove the dependence on photon vcpu (alibaba#965)
* reimplement qspinlock, to remove the dependece on photon vcpu, so that it can be used in non-photon environment. * Add lock perf for spinlock and mutex --------- Co-authored-by: Coldwings <coldwings@me.com>
1 parent 088d734 commit ff3ef5f

4 files changed

Lines changed: 92 additions & 25 deletions

File tree

examples/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ if (PHOTON_ENABLE_URING)
1313
target_compile_definitions(net-perf PRIVATE PHOTON_URING=1)
1414
endif()
1515

16+
add_executable(lock-perf perf/lock-perf.cpp)
17+
target_link_libraries(lock-perf PRIVATE photon_static)
18+
1619
add_executable(workpool-perf perf/workpool-perf.cpp)
1720
target_link_libraries(workpool-perf PRIVATE photon_static)
1821

examples/perf/lock-perf.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Copyright 2022 The Photon Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
#include <gflags/gflags.h>
18+
#include <photon/common/alog.h>
19+
#include <photon/photon.h>
20+
#include <photon/thread/thread11.h>
21+
22+
#include <chrono>
23+
#include <thread>
24+
#include <vector>
25+
#include "photon/thread/thread.h"
26+
27+
DEFINE_uint64(threads, 8, "threads requires lock");
28+
DEFINE_uint64(turn, 100000, "turns requiring lock for each thread");
29+
30+
template <typename locktype>
31+
void lockperf(const char* name) {
32+
std::vector<std::thread> ths;
33+
locktype mtx;
34+
for (size_t i = 0; i < FLAGS_threads; i++) {
35+
ths.emplace_back([&] {
36+
photon::init();
37+
DEFER(photon::fini());
38+
auto start = std::chrono::system_clock::now();
39+
for (size_t j = 0; j < FLAGS_turn; j++) {
40+
mtx.lock();
41+
mtx.unlock();
42+
}
43+
auto end = std::chrono::system_clock::now();
44+
LOG_TEMP("` time: `", name, (end - start).count());
45+
});
46+
}
47+
for (auto& x : ths) x.join();
48+
}
49+
50+
int main(int argc, char** arg) {
51+
gflags::ParseCommandLineFlags(&argc, &arg, true);
52+
log_output_level = ALOG_WARN;
53+
photon::init();
54+
DEFER(photon::fini());
55+
lockperf<photon::spinlock>("spin");
56+
lockperf<photon::qspinlock>("qspin");
57+
lockperf<photon::mutex>("ticket");
58+
return 0;
59+
}

thread/thread.cpp

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -569,8 +569,6 @@ namespace photon
569569
mee = &_default_event_engine;
570570
}
571571

572-
std::atomic<vcpu_base*> _qspin_next { nullptr };
573-
std::atomic<bool> _qspin_got_lock { false };
574572
static spinlock vcpu_list_lock; // lock when add, remove, iterate next
575573
static rwlock vcpu_list_rwlock; // rlock when iterate, wlock when remove
576574
static intrusive_list<vcpu_t, false> pvcpu;
@@ -1629,33 +1627,44 @@ R"(
16291627
serv.store(successor, std::memory_order_release);
16301628
}
16311629

1630+
struct qspinlock::holder {
1631+
std::atomic<holder*> next { nullptr };
1632+
std::atomic<bool> got_lock { false };
1633+
};
1634+
static thread_local qspinlock::holder qslholder;
1635+
int qspinlock::try_lock() {
1636+
holder* expected = nullptr;
1637+
bool ok = _owner_tail.compare_exchange_strong(expected,
1638+
&qslholder, std::memory_order_acq_rel);
1639+
return int(ok) - 1;
1640+
}
16321641
int qspinlock::lock() {
1633-
auto vcpu = (vcpu_t*)get_vcpu();
1634-
assert(vcpu->_qspin_next == nullptr);
1635-
auto old_tail = (vcpu_t*)_owner_tail.exchange(vcpu, std::memory_order_acq_rel);
1642+
// forbid gcc to silly update h
1643+
auto h = &qslholder; asm volatile("": "+r"(h));
1644+
assert(h->next == nullptr);
1645+
auto old_tail = _owner_tail.exchange(h, std::memory_order_acq_rel);
16361646
if (!old_tail) return 0;
16371647

1638-
vcpu->_qspin_got_lock.store(false, std::memory_order_relaxed);
1639-
assert(old_tail->_qspin_next.load(std::memory_order_acquire) == nullptr);
1640-
old_tail->_qspin_next.store(vcpu, std::memory_order_release);
1648+
h->got_lock.store(false, std::memory_order_relaxed);
1649+
assert(old_tail->next.load(std::memory_order_acquire) == nullptr);
1650+
old_tail->next.store(h, std::memory_order_release);
16411651
do { spin_wait(); }
1642-
while (vcpu->_qspin_got_lock.load(std::memory_order_acquire) == false);
1652+
while (h->got_lock.load(std::memory_order_acquire) == false);
16431653
return 0;
16441654
}
1645-
16461655
void qspinlock::unlock() {
1647-
auto vcpu = (vcpu_t*)get_vcpu();
1656+
// forbid gcc to silly update h
1657+
auto h = &qslholder; asm volatile("": "+r"(h));
16481658
while(true) {
1649-
auto next = (vcpu_t*) vcpu->_qspin_next.load(std::memory_order_acquire);
1659+
auto next = h->next.load(std::memory_order_acquire);
16501660
if (next) { // resume the next waiter, if there is one
1651-
vcpu->_qspin_next.store(nullptr, std::memory_order_release);
1652-
next->_qspin_got_lock.store(true, std::memory_order_release);
1661+
h->next.store(nullptr, std::memory_order_release);
1662+
next->got_lock.store(true, std::memory_order_release);
16531663
return;
16541664
} else { // do unlock if there is no waiter
1655-
vcpu_base* expected = vcpu;
1656-
bool ok = _owner_tail.compare_exchange_strong(expected,
1657-
nullptr, std::memory_order_acq_rel);
1658-
if (ok) return;
1665+
auto expected = h;
1666+
if (_owner_tail.compare_exchange_strong(expected,
1667+
nullptr, std::memory_order_acq_rel)) return;
16591668
spin_wait(); // unlock failed, wait and try again
16601669
}
16611670
}

thread/thread.h

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,19 +237,15 @@ namespace photon
237237

238238
class qspinlock {
239239
public:
240-
int try_lock() {
241-
vcpu_base* expected = nullptr;
242-
bool ok = _owner_tail.compare_exchange_strong(expected,
243-
get_vcpu(), std::memory_order_acq_rel);
244-
return int(ok) - 1;
245-
}
240+
int try_lock();
246241
int lock();
247242
bool locked() const {
248243
return _owner_tail.load(std::memory_order_acquire);
249244
}
250245
void unlock();
251246
protected:
252-
std::atomic<vcpu_base*> _owner_tail { nullptr };
247+
struct holder;
248+
std::atomic<holder*> _owner_tail { nullptr };
253249
};
254250

255251
class ticket_spinlock {

0 commit comments

Comments
 (0)