@samitouri / QOSamiQemu / commits / 75d782a1e9

migration/multifd: cache migrate_multifd_channels() in send/recv hot paths

multifd_send() and multifd_recv() are on the per-page-batch hot path of live migration. Both functions call migrate_multifd_channels() multiple times (3-4 calls each) for modulo arithmetic in the round-robin channel selection loop. Each call goes through migrate_get_current() -> dereference MigrationState -> read parameters.multifd_channels. While each individual call is cheap, these functions execute for every page batch during the entire migration, easily millions of times. Cache the return value in a local variable at function entry. The channel count is fixed for the duration of a migration and cannot change mid-flight. For multifd_send(): 3 calls reduced to 1. For multifd_recv(): 4 calls reduced to 1. Signed-off-by: Bin Guo <guobin@linux.alibaba.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20260518110112.21395-8-guobin@linux.alibaba.com Signed-off-by: Peter Xu <peterx@redhat.com>

Bin Guo committed May 18, 2026 at 19:01 UTC 75d782a1e9f376a4dc1812f67101e9474cc133df
1 file changed +9 -6
migration/multifd.c
+9 -6
@@ -362,13 +362,15 @@ bool multifd_send(MultiFDSendData **send_data)
362 /* We wait here, until at least one channel is ready */
363 qemu_sem_wait(&multifd_send_state->channels_ready);
364
365 + int thread_count = migrate_multifd_channels();
366 +
367 /*
368 * next_channel can remain from a previous migration that was
369 * using more channels, so ensure it doesn't overflow if the
370 * limit is lower now.
371 */
370 - next_channel %= migrate_multifd_channels();
371 - for (i = next_channel;; i = (i + 1) % migrate_multifd_channels()) {
372 + next_channel %= thread_count;
373 + for (i = next_channel;; i = (i + 1) % thread_count) {
374 if (multifd_send_should_exit()) {
375 return false;
376 }
@@ -378,7 +380,7 @@ bool multifd_send(MultiFDSendData **send_data)
380 * sender thread can clear it.
381 */
382 if (qatomic_read(&p->pending_job) == false) {
381 - next_channel = (i + 1) % migrate_multifd_channels();
383 + next_channel = (i + 1) % thread_count;
384 break;
385 }
386 }
@@ -998,6 +1000,7 @@ bool multifd_recv(void)
1000 int i;
1001 static int next_recv_channel;
1002 MultiFDRecvParams *p = NULL;
1003 + int thread_count = migrate_multifd_channels();
1004 MultiFDRecvData *data = multifd_recv_state->data;
1005
1006 /*
@@ -1005,8 +1008,8 @@ bool multifd_recv(void)
1008 * using more channels, so ensure it doesn't overflow if the
1009 * limit is lower now.
1010 */
1008 - next_recv_channel %= migrate_multifd_channels();
1009 - for (i = next_recv_channel;; i = (i + 1) % migrate_multifd_channels()) {
1011 + next_recv_channel %= thread_count;
1012 + for (i = next_recv_channel;; i = (i + 1) % thread_count) {
1013 if (multifd_recv_should_exit()) {
1014 return false;
1015 }
@@ -1014,7 +1017,7 @@ bool multifd_recv(void)
1017 p = &multifd_recv_state->params[i];
1018
1019 if (qatomic_read(&p->pending_job) == false) {
1017 - next_recv_channel = (i + 1) % migrate_multifd_channels();
1020 + next_recv_channel = (i + 1) % thread_count;
1021 break;
1022 }
1023 }