-
Notifications
You must be signed in to change notification settings - Fork 376
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
Add multithreaded sample sort and use it to sort memtx keys in secondary indexes on startup #8562
Conversation
@@ -1412,6 +1413,9 @@ memtx_engine_new(const char *snap_dirname, bool force_recovery, | |||
memtx->state = MEMTX_INITIALIZED; | |||
memtx->max_tuple_size = MAX_TUPLE_SIZE; | |||
memtx->force_recovery = force_recovery; | |||
if (sort_threads == 0) | |||
sort_threads = get_nprocs(); | |||
memtx->sort_threads = sort_threads; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest default behavior sort_threads = max(1, (int)get_nprocs() - 1)
Try not to step on the same rake - do not consume all cores by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it won't much help as threads number limit is added to keep others Tarantool instances running while this one is starting. Other instances number can be more then one and also Tarantool have more then one thread that can be loaded with work. I guess it is better to use introduced option to limit starting Tarantool according to the installation needs.
Yet I'll add low limit 1 (and high too) on sorting threads.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion regarding wording in a changelog:
* Added the `box.cfg.memtx_sort_threads` parameter that specifies the number of threads
used to sort keys of secondary indexes on loading a memtx database (gh-3389).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see a few comments. I haven't reviewed the algorithm in detail yet so there may be more.
7cc088c
to
cd3aad3
Compare
a33b2f5
to
0e17a7a
Compare
0e17a7a
to
c8cdabb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost good for me, see several comments.
src/lib/core/tt_sort.c
Outdated
* application faster if there is no much data in database (test cases in | ||
* particular). Otherwise sorting will be done in threads. | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to be an excess line break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
## feature/box | ||
|
||
* Added the `box.cfg.memtx_sort_threads` parameter that specifies the number of | ||
threads used to sort keys of secondary indexes on loading a memtx database |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should directly state that openmp is used no more. Actually the patch is a kind of breaking change: before an advanced user can specify OMP_NUM_THREADS as some value. Now that think doesn't work anymore. Maybe we should read the variable if memtx_sort_threads is not specified, just for backward compatibility.. Perhaps under a compat option..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
if (sort_threads == 0) {
char *ompnum_str = getenv_safe("OMP_NUM_THREADS", NULL, 0);
if (ompnum_str != NULL) {
long ompnum = strtol(ompnum_str, NULL, 10);
if (ompnum > 0 && ompnum <= TT_SORT_THREADS_MAX) {
say_warn("OMP_NUM_THREADS is used to set number"
" of sorting threads. Use cfg option"
"'memtx_sort_threads' instead.");
sort_threads = ompnum;
}
free(ompnum_str);
}
if (sort_threads == 0) {
sort_threads = get_nprocs();
if (sort_threads < 1)
sort_threads = 1;
else if (sort_threads > TT_SORT_THREADS_MAX)
sort_threads = TT_SORT_THREADS_MAX;
}
}
memtx->sort_threads = sort_threads;
src/lib/core/tt_sort.c
Outdated
* place most left and most right boundaries at imaginary indexes `-1` and | ||
* `size of splitters` respectively. | ||
*/ | ||
static inline int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Usually we does not use inline
in static functions within .c file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
src/lib/core/tt_sort.c
Outdated
* Find bucket for element using binary search among sorted in ascending | ||
* order splitters. | ||
* | ||
* Bucket number is `thread_count`, thus bucket boundraries (splitters) number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me number
here is misleading. I think count
will be correct and sound.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to our coding style, generally, you want your comments to tell WHAT your code does, not HOW.
In other word the comment should describe what find_bucket
makes and returns. It would be a good idea (but not necessary) to state that the 0 <= return value < thread_count
. This comment is needed when you use the function.
The comment about imaginary indexes is also good, but should be placed in the function body. This comment is needed for better understanding when you read the function code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to:
/**
* Find bucket for element using binary search among sorted in ascending
* order splitters.
*
* Return index of the bucket for the element.
*/
static int
find_bucket(struct tt_sort *sort, void *elem)
{
/*
* Bucket count is `thread_count`, thus bucket boundaries (splitters)
* count is `thread_count - 1` omitting most left and most right
* boundaries. Let's place most left and most right boundaries at
* imaginary indexes `-1` and `size of splitters` respectively.
*/
I didn't write the exact assertion for the return value in the at the file level. Looks like term index should be enough.
src/lib/core/tt_sort.c
Outdated
* Sample sort algorithm data. | ||
*/ | ||
struct tt_sort { | ||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: We usually use one-line comments for short descriptions. Sometimes it simplifies code observation:
/** The data being sorted. */
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used one-line comment after f2f discussion.
/** | ||
* Data for a single sample sort worker thread. | ||
*/ | ||
struct sort_worker { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit inconsistent name. I mean struct tt_sort
and struct sort_worker
.
I would name them struct sort_data
and struct sort_worker
, or struct tt_sort
and tt_worker
..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to sort_data
.
sort.cmp_arg = cmp_arg; | ||
sort.thread_count = thread_count; | ||
|
||
if (thread_count == 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function (tt_sort
) is too big. It seems that if we create a couple of helpers (for example tt_sort_one_thread
) it would be much shorter and thus understandable.
99af2a8
to
74bc106
Compare
DО NOT MERGE before tarantool/test-run#391. |
74bc106
to
11a963d
Compare
11a963d
to
f3c5427
Compare
|
f3c5427
to
f83a2cc
Compare
The algorithm runs sort in multiple threads and does not use OpenMP. It has better threads utilization right from the beginning but probably a worse constant than parallel qsort. See details in code comments. Besides sort is not performed in calling thread but instead in spawned worker threads. Calling thread yields waiting for worker threads to finish. Exception is small data size, in this case sorting is executed in calling thread saving time on spawning a thread. This should speed up test execution. This is existing behaviour of qsort_arg but data size threshold is reduced from 128000 to 1024. Part of tarantool#3389 NO_CHANGELOG=internal NO_DOC=internal
Bump test-run to new version with the following improvements: - Drop setting OMP_NUM_THREADS Part of tarantool#3389 NO_DOC=testing stuff NO_TEST=testing stuff NO_CHANGELOG=testing stuff
f83a2cc
to
f3ed1ad
Compare
Next changes are done to pass full CI:
--- a/src/box/memtx_engine.cc
+++ b/src/box/memtx_engine.cc
@@ -33,7 +33,6 @@
#include <small/quota.h>
#include <small/small.h>
#include <small/mempool.h>
-#include <sys/sysinfo.h>
#include "fiber.h"
#include "errinj.h"
@@ -1519,11 +1518,14 @@ memtx_engine_new(const char *snap_dirname, bool force_recovery,
free(ompnum_str);
}
if (sort_threads == 0) {
- sort_threads = get_nprocs();
- if (sort_threads < 1)
+ sort_threads = sysconf(_SC_NPROCESSORS_ONLN);
+ if (sort_threads < 1) {
+ say_warn("Cannot get number of processors."
+ "Fallback to single processor.");
sort_threads = 1;
- else if (sort_threads > TT_SORT_THREADS_MAX)
+ } else if (sort_threads > TT_SORT_THREADS_MAX) {
sort_threads = TT_SORT_THREADS_MAX;
+ }
}
}
create_unit_test(PREFIX tt_sort
SOURCES tt_sort.cc core_test_utils.c
- LIBRARIES misc unit box
+ LIBRARIES unit core
) |
f3ed1ad
to
e8c81ef
Compare
Fixed messages whitespaces: diff --git a/src/box/box.cc b/src/box/box.cc
index cc61ba743..b9a5e0c66 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -1675,7 +1675,7 @@ box_check_memtx_sort_threads(void)
if (cfg_isnumber("memtx_sort_threads") &&
(num <= 0 || num > TT_SORT_THREADS_MAX))
tnt_raise(ClientError, ER_CFG, "memtx_sort_threads",
- tt_sprintf("must be greater than 0 and less than or "
+ tt_sprintf("must be greater than 0 and less than or"
" equal to %d", TT_SORT_THREADS_MAX));
}
diff --git a/src/box/memtx_engine.cc b/src/box/memtx_engine.cc
index 49cff1f6d..6ba8eba11 100644
--- a/src/box/memtx_engine.cc
+++ b/src/box/memtx_engine.cc
@@ -1512,7 +1512,7 @@ memtx_engine_new(const char *snap_dirname, bool force_recovery,
if (ompnum > 0 && ompnum <= TT_SORT_THREADS_MAX) {
say_warn("OMP_NUM_THREADS is used to set number"
" of sorting threads. Use cfg option"
- "'memtx_sort_threads' instead.");
+ " 'memtx_sort_threads' instead.");
sort_threads = ompnum;
}
free(ompnum_str);
@@ -1520,8 +1520,8 @@ memtx_engine_new(const char *snap_dirname, bool force_recovery,
if (sort_threads == 0) {
sort_threads = sysconf(_SC_NPROCESSORS_ONLN);
if (sort_threads < 1) {
- say_warn("Cannot get number of processors."
- "Fallback to single processor.");
+ say_warn("Cannot get number of processors. "
+ "Fallback to single processor.");
sort_threads = 1;
} else if (sort_threads > TT_SORT_THREADS_MAX) {
sort_threads = TT_SORT_THREADS_MAX;
diff --git a/test/box-luatest/gh_3389_cfg_option_memtx_sort_threads_test.lua b/test/box-luatest/gh_3389_cfg_option_memtx_sort_threads_test.lua
index 65863d87c..4b9bb8c08 100644
--- a/test/box-luatest/gh_3389_cfg_option_memtx_sort_threads_test.lua
+++ b/test/box-luatest/gh_3389_cfg_option_memtx_sort_threads_test.lua
@@ -46,7 +46,7 @@ g.test_setting_openmp_env_var = function(cg)
t.helpers.retrying({}, function()
local p = "OMP_NUM_THREADS is used to set number" ..
" of sorting threads. Use cfg option" ..
- "'memtx_sort_threads' instead."
+ " 'memtx_sort_threads' instead."
t.assert_not_equals(cg.server:grep_log(p), nil)
end)
cg.server:stop() |
Closes tarantool#3389 Closes tarantool#7689 Closes tarantool#4646 @TarantoolBot document Title: new box.cfg parameter memtx_sort_threads The parameter sets the number of threads used to sort keys of secondary indexes on loading memtx database. The parameter cannot be changed dynamically (as it does not make sense). Maximum value is 256, minimum is 1. Default is to use all available cores. Usage example: ``` box.cfg{memtx_sort_threads=4} ```
e8c81ef
to
e02039e
Compare
In short:
memtx_sort_threads
Closes #3389
Closes #7689
Closes #4646