Skip to content

Mechanik20051988/gh 5645 several iproto threads new#5877

Merged
Korablev77 merged 3 commits intomasterfrom
mechanik20051988/gh-5645-several-iproto-threads-new
Apr 13, 2021
Merged

Mechanik20051988/gh 5645 several iproto threads new#5877
Korablev77 merged 3 commits intomasterfrom
mechanik20051988/gh-5645-several-iproto-threads-new

Conversation

@EvgenyMekhanik
Copy link
Contributor

@EvgenyMekhanik EvgenyMekhanik commented Mar 11, 2021

iproto: implement ability to run multiple iproto threads

There are users that have specific workloads where iproto thread
is the bottleneck of throughput: iproto thread's code is 100% loaded
while TX thread's core is not. For such cases it would be nice to have
a capability to create several iproto threads.

Closes #5645

@EvgenyMekhanik EvgenyMekhanik self-assigned this Mar 11, 2021
@EvgenyMekhanik EvgenyMekhanik force-pushed the mechanik20051988/gh-5645-several-iproto-threads-new branch 6 times, most recently from e77b431 to 5003a9b Compare March 12, 2021 12:01
Copy link
Contributor

@alyapunov alyapunov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A left several comments. I think there are ways to improve this patchset.

src/rmean.c Outdated
int64_t mean = 0;
for (size_t j = 1; j <= RMEAN_WINDOW; j++)
mean += rmean->stats[name].value[j];
mean += __atomic_load_n(&rmean->stats[name].value[j], __ATOMIC_ACQUIRE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we may assume that values 1..RMEAN_WINDOW are always accessed from main thread and thus should not be synchronized.

src/rmean.c Outdated

rmean->stats[name].value[0] += value;
rmean->stats[name].total += value;
__atomic_add_fetch(&rmean->stats[name].value[0], value, __ATOMIC_RELEASE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not __ATOMIC_RELAXED? I don't see a reason of using __ATOMIC_RELEASE here.

src/rmean.c Outdated
for (size_t j = 0; j < RMEAN_WINDOW + 1; j++)
rmean->stats[i].value[j] = 0;
rmean->stats[i].total = 0;
__atomic_store_n(&rmean->stats[i].value[j], 0, __ATOMIC_RELEASE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that only value[0] should have a fence, since only it can be modified from other thread.

src/rmean.c Outdated
rmean_roll(int64_t *value, double dt)
{
value[0] /= dt;
int64_t tmp = __atomic_load_n(&value[0], __ATOMIC_ACQUIRE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That function was not a good piece of code, and you make it worse.
What's a point of several atomic accesses of value[0] in this function? If you are expecting that somebody else is changing it - what would be a correct way to handle it?
I don't like thoughtless changing of access to everything suspicious to store+RELEASE and load+ACQUIRE. A code should do everything it must and not more.

src/rmean.h Outdated
rmean_total(struct rmean *rmean, size_t name)
{
return rmean->stats[name].total;
return __atomic_load_n(&rmean->stats[name].total, __ATOMIC_ACQUIRE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, it think __ATOMIC_RELAXED would be enough. Not a big deal btw.

static inline void
iproto_thread_init_routes(struct iproto_thread *iproto_thread)
{
assert(iproto_thread != NULL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't verify each line of this function. I hope it's correct.

return mempool_count(&iproto_msg_pool);
size_t count = 0;
for( auto& iproto_thread : iproto_threads )
count += mempool_count(&iproto_thread->iproto_msg_pool);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about synchronization here?

return mempool_count(&iproto_connection_pool);
size_t count = 0;
for( auto& iproto_thread : iproto_threads )
count += mempool_count(&iproto_thread->iproto_connection_pool);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about synchronization here?

size_t mem = 0;
for( auto& iproto_thread : iproto_threads )
mem += slab_cache_used(&iproto_thread->net_cord.slabc) +
slab_cache_used(&iproto_thread->net_slabc);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about synchronization here?

if (uri != NULL) {
if (evio_service_bind(&binary, uri) != 0)
diag_raise();
for( auto& iproto_thread : iproto_threads ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this solutions. Actually I don't know how to do it good, but I didn't think a lot. I see the following problems to be fixed:

  1. You set here iproto_thread's members and it's not obvious why it is safe. Please don't explain me, I understand, but you should make the code understandable by itself.
  2. This new meesage IPROTO_CFG_STOP is not documented fine. It seems that doesn't require the service to be started. At the same time IPROTO_CFG_LISTEN requires (!) service to be stopped. That's very confusing.
  3. You do a lot work right here, while doing a lot if work in IPROTO_CFG_LISTEN handler. That's not obvious either.

@EvgenyMekhanik EvgenyMekhanik force-pushed the mechanik20051988/gh-5645-several-iproto-threads-new branch 2 times, most recently from 5edb2a0 to 34b285c Compare March 26, 2021 16:16
@EvgenyMekhanik EvgenyMekhanik force-pushed the mechanik20051988/gh-5645-several-iproto-threads-new branch 13 times, most recently from d0fa199 to eb657a2 Compare March 31, 2021 08:52
}

void
iproto_listen(const char *uri)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two funcs with the same name - I'd rather rename one of them

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix

test_run:cmd('start server test with args="8"')
server_addr = test_run:cmd("eval test 'return box.cfg.listen'")[1]
fibers_count = fibers_count * 2
assert(test_run:grep_log("test", "stopping input on connection") == nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain what is the purpose of the test and what are you checking?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why we are making this patch is that: there are users that have specific workloads where iproto thread
is the bottleneck of throughput: iproto thread's code is 100% loaded while TX thread's core is not. I don't know how to check full iproto thread loading in test, except for indirect signs, one of which is the message in the log "stopping connection on input". So in test first of all i start instance with one iproto thread, and send requests in a large number of fibers at the same time to this instance. I increase the number of fibers by 4 times until I get "stopping connection on input" in log. Then i restart instance with 8 itproto threads increase the number of fibers by 4 times again and check that there is no such message. Based on this, I conclude that 8 threads can withstand at least 4 times the more load

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have any ideas on how to test this better, that would be great!

@EvgenyMekhanik EvgenyMekhanik force-pushed the mechanik20051988/gh-5645-several-iproto-threads-new branch 6 times, most recently from 27b5d3d to 5d87828 Compare April 13, 2021 11:11
evio_service_init(loop(), &iproto_thread->binary, "binary",
iproto_on_accept, iproto_thread);

static const unsigned int endpoint_name_max = ENDPOINT_NAME_MAX;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this var? Why not directly use ENDPOINT_NAME_MAX?

iproto_thread->stopped_connections =
RLIST_HEAD_INITIALIZER(iproto_thread->
stopped_connections);
static const unsigned int endpoint_name_max = 10;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use added enum

cpipe_set_max_input(&net_pipe, iproto_msg_max / 2);
/** Initialize the iproto subsystem and start network io thread */
void
iproto_init(unsigned int threads_count)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cfg_geti returns int, so better make this function take int param. Also I'd add some check verifying rationality of threads_count value. I mean raise warning/error in case user passes too large value. For instance, I doubt that it makes sense to set value more than 2*cores. I don't insist on this check tho.

@EvgenyMekhanik EvgenyMekhanik force-pushed the mechanik20051988/gh-5645-several-iproto-threads-new branch 4 times, most recently from 9bddee4 to 5c90dc2 Compare April 13, 2021 18:34
The fields of the rmean structure can be accessed from
multiple threads, so we must use atomic operations to get/set
fields in this structure. Also in the comments to the functions
i wrote in which threads they should be called to correctly access
the fields of the rmean structure.
There was two problems with struct rmean:
- For correct access for rmean struct fields, this struct should be
  created in tx thread.
- In case when rmean_new return NULL in net_cord_f tarantool hangs
  and does not terminate in any way except on SIGKILL.
Also net_slabc cache was not destroyed. Moved allocation and deallocation
of rmean structure to iproto_init/iproto_free respectively. Added
slab_cache_destroy for net_slabc for graceful resource releases.
@EvgenyMekhanik EvgenyMekhanik force-pushed the mechanik20051988/gh-5645-several-iproto-threads-new branch 5 times, most recently from 31244da to bb1c6e4 Compare April 13, 2021 21:13
@Korablev77 Korablev77 force-pushed the mechanik20051988/gh-5645-several-iproto-threads-new branch from bb1c6e4 to 0081b6f Compare April 13, 2021 22:24
There are users that have specific workloads where iproto thread
is the bottleneck of throughput: iproto thread's code is 100% loaded
while TX thread's core is not. For such cases it would be nice to have
a capability to create several iproto threads.

Closes #5645

@TarantoolBot document
Title: implement ability to run multiple iproto threads
Implement ability to run multiple iproto threads, which is useful
in some specific workloads where iproto thread is the bottleneck
of throughput. To specify count of iproto threads, user should used
iproto_threads option in box.cfg. For example if user want to start
8 iproto threads, he must enter `box.cfg{iproto_threads=8}`. Default
iproto threads count == 1. This option is not dynamic, so user can't
change it after first setting, until server restart. Distribution of
connections per threads is managed by OS kernel.
@Korablev77 Korablev77 force-pushed the mechanik20051988/gh-5645-several-iproto-threads-new branch from 0081b6f to 754f64c Compare April 13, 2021 22:58
Copy link
Contributor

@Korablev77 Korablev77 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Korablev77 Korablev77 merged commit 2ede3be into master Apr 13, 2021
@Korablev77 Korablev77 deleted the mechanik20051988/gh-5645-several-iproto-threads-new branch April 13, 2021 23:15
@Gerold103
Copy link
Collaborator

Well, hello. I suppose the review wasn't really thorough, was it?

  • Why is there just one review for such a big and important feature?

  • Why is such a huge feature is merged in a monolithic commit along with tons of refactoring and new minor formatting issues? A truly enormous time was spent on making iproto.cc code readable and conform to our code style at least to a notable extent and to document everything. iproto_thread struct should have been introduced separately before its count can be configured. Otherwise it is really hard where are the functional changes. Also there is no a single mentioning in the comments how the clients sharing works, and why it works like that (because Mons said we don't have time to make it properly).

  • You didn't think about net_msg_max. After this patch the real number of maximal messages becomes multiple of the number of threads. And that affects the TX thread. How does they work now together?

  • Why the heck the stop and stat commands are a part of iproto_cfg_op now? It is for configuration, not for getting info or terminating the subsystem! You can't blindly piggyback stuff to code which just happened to do something similar. We are going to live with this code. Not to hand it out to a lecturer and forget about it.

  • I just managed to create a tarantool instance with 0 iproto threads. Did you intend that behaviour? At the same time, box.cfg.listen is "successfully" specified. Was the feature considered too trivial to test it properly?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Several iproto threads

4 participants