Skip to content

Commit 1f3622d

Browse files
author
Marcin Babij
committed
Bug#37366607 InnoDB: Simulated AIO handler is slow on older platforms on higher IO throughput
Symptom: During high volume of AIO operations when Simulated AIO is used, the performance of IO is greatly decreased. Root cause: The `os_aio_simulated_handler` calls `SimulatedAIOHandler::select()` which indirectly calls `select_if_older()` which uses `std::chrono::steady_clock::now()` repeatedly for each of reserved slots, when selecting a next IO to execute. On older platforms the `std::chrono::steady_clock::now()` causes a costly syscall, and as the `SimulatedAIOHandler::select()` is executed under the AIO::m_mutex, this greatly reduces the IO concurrency and thus throughput. Fix: The `std::chrono::steady_clock::now()` is called once per `SimulatedAIOHandler::select()` call, before grabbing the AIO mutex. Change-Id: Ifa8eabd43431ca3e537782ceb97a8b8705f3a1ec
1 parent ed91ea5 commit 1f3622d

File tree

1 file changed

+37
-51
lines changed

1 file changed

+37
-51
lines changed

storage/innobase/os/os0file.cc

Lines changed: 37 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6925,13 +6925,7 @@ class SimulatedAIOHandler {
69256925
@param[in,out] array The AIO array
69266926
@param[in] segment Local segment in the array */
69276927
SimulatedAIOHandler(AIO *array, ulint segment)
6928-
: m_oldest(),
6929-
m_n_elems(),
6930-
m_lowest_offset(std::numeric_limits<uint64_t>::max()),
6931-
m_array(array),
6932-
m_n_slots(),
6933-
m_segment(segment),
6934-
m_buf() {
6928+
: m_n_elems(), m_array(array), m_n_slots(), m_segment(segment), m_buf() {
69356929
ut_ad(m_segment < 100);
69366930

69376931
m_slots.resize(OS_AIO_MERGE_N_CONSECUTIVE);
@@ -6943,10 +6937,8 @@ class SimulatedAIOHandler {
69436937
/** Reset the state of the handler
69446938
@param[in] n_slots Number of pending AIO operations supported */
69456939
void init(ulint n_slots) {
6946-
m_oldest = std::chrono::seconds::zero();
69476940
m_n_elems = 0;
69486941
m_n_slots = n_slots;
6949-
m_lowest_offset = std::numeric_limits<uint64_t>::max();
69506942

69516943
ut::aligned_free(m_buf);
69526944
m_buf = nullptr;
@@ -6984,13 +6976,13 @@ class SimulatedAIOHandler {
69846976
/** If there are at least 2 seconds old requests, then pick the
69856977
oldest one to prevent starvation. If several requests have the
69866978
same age, then pick the one at the lowest offset.
6979+
@param[in] current_time Time of start of the slot selection.
69876980
@return true if request was selected */
6988-
bool select() {
6989-
if (!select_oldest()) {
6990-
return (select_lowest_offset());
6981+
bool select(std::chrono::steady_clock::time_point current_time) {
6982+
if (select_oldest(current_time)) {
6983+
return true;
69916984
}
6992-
6993-
return (true);
6985+
return select_lowest_offset();
69946986
}
69956987

69966988
/** Check if there are several consecutive blocks
@@ -7152,20 +7144,20 @@ class SimulatedAIOHandler {
71527144

71537145
ulint offset = m_segment * m_n_slots;
71547146

7155-
m_lowest_offset = std::numeric_limits<uint64_t>::max();
7147+
auto lowest_offset = std::numeric_limits<uint64_t>::max();
71567148

71577149
for (ulint i = 0; i < m_n_slots; ++i) {
71587150
Slot *slot;
71597151

71607152
slot = m_array->at(i + offset);
71617153

7162-
if (slot->is_reserved && slot->offset < m_lowest_offset) {
7154+
if (slot->is_reserved && slot->offset < lowest_offset) {
71637155
/* Found an i/o request */
71647156
m_slots[0] = slot;
71657157

71667158
m_n_elems = 1;
71677159

7168-
m_lowest_offset = slot->offset;
7160+
lowest_offset = slot->offset;
71697161
}
71707162
}
71717163

@@ -7175,50 +7167,39 @@ class SimulatedAIOHandler {
71757167
typedef std::vector<Slot *> slots_t;
71767168

71777169
private:
7178-
/** Select the slot if it is older than the current oldest slot.
7179-
@param[in] slot The slot to check */
7180-
void select_if_older(Slot *slot) {
7181-
const auto time_diff =
7182-
std::max(std::chrono::steady_clock::now() - slot->reservation_time,
7183-
std::chrono::steady_clock::duration{0});
7184-
7185-
if (time_diff >= std::chrono::seconds{2}) {
7186-
if ((time_diff > m_oldest) ||
7187-
(time_diff == m_oldest && slot->offset < m_lowest_offset)) {
7188-
/* Found an i/o request */
7189-
m_slots[0] = slot;
7190-
7191-
m_n_elems = 1;
7170+
/** Select the oldest slot in the array if there are any older than 2s.
7171+
@param[in] current_time Time of start of the slot selection.
7172+
@return true if any slot older than 2s is found */
7173+
bool select_oldest(std::chrono::steady_clock::time_point current_time) {
7174+
ut_ad(m_n_elems == 0);
71927175

7193-
m_oldest = time_diff;
7176+
ulint offset = m_n_slots * m_segment;
71947177

7195-
m_lowest_offset = slot->offset;
7178+
auto currrent_slot = m_array->at(offset);
7179+
Slot *oldest_slot = nullptr;
7180+
for (size_t i = 0; i < m_n_slots; ++i, ++currrent_slot) {
7181+
if (currrent_slot->is_reserved &&
7182+
(oldest_slot == nullptr ||
7183+
currrent_slot->reservation_time < oldest_slot->reservation_time ||
7184+
(currrent_slot->reservation_time == oldest_slot->reservation_time &&
7185+
currrent_slot->offset < oldest_slot->offset))) {
7186+
oldest_slot = currrent_slot;
71967187
}
71977188
}
7198-
}
71997189

7200-
/** Select th oldest slot in the array
7201-
@return true if oldest slot found */
7202-
bool select_oldest() {
7203-
ut_ad(m_n_elems == 0);
7190+
if (oldest_slot != nullptr &&
7191+
current_time - oldest_slot->reservation_time >=
7192+
std::chrono::seconds{2}) {
7193+
m_slots[0] = oldest_slot;
72047194

7205-
Slot *slot;
7206-
ulint offset = m_n_slots * m_segment;
7207-
7208-
slot = m_array->at(offset);
7209-
7210-
for (ulint i = 0; i < m_n_slots; ++i, ++slot) {
7211-
if (slot->is_reserved) {
7212-
select_if_older(slot);
7213-
}
7195+
m_n_elems = 1;
7196+
return true;
72147197
}
72157198

7216-
return (m_n_elems > 0);
7199+
return false;
72177200
}
72187201

7219-
std::chrono::steady_clock::duration m_oldest;
72207202
ulint m_n_elems;
7221-
os_offset_t m_lowest_offset;
72227203

72237204
AIO *m_array;
72247205
ulint m_n_slots;
@@ -7294,6 +7275,11 @@ static dberr_t os_aio_simulated_handler(ulint global_segment, fil_node_t **m1,
72947275

72957276
srv_set_io_thread_op_info(global_segment, "looking for i/o requests (b)");
72967277

7278+
/* Take the current time once as it may cause syscalls and not be too
7279+
performant, it improves throughput greatly to have this executed before
7280+
we grab the AIO mutex. */
7281+
const auto current_time = std::chrono::steady_clock::now();
7282+
72977283
array->acquire();
72987284

72997285
ulint n_reserved;
@@ -7317,7 +7303,7 @@ static dberr_t os_aio_simulated_handler(ulint global_segment, fil_node_t **m1,
73177303

73187304
return (DB_SUCCESS);
73197305

7320-
} else if (handler.select()) {
7306+
} else if (handler.select(current_time)) {
73217307
break;
73227308
}
73237309

0 commit comments

Comments
 (0)