Commit ce047900 authored by Sergey Vojtovich's avatar Sergey Vojtovich

MDEV-14482 - Cache line contention on ut_rnd_ulint_counter()

InnoDB RNG maintains global state, causing otherwise unnecessary bus
traffic. Even worse this is cross-mutex traffic. That is different
mutexes suffer from contention.

Fixed delay of 4 was verified to give best throughput by OLTP update
index and read-write benchmarks on Intel Broadwell (2/20/40) and
ARM (1/46/46).
parent 92d233a5
SET @start_global_value = @@global.innodb_spin_wait_delay; SET @start_global_value = @@global.innodb_spin_wait_delay;
SELECT @start_global_value; SELECT @start_global_value;
@start_global_value @start_global_value
6 4
Valid values are zero or above Valid values are zero or above
select @@global.innodb_spin_wait_delay >=0; select @@global.innodb_spin_wait_delay >=0;
@@global.innodb_spin_wait_delay >=0 @@global.innodb_spin_wait_delay >=0
1 1
select @@global.innodb_spin_wait_delay; select @@global.innodb_spin_wait_delay;
@@global.innodb_spin_wait_delay @@global.innodb_spin_wait_delay
6 4
select @@session.innodb_spin_wait_delay; select @@session.innodb_spin_wait_delay;
ERROR HY000: Variable 'innodb_spin_wait_delay' is a GLOBAL variable ERROR HY000: Variable 'innodb_spin_wait_delay' is a GLOBAL variable
show global variables like 'innodb_spin_wait_delay'; show global variables like 'innodb_spin_wait_delay';
Variable_name Value Variable_name Value
innodb_spin_wait_delay 6 innodb_spin_wait_delay 4
show session variables like 'innodb_spin_wait_delay'; show session variables like 'innodb_spin_wait_delay';
Variable_name Value Variable_name Value
innodb_spin_wait_delay 6 innodb_spin_wait_delay 4
select * from information_schema.global_variables where variable_name='innodb_spin_wait_delay'; select * from information_schema.global_variables where variable_name='innodb_spin_wait_delay';
VARIABLE_NAME VARIABLE_VALUE VARIABLE_NAME VARIABLE_VALUE
INNODB_SPIN_WAIT_DELAY 6 INNODB_SPIN_WAIT_DELAY 4
select * from information_schema.session_variables where variable_name='innodb_spin_wait_delay'; select * from information_schema.session_variables where variable_name='innodb_spin_wait_delay';
VARIABLE_NAME VARIABLE_VALUE VARIABLE_NAME VARIABLE_VALUE
INNODB_SPIN_WAIT_DELAY 6 INNODB_SPIN_WAIT_DELAY 4
set global innodb_spin_wait_delay=10; set global innodb_spin_wait_delay=10;
select @@global.innodb_spin_wait_delay; select @@global.innodb_spin_wait_delay;
@@global.innodb_spin_wait_delay @@global.innodb_spin_wait_delay
...@@ -38,7 +38,7 @@ ERROR HY000: Variable 'innodb_spin_wait_delay' is a GLOBAL variable and should b ...@@ -38,7 +38,7 @@ ERROR HY000: Variable 'innodb_spin_wait_delay' is a GLOBAL variable and should b
set global innodb_spin_wait_delay=DEFAULT; set global innodb_spin_wait_delay=DEFAULT;
select @@global.innodb_spin_wait_delay; select @@global.innodb_spin_wait_delay;
@@global.innodb_spin_wait_delay @@global.innodb_spin_wait_delay
6 4
set global innodb_spin_wait_delay=0; set global innodb_spin_wait_delay=0;
select @@global.innodb_spin_wait_delay; select @@global.innodb_spin_wait_delay;
@@global.innodb_spin_wait_delay @@global.innodb_spin_wait_delay
...@@ -111,4 +111,4 @@ INNODB_SPIN_WAIT_DELAY 0 ...@@ -111,4 +111,4 @@ INNODB_SPIN_WAIT_DELAY 0
SET @@global.innodb_spin_wait_delay = @start_global_value; SET @@global.innodb_spin_wait_delay = @start_global_value;
SELECT @@global.innodb_spin_wait_delay; SELECT @@global.innodb_spin_wait_delay;
@@global.innodb_spin_wait_delay @@global.innodb_spin_wait_delay
6 4
...@@ -1996,12 +1996,12 @@ READ_ONLY YES ...@@ -1996,12 +1996,12 @@ READ_ONLY YES
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME INNODB_SPIN_WAIT_DELAY VARIABLE_NAME INNODB_SPIN_WAIT_DELAY
SESSION_VALUE NULL SESSION_VALUE NULL
GLOBAL_VALUE 6 GLOBAL_VALUE 4
GLOBAL_VALUE_ORIGIN COMPILE-TIME GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE 6 DEFAULT_VALUE 4
VARIABLE_SCOPE GLOBAL VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE INT UNSIGNED VARIABLE_TYPE INT UNSIGNED
VARIABLE_COMMENT Maximum delay between polling for a spin lock (6 by default) VARIABLE_COMMENT Maximum delay between polling for a spin lock (4 by default)
NUMERIC_MIN_VALUE 0 NUMERIC_MIN_VALUE 0
NUMERIC_MAX_VALUE 6000 NUMERIC_MAX_VALUE 6000
NUMERIC_BLOCK_SIZE 0 NUMERIC_BLOCK_SIZE 0
......
...@@ -1758,11 +1758,8 @@ dict_stats_analyze_index_for_n_prefix( ...@@ -1758,11 +1758,8 @@ dict_stats_analyze_index_for_n_prefix(
ut_a(left <= right); ut_a(left <= right);
ut_a(right <= last_idx_on_level); ut_a(right <= last_idx_on_level);
/* we do not pass (left, right) because we do not want to ask const ulint rnd = right == left ? 0 :
ut_rnd_interval() to work with too big numbers since ut_rnd_gen_ulint() % (right - left);
ib_uint64_t could be bigger than ulint */
const ulint rnd = ut_rnd_interval(
0, static_cast<ulint>(right - left));
const ib_uint64_t dive_below_idx const ib_uint64_t dive_below_idx
= boundaries->at(static_cast<unsigned>(left + rnd)); = boundaries->at(static_cast<unsigned>(left + rnd));
......
...@@ -5978,17 +5978,6 @@ fil_tablespace_iterate( ...@@ -5978,17 +5978,6 @@ fil_tablespace_iterate(
innodb_data_file_key, filepath, innodb_data_file_key, filepath,
OS_FILE_OPEN, OS_FILE_READ_WRITE, srv_read_only_mode, &success); OS_FILE_OPEN, OS_FILE_READ_WRITE, srv_read_only_mode, &success);
DBUG_EXECUTE_IF("fil_tablespace_iterate_failure",
{
static bool once;
if (!once || ut_rnd_interval(0, 10) == 5) {
once = true;
success = false;
os_file_close(file);
}
});
if (!success) { if (!success) {
/* The following call prints an error message */ /* The following call prints an error message */
os_file_get_last_error(true); os_file_get_last_error(true);
......
...@@ -20559,8 +20559,8 @@ static MYSQL_SYSVAR_ULONG(sync_spin_loops, srv_n_spin_wait_rounds, ...@@ -20559,8 +20559,8 @@ static MYSQL_SYSVAR_ULONG(sync_spin_loops, srv_n_spin_wait_rounds,
static MYSQL_SYSVAR_UINT(spin_wait_delay, srv_spin_wait_delay, static MYSQL_SYSVAR_UINT(spin_wait_delay, srv_spin_wait_delay,
PLUGIN_VAR_OPCMDARG, PLUGIN_VAR_OPCMDARG,
"Maximum delay between polling for a spin lock (6 by default)", "Maximum delay between polling for a spin lock (4 by default)",
NULL, NULL, 6, 0, 6000, 0); NULL, NULL, 4, 0, 6000, 0);
static MYSQL_SYSVAR_ULONG(thread_concurrency, srv_thread_concurrency, static MYSQL_SYSVAR_ULONG(thread_concurrency, srv_thread_concurrency,
PLUGIN_VAR_RQCMDARG, PLUGIN_VAR_RQCMDARG,
......
...@@ -225,7 +225,7 @@ struct TTASFutexMutex { ...@@ -225,7 +225,7 @@ struct TTASFutexMutex {
return; return;
} }
ut_delay(ut_rnd_interval(0, max_delay)); ut_delay(max_delay);
} }
for (n_waits= 0;; n_waits++) { for (n_waits= 0;; n_waits++) {
...@@ -362,7 +362,7 @@ struct TTASMutex { ...@@ -362,7 +362,7 @@ struct TTASMutex {
uint32_t n_spins = 0; uint32_t n_spins = 0;
while (!try_lock()) { while (!try_lock()) {
ut_delay(ut_rnd_interval(0, max_delay)); ut_delay(max_delay);
if (++n_spins == max_spins) { if (++n_spins == max_spins) {
os_thread_yield(); os_thread_yield();
max_spins+= step; max_spins+= step;
...@@ -516,7 +516,7 @@ struct TTASEventMutex { ...@@ -516,7 +516,7 @@ struct TTASEventMutex {
sync_array_wait_event(sync_arr, cell); sync_array_wait_event(sync_arr, cell);
} }
} else { } else {
ut_delay(ut_rnd_interval(0, max_delay)); ut_delay(max_delay);
} }
} }
......
...@@ -61,16 +61,6 @@ UNIV_INLINE ...@@ -61,16 +61,6 @@ UNIV_INLINE
ulint ulint
ut_rnd_gen_ulint(void); ut_rnd_gen_ulint(void);
/*==================*/ /*==================*/
/********************************************************//**
Generates a random integer from a given interval.
@return the 'random' number */
UNIV_INLINE
ulint
ut_rnd_interval(
/*============*/
ulint low, /*!< in: low limit; can generate also this value */
ulint high); /*!< in: high limit; can generate also this value */
/*******************************************************//** /*******************************************************//**
The following function generates a hash value for a ulint integer The following function generates a hash value for a ulint integer
to a hash table of size table_size, which should be a prime or some to a hash table of size table_size, which should be a prime or some
......
...@@ -97,30 +97,6 @@ ut_rnd_gen_ulint(void) ...@@ -97,30 +97,6 @@ ut_rnd_gen_ulint(void)
return(rnd); return(rnd);
} }
/********************************************************//**
Generates a random integer from a given interval.
@return the 'random' number */
UNIV_INLINE
ulint
ut_rnd_interval(
/*============*/
ulint low, /*!< in: low limit; can generate also this value */
ulint high) /*!< in: high limit; can generate also this value */
{
ulint rnd;
ut_ad(high >= low);
if (low == high) {
return(low);
}
rnd = ut_rnd_gen_ulint();
return(low + (rnd % (high - low)));
}
/*******************************************************//** /*******************************************************//**
The following function generates a hash value for a ulint integer The following function generates a hash value for a ulint integer
to a hash table of size table_size, which should be a prime to a hash table of size table_size, which should be a prime
......
...@@ -7285,7 +7285,7 @@ lock_trx_release_locks( ...@@ -7285,7 +7285,7 @@ lock_trx_release_locks(
/** Doing an implicit to explicit conversion /** Doing an implicit to explicit conversion
should not be expensive. */ should not be expensive. */
ut_delay(ut_rnd_interval(0, srv_spin_wait_delay)); ut_delay(srv_spin_wait_delay);
} }
lock_mutex_enter(); lock_mutex_enter();
......
...@@ -317,10 +317,7 @@ rw_lock_s_lock_spin( ...@@ -317,10 +317,7 @@ rw_lock_s_lock_spin(
while (i < srv_n_spin_wait_rounds && while (i < srv_n_spin_wait_rounds &&
my_atomic_load32_explicit(&lock->lock_word, my_atomic_load32_explicit(&lock->lock_word,
MY_MEMORY_ORDER_RELAXED) <= 0) { MY_MEMORY_ORDER_RELAXED) <= 0) {
if (srv_spin_wait_delay) { ut_delay(srv_spin_wait_delay);
ut_delay(ut_rnd_interval(0, srv_spin_wait_delay));
}
i++; i++;
} }
...@@ -441,9 +438,7 @@ rw_lock_x_lock_wait_func( ...@@ -441,9 +438,7 @@ rw_lock_x_lock_wait_func(
HMT_low(); HMT_low();
while (my_atomic_load32_explicit(&lock->lock_word, MY_MEMORY_ORDER_RELAXED) < threshold) { while (my_atomic_load32_explicit(&lock->lock_word, MY_MEMORY_ORDER_RELAXED) < threshold) {
if (srv_spin_wait_delay) { ut_delay(srv_spin_wait_delay);
ut_delay(ut_rnd_interval(0, srv_spin_wait_delay));
}
if (i < srv_n_spin_wait_rounds) { if (i < srv_n_spin_wait_rounds) {
i++; i++;
...@@ -714,12 +709,7 @@ rw_lock_x_lock_func( ...@@ -714,12 +709,7 @@ rw_lock_x_lock_func(
HMT_low(); HMT_low();
while (i < srv_n_spin_wait_rounds while (i < srv_n_spin_wait_rounds
&& my_atomic_load32_explicit(&lock->lock_word, MY_MEMORY_ORDER_RELAXED) <= X_LOCK_HALF_DECR) { && my_atomic_load32_explicit(&lock->lock_word, MY_MEMORY_ORDER_RELAXED) <= X_LOCK_HALF_DECR) {
ut_delay(srv_spin_wait_delay);
if (srv_spin_wait_delay) {
ut_delay(ut_rnd_interval(
0, srv_spin_wait_delay));
}
i++; i++;
} }
...@@ -820,12 +810,7 @@ rw_lock_sx_lock_func( ...@@ -820,12 +810,7 @@ rw_lock_sx_lock_func(
/* Spin waiting for the lock_word to become free */ /* Spin waiting for the lock_word to become free */
while (i < srv_n_spin_wait_rounds while (i < srv_n_spin_wait_rounds
&& my_atomic_load32_explicit(&lock->lock_word, MY_MEMORY_ORDER_RELAXED) <= X_LOCK_HALF_DECR) { && my_atomic_load32_explicit(&lock->lock_word, MY_MEMORY_ORDER_RELAXED) <= X_LOCK_HALF_DECR) {
ut_delay(srv_spin_wait_delay);
if (srv_spin_wait_delay) {
ut_delay(ut_rnd_interval(
0, srv_spin_wait_delay));
}
i++; i++;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment