Skip to content

Commit 7d3f797

Browse files
committed
Improve dispatch queue usage
The way we used dispatch queue was not optimal, leading to creation of too many threads, which can cause unneeded context switching, and lower performance. We use now 2 queues: - vms_queue - concurrent queue use to run accepted connections threads. For this queue we must use concurrent queue since we want to process requests from VMs in parallel. - host_queue - serial queue for processing events from vmnet. This queue is used for the start and stop callback and for the events callback. With this change we use consistent number of threads: main thread, 1 vmnet thread, 1 thread per connected vm. Testing show no change in performance. | hardware | os | test | before (Gb/s) | after (Gb/s) | |----------|--------|---------------|---------------|--------------| | M1 Pro | 14.7.1 | host-to-vm | 3.252 | 3.219 | | M1 Pro | 14.7.1 | host-to-vm-2 | 1.902 | 1.880 | | M1 Pro | 14.7.1 | vm-to-vm | 1.493 | 1.507 | | M2 Max | 15.1 | host-to-vm | 2.335 | 2.359 | | M2 Max | 15.1 | host-to-vm-2 | 1.798 | 1.802 | | M2 Max | 15.1 | vm-to-vm | 1.275 | 1.266 | Results are average send throughput of 10 runs. Using performance tests from #66. We have a performance regression on macOS 15.1. With 14.7 on same M2 Max machine it was up ot 1.2 times faster. With 15.1 it is up to 1.4 times slower. Signed-off-by: Nir Soffer <[email protected]>
1 parent f486d47 commit 7d3f797

File tree

1 file changed

+19
-18
lines changed

1 file changed

+19
-18
lines changed

main.c

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ struct conn {
8585

8686
struct state {
8787
dispatch_semaphore_t sem;
88+
dispatch_queue_t vms_queue;
89+
dispatch_queue_t host_queue;
8890
struct conn *conns; // TODO: avoid O(N) lookup
8991
} _state;
9092

@@ -252,16 +254,14 @@ static interface_ref start(struct state *state, struct cli_options *cliopt) {
252254
cliopt->vmnet_nat66_prefix);
253255
}
254256

255-
dispatch_queue_t q = dispatch_queue_create(
256-
"io.github.lima-vm.socket_vmnet.start", DISPATCH_QUEUE_SERIAL);
257257
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
258258

259259
__block interface_ref iface;
260260
__block vmnet_return_t status;
261261

262262
__block uint64_t max_bytes = 0;
263263
iface = vmnet_start_interface(
264-
dict, q, ^(vmnet_return_t x_status, xpc_object_t x_param) {
264+
dict, state->host_queue, ^(vmnet_return_t x_status, xpc_object_t x_param) {
265265
status = x_status;
266266
if (x_status == VMNET_SUCCESS) {
267267
print_vmnet_start_param(x_param);
@@ -271,17 +271,14 @@ static interface_ref start(struct state *state, struct cli_options *cliopt) {
271271
dispatch_semaphore_signal(sem);
272272
});
273273
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
274-
dispatch_release(q);
275274
xpc_release(dict);
276275
if (status != VMNET_SUCCESS) {
277276
ERRORF("vmnet_start_interface: [%d] %s", status, vmnet_strerror(status));
278277
return NULL;
279278
}
280279

281-
dispatch_queue_t event_q = dispatch_queue_create(
282-
"io.github.lima-vm.socket_vmnet.events", DISPATCH_QUEUE_CONCURRENT);
283280
vmnet_interface_set_event_callback(
284-
iface, VMNET_INTERFACE_PACKETS_AVAILABLE, event_q,
281+
iface, VMNET_INTERFACE_PACKETS_AVAILABLE, state->host_queue,
285282
^(interface_event_t __attribute__((unused)) x_event_id,
286283
xpc_object_t x_event) {
287284
uint64_t estim_count = xpc_dictionary_get_uint64(
@@ -298,21 +295,17 @@ static void signalhandler(int signal) {
298295
siglongjmp(jmpbuf, 1);
299296
}
300297

301-
static void stop(interface_ref iface) {
298+
static void stop(struct state *state, interface_ref iface) {
302299
if (iface == NULL) {
303300
return;
304301
}
305-
dispatch_queue_t q = dispatch_queue_create(
306-
"io.github.lima-vm.socket_vmnet.stop", DISPATCH_QUEUE_SERIAL);
307302
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
308303
__block vmnet_return_t status;
309-
vmnet_stop_interface(iface, q, ^(vmnet_return_t x_status) {
304+
vmnet_stop_interface(iface, state->host_queue, ^(vmnet_return_t x_status) {
310305
status = x_status;
311306
dispatch_semaphore_signal(sem);
312307
});
313308
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
314-
dispatch_release(q);
315-
// TODO: release event_q ?
316309
if (status != VMNET_SUCCESS) {
317310
ERRORF("vmnet_stop_interface: [%d] %s", status, vmnet_strerror(status));
318311
}
@@ -376,8 +369,6 @@ int main(int argc, char *argv[]) {
376369
debug = getenv("DEBUG") != NULL;
377370
int rc = 1, listen_fd = -1;
378371
__block interface_ref iface = NULL;
379-
dispatch_queue_t q = dispatch_queue_create(
380-
"io.github.lima-vm.socket_vmnet.accept", DISPATCH_QUEUE_CONCURRENT);
381372

382373
struct cli_options *cliopt = cli_options_parse(argc, argv);
383374
assert(cliopt != NULL);
@@ -420,6 +411,15 @@ int main(int argc, char *argv[]) {
420411
struct state state;
421412
memset(&state, 0, sizeof(state));
422413
state.sem = dispatch_semaphore_create(1);
414+
415+
// Queue for vm connections, allowing processing vms requests in parallel.
416+
state.vms_queue = dispatch_queue_create(
417+
"io.github.lima-vm.socket_vmnet.vms", DISPATCH_QUEUE_CONCURRENT);
418+
419+
// Queue for processing vmnet events.
420+
state.host_queue = dispatch_queue_create(
421+
"io.github.lima-vm.socket_vmnet.host", DISPATCH_QUEUE_SERIAL);
422+
423423
iface = start(&state, cliopt);
424424
if (iface == NULL) {
425425
// Error already logged.
@@ -442,16 +442,15 @@ int main(int argc, char *argv[]) {
442442
goto done;
443443
}
444444
struct state *state_p = &state;
445-
dispatch_async(q, ^{
445+
dispatch_async(state.vms_queue, ^{
446446
on_accept(state_p, accept_fd, iface);
447447
});
448448
}
449449
rc = 0;
450450
done:
451451
DEBUGF("shutting down with rc=%d", rc);
452-
dispatch_release(q);
453452
if (iface != NULL) {
454-
stop(iface);
453+
stop(&state, iface);
455454
}
456455
if (listen >= 0) {
457456
close(listen_fd);
@@ -460,6 +459,8 @@ int main(int argc, char *argv[]) {
460459
unlink(cliopt->pidfile);
461460
close(pid_fd);
462461
}
462+
dispatch_release(state.vms_queue);
463+
dispatch_release(state.host_queue);
463464
cli_options_destroy(cliopt);
464465
return rc;
465466
}

0 commit comments

Comments
 (0)