Skip to content
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

Optimize IO thread offload for modified argv #1360

Open
wants to merge 1 commit into
base: unstable
Choose a base branch
from

Conversation

uriyage
Copy link
Contributor

@uriyage uriyage commented Nov 26, 2024

Improve expired commands performance with IO threads

Background

In our IO threads architecture, IO threads allocate client argv's and later when we free it after processCommand we offload its free to the IO threads.
With jemalloc, it's crucial that the same thread that allocates memory also frees it.

For some commands we modify the client's argv in the main thread during command processing (for example in SET EX command we rewrite the command to use absolute time for replication propagation).

Current issues

  1. When commands are rewritten (e.g., expire commands), we store the original argv
    in c->original_argv. However, we're currently:
    • Freeing new argv (allocated by main thread) in IO threads
    • Freeing original argv (allocated by IO threads) in main thread
  2. Currently, c->original_argv points to new array with old
    objects, while c->argv has old array with new objects, making memory free management complicated.

Changes

  1. Refactored argv modification handling code to ensure consistency - both array and objects are now either all new or all old
  2. Moved original_argv cleanup to happen in resetClient after argv cleanup
  3. Modified IO threads code to properly handle original argv cleanup when argv are modified.

Performance Impact

Benchmark with SET EX commands (650 clients, 512 byte value, 8 IO threads):

  • New implementation: 729,548 ops/sec
  • Old implementation: 633,243 ops/sec
    Representing a ~15% performance improvement due to more efficient memory handling.

Signed-off-by: Uri Yagelnik <uriy@amazon.com>
@ranshid ranshid self-requested a review November 27, 2024 14:59
Copy link
Contributor

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

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

Very nice! Just some points I'm not sure if we can improve, but I'm not sure we have to change anything. Maybe not. Let's think about and discuss first.

* new_argv - Optional pointer to a new argument vector. If NULL, space will be
* allocated for new_argc arguments, preserving the existing arguments.
*/
static void backupAndUpdateClientArgv(client *c, int new_argc, robj **new_argv) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just noting that this function became pretty complex now. It would be useful to have unit test for it, but I will not require it. I hope we have some coverage for the edge cases, like a test that does replication of RESTORE with TTL and without ABSTTL. (It does redactClientCommandArgument twice and adds ABSTTL to the end.)

* Returns C_OK if the client's argv were successfully offloaded to an IO thread,
* C_ERR otherwise. */
int tryOffloadFreeArgvToIOThreads(client *c) {
int tryOffloadFreeArgvToIOThreads(client *c, int free_original_argv) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Here, it is clear what the arguments mean, but at the call site, it is not immediately clear to the reader what the 2nd argument is, when you see the call like tryOffloadFreeArgvToIOThreads(c, 0).

Trying to think of ideas:

  • We could use some enum {FREE_ARGV, FREE_ORIGINAL_ARGV} instead of 0 and 1.
  • We could pass pointers to the argv and argc fields within the client struct instead of the current parameters, so tryOffloadFreeArgvToIOThreads(robj ***argv_ptr, int *argc_ptr) and then at the call site it would look like tryOffloadFreeArgvToIOThreads(&c->original_argv, &c->original_argc). Is this good?

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.

2 participants