Skip to content

Commit 23f66e0

Browse files
committed
Reordered connection holder's members for the purpose of L1 cache line
1 parent cbb81ac commit 23f66e0

File tree

4 files changed

+124
-42
lines changed

4 files changed

+124
-42
lines changed

dev/connection_holder.h

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <string> // std::string
1111
#endif
1212

13+
#include "functional/cxx_new.h"
1314
#include "error_code.h"
1415

1516
namespace sqlite_orm {
@@ -44,8 +45,8 @@ namespace sqlite_orm {
4445
};
4546

4647
connection_holder(std::string filename, bool openedForeverHint, std::function<void(sqlite3*)> onAfterOpen) :
47-
filename(std::move(filename)), _openedForeverHint{openedForeverHint},
48-
_onAfterOpen{std::move(onAfterOpen)} {}
48+
_openedForeverHint{openedForeverHint}, _onAfterOpen{std::move(onAfterOpen)},
49+
filename(std::move(filename)) {}
4950

5051
connection_holder(const connection_holder&) = delete;
5152

@@ -59,7 +60,7 @@ namespace sqlite_orm {
5960
// `maybeLock.isSynced`: the lock above already synchronized everything, so we can just atomically increment the counter
6061
// `!maybeLock.isSynced`: we presume that the connection is opened once in a single-threaded context [also open forever].
6162
// therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`.
62-
if (int prevCount = _retain_count.fetch_add(1, std::memory_order_relaxed); prevCount > 0) {
63+
if (int prevCount = _retainCount.fetch_add(1, std::memory_order_relaxed); prevCount > 0) {
6364
return;
6465
}
6566

@@ -78,7 +79,7 @@ namespace sqlite_orm {
7879
void release() {
7980
const maybe_lock maybeLock{_sync, !_openedForeverHint};
8081

81-
if (int prevCount = _retain_count.fetch_sub(
82+
if (int prevCount = _retainCount.fetch_sub(
8283
1,
8384
maybeLock.isSynced
8485
// the lock above already synchronized everything, so we can just atomically decrement the counter
@@ -107,36 +108,41 @@ namespace sqlite_orm {
107108
* @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code.
108109
*/
109110
int retain_count() const {
110-
return _retain_count.load(std::memory_order_relaxed);
111+
return _retainCount.load(std::memory_order_relaxed);
111112
}
112113

113-
const std::string filename;
114-
115114
protected:
116-
sqlite3* db = nullptr;
115+
alignas(polyfill::hardware_destructive_interference_size) sqlite3* db = nullptr;
117116

118117
private:
118+
std::atomic_int _retainCount{};
119119
const bool _openedForeverHint = false;
120-
const std::function<void(sqlite3* db)> _onAfterOpen;
121120
std::binary_semaphore _sync{1};
122-
std::atomic_int _retain_count{};
121+
122+
private:
123+
alignas(
124+
polyfill::hardware_destructive_interference_size) const std::function<void(sqlite3* db)> _onAfterOpen;
125+
126+
public:
127+
const std::string filename;
123128
};
124129
#else
125130
struct connection_holder {
126131
connection_holder(std::string filename,
127132
bool /*openedForeverHint*/,
128133
std::function<void(sqlite3*)> onAfterOpen) :
129-
filename(std::move(filename)), _onAfterOpen{std::move(onAfterOpen)} {}
134+
_onAfterOpen{std::move(onAfterOpen)}, filename(std::move(filename)) {}
130135

131136
connection_holder(const connection_holder&) = delete;
137+
132138
connection_holder(const connection_holder& other, std::function<void(sqlite3*)> onAfterOpen) :
133-
filename{other.filename}, _onAfterOpen{std::move(onAfterOpen)} {}
139+
_onAfterOpen{std::move(onAfterOpen)}, filename{other.filename} {}
134140

135141
void retain() {
136142
// first one opens the connection.
137143
// we presume that the connection is opened once in a single-threaded context [also open forever].
138144
// therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`.
139-
if (_retain_count.fetch_add(1, std::memory_order_relaxed) == 0) {
145+
if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) {
140146
int rc = sqlite3_open(this->filename.c_str(), &this->db);
141147
if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ {
142148
throw_translated_sqlite_error(this->db);
@@ -151,7 +157,7 @@ namespace sqlite_orm {
151157
void release() {
152158
// last one closes the connection.
153159
// we assume that this might happen by any thread, therefore the counter must serve as a synchronization point.
154-
if (_retain_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
160+
if (_retainCount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
155161
int rc = sqlite3_close(this->db);
156162
if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY {
157163
throw_translated_sqlite_error(this->db);
@@ -170,17 +176,26 @@ namespace sqlite_orm {
170176
* @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code.
171177
*/
172178
int retain_count() const {
173-
return _retain_count.load(std::memory_order_relaxed);
179+
return _retainCount.load(std::memory_order_relaxed);
174180
}
175181

176-
const std::string filename;
177-
178182
protected:
179-
sqlite3* db = nullptr;
183+
#if SQLITE_ORM_ALIGNED_NEW_SUPPORTED
184+
alignas(polyfill::hardware_destructive_interference_size)
185+
#endif
186+
sqlite3* db = nullptr;
187+
188+
private:
189+
std::atomic_int _retainCount{};
180190

181191
private:
182-
const std::function<void(sqlite3* db)> _onAfterOpen;
183-
std::atomic_int _retain_count{};
192+
#if SQLITE_ORM_ALIGNED_NEW_SUPPORTED
193+
alignas(polyfill::hardware_destructive_interference_size)
194+
#endif
195+
const std::function<void(sqlite3* db)> _onAfterOpen;
196+
197+
public:
198+
const std::string filename;
184199
};
185200
#endif
186201

dev/functional/cxx_core_features.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,12 @@
4949
#define SQLITE_ORM_STRUCTURED_BINDINGS_SUPPORTED
5050
#endif
5151

52+
#if __cpp_aligned_new >= 201606L
53+
#define SQLITE_ORM_ALIGNED_NEW_SUPPORTED
54+
#endif
55+
5256
#if __cpp_generic_lambdas >= 201707L
5357
#define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED
54-
#else
5558
#endif
5659

5760
#if __cpp_init_captures >= 201803L

dev/functional/cxx_new.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#ifdef SQLITE_ORM_IMPORT_STD_MODULE
4+
#include <version>
5+
#else
6+
#include <new>
7+
#endif
8+
9+
namespace sqlite_orm {
10+
namespace internal {
11+
namespace polyfill {
12+
#if __cpp_lib_hardware_interference_size >= 201703L
13+
using std::hardware_constructive_interference_size;
14+
using std::hardware_destructive_interference_size;
15+
#else
16+
constexpr size_t hardware_constructive_interference_size = 64;
17+
constexpr size_t hardware_destructive_interference_size = 64;
18+
#endif
19+
}
20+
}
21+
22+
namespace polyfill = internal::polyfill;
23+
}

include/sqlite_orm/sqlite_orm.h

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,12 @@ using std::nullptr_t;
9898
#define SQLITE_ORM_STRUCTURED_BINDINGS_SUPPORTED
9999
#endif
100100

101+
#if __cpp_aligned_new >= 201606L
102+
#define SQLITE_ORM_ALIGNED_NEW_SUPPORTED
103+
#endif
104+
101105
#if __cpp_generic_lambdas >= 201707L
102106
#define SQLITE_ORM_EXPLICIT_GENERIC_LAMBDA_SUPPORTED
103-
#else
104107
#endif
105108

106109
#if __cpp_init_captures >= 201803L
@@ -13887,6 +13890,30 @@ namespace sqlite_orm {
1388713890
#include <string> // std::string
1388813891
#endif
1388913892

13893+
// #include "functional/cxx_new.h"
13894+
13895+
#ifdef SQLITE_ORM_IMPORT_STD_MODULE
13896+
#include <version>
13897+
#else
13898+
#include <new>
13899+
#endif
13900+
13901+
namespace sqlite_orm {
13902+
namespace internal {
13903+
namespace polyfill {
13904+
#if __cpp_lib_hardware_interference_size >= 201703L
13905+
using std::hardware_constructive_interference_size;
13906+
using std::hardware_destructive_interference_size;
13907+
#else
13908+
constexpr size_t hardware_constructive_interference_size = 64;
13909+
constexpr size_t hardware_destructive_interference_size = 64;
13910+
#endif
13911+
}
13912+
}
13913+
13914+
namespace polyfill = internal::polyfill;
13915+
}
13916+
1389013917
// #include "error_code.h"
1389113918

1389213919
namespace sqlite_orm {
@@ -13921,8 +13948,8 @@ namespace sqlite_orm {
1392113948
};
1392213949

1392313950
connection_holder(std::string filename, bool openedForeverHint, std::function<void(sqlite3*)> onAfterOpen) :
13924-
filename(std::move(filename)), _openedForeverHint{openedForeverHint},
13925-
_onAfterOpen{std::move(onAfterOpen)} {}
13951+
_openedForeverHint{openedForeverHint}, _onAfterOpen{std::move(onAfterOpen)},
13952+
filename(std::move(filename)) {}
1392613953

1392713954
connection_holder(const connection_holder&) = delete;
1392813955

@@ -13936,7 +13963,7 @@ namespace sqlite_orm {
1393613963
// `maybeLock.isSynced`: the lock above already synchronized everything, so we can just atomically increment the counter
1393713964
// `!maybeLock.isSynced`: we presume that the connection is opened once in a single-threaded context [also open forever].
1393813965
// therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`.
13939-
if (int prevCount = _retain_count.fetch_add(1, std::memory_order_relaxed); prevCount > 0) {
13966+
if (int prevCount = _retainCount.fetch_add(1, std::memory_order_relaxed); prevCount > 0) {
1394013967
return;
1394113968
}
1394213969

@@ -13955,7 +13982,7 @@ namespace sqlite_orm {
1395513982
void release() {
1395613983
const maybe_lock maybeLock{_sync, !_openedForeverHint};
1395713984

13958-
if (int prevCount = _retain_count.fetch_sub(
13985+
if (int prevCount = _retainCount.fetch_sub(
1395913986
1,
1396013987
maybeLock.isSynced
1396113988
// the lock above already synchronized everything, so we can just atomically decrement the counter
@@ -13984,36 +14011,41 @@ namespace sqlite_orm {
1398414011
* @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code.
1398514012
*/
1398614013
int retain_count() const {
13987-
return _retain_count.load(std::memory_order_relaxed);
14014+
return _retainCount.load(std::memory_order_relaxed);
1398814015
}
1398914016

13990-
const std::string filename;
13991-
1399214017
protected:
13993-
sqlite3* db = nullptr;
14018+
alignas(polyfill::hardware_destructive_interference_size) sqlite3* db = nullptr;
1399414019

1399514020
private:
14021+
std::atomic_int _retainCount{};
1399614022
const bool _openedForeverHint = false;
13997-
const std::function<void(sqlite3* db)> _onAfterOpen;
1399814023
std::binary_semaphore _sync{1};
13999-
std::atomic_int _retain_count{};
14024+
14025+
private:
14026+
alignas(
14027+
polyfill::hardware_destructive_interference_size) const std::function<void(sqlite3* db)> _onAfterOpen;
14028+
14029+
public:
14030+
const std::string filename;
1400014031
};
1400114032
#else
1400214033
struct connection_holder {
1400314034
connection_holder(std::string filename,
1400414035
bool /*openedForeverHint*/,
1400514036
std::function<void(sqlite3*)> onAfterOpen) :
14006-
filename(std::move(filename)), _onAfterOpen{std::move(onAfterOpen)} {}
14037+
_onAfterOpen{std::move(onAfterOpen)}, filename(std::move(filename)) {}
1400714038

1400814039
connection_holder(const connection_holder&) = delete;
14040+
1400914041
connection_holder(const connection_holder& other, std::function<void(sqlite3*)> onAfterOpen) :
14010-
filename{other.filename}, _onAfterOpen{std::move(onAfterOpen)} {}
14042+
_onAfterOpen{std::move(onAfterOpen)}, filename{other.filename} {}
1401114043

1401214044
void retain() {
1401314045
// first one opens the connection.
1401414046
// we presume that the connection is opened once in a single-threaded context [also open forever].
1401514047
// therefore we can just use an atomic increment but don't need sequencing due to `prevCount > 0`.
14016-
if (_retain_count.fetch_add(1, std::memory_order_relaxed) == 0) {
14048+
if (_retainCount.fetch_add(1, std::memory_order_relaxed) == 0) {
1401714049
int rc = sqlite3_open(this->filename.c_str(), &this->db);
1401814050
if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY /*possible, but unexpected*/ {
1401914051
throw_translated_sqlite_error(this->db);
@@ -14028,7 +14060,7 @@ namespace sqlite_orm {
1402814060
void release() {
1402914061
// last one closes the connection.
1403014062
// we assume that this might happen by any thread, therefore the counter must serve as a synchronization point.
14031-
if (_retain_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
14063+
if (_retainCount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
1403214064
int rc = sqlite3_close(this->db);
1403314065
if (rc != SQLITE_OK) SQLITE_ORM_CPP_UNLIKELY {
1403414066
throw_translated_sqlite_error(this->db);
@@ -14047,17 +14079,26 @@ namespace sqlite_orm {
1404714079
* @attention While retrieving the reference count value is atomic it makes only sense at single-threaded points in code.
1404814080
*/
1404914081
int retain_count() const {
14050-
return _retain_count.load(std::memory_order_relaxed);
14082+
return _retainCount.load(std::memory_order_relaxed);
1405114083
}
1405214084

14053-
const std::string filename;
14054-
1405514085
protected:
14056-
sqlite3* db = nullptr;
14086+
#if SQLITE_ORM_ALIGNED_NEW_SUPPORTED
14087+
alignas(polyfill::hardware_destructive_interference_size)
14088+
#endif
14089+
sqlite3* db = nullptr;
14090+
14091+
private:
14092+
std::atomic_int _retainCount{};
1405714093

1405814094
private:
14059-
const std::function<void(sqlite3* db)> _onAfterOpen;
14060-
std::atomic_int _retain_count{};
14095+
#if SQLITE_ORM_ALIGNED_NEW_SUPPORTED
14096+
alignas(polyfill::hardware_destructive_interference_size)
14097+
#endif
14098+
const std::function<void(sqlite3* db)> _onAfterOpen;
14099+
14100+
public:
14101+
const std::string filename;
1406114102
};
1406214103
#endif
1406314104

0 commit comments

Comments
 (0)