@@ -21,6 +21,7 @@ static constexpr bool FULL_RESET = true;
2121inline timespec time_now ();
2222inline long nanodiff (timespec start_time, timespec end_time);
2323static long micro_benchmark (std::function<void ()>);
24+ static void benchmark_alternate_tenant_vmcalls (tinykvm::Machine &, size_t );
2425static void benchmark_alternate_tenant_resets (tinykvm::Machine &, size_t );
2526static void benchmark_multiple_vms (tinykvm::Machine&, size_t , size_t );
2627static void benchmark_multiple_pooled_vms (tinykvm::Machine&, size_t , size_t );
@@ -393,6 +394,9 @@ int main(int argc, char** argv)
393394 printf (" Fast reset: %ldns (%ld micros)\n " , frtime, frtime / 1000 );
394395 printf (" Fast vmcall: %ldns (%ld micros)\n " , frcall, frcall / 1000 );
395396
397+ // Benchmark alternating vmcalls on two different VMs
398+ benchmark_alternate_tenant_vmcalls (master_vm, 500000 );
399+
396400 // This benchmark mixes different VMs on the same thread,
397401 // which is supported, but has a serious penalty on Linux.
398402 benchmark_alternate_tenant_resets (master_vm, 5000 );
@@ -420,6 +424,68 @@ int main(int argc, char** argv)
420424 benchmark_multiple_pooled_vms (master_vm, 64 , 15000 );
421425}
422426
427+ void benchmark_alternate_tenant_vmcalls (tinykvm::Machine& master_vm, const size_t iterations)
428+ {
429+ // Make a full copy of the main VM into other_vm
430+ tinykvm::Machine vm1 { binary,
431+ {
432+ .max_mem = GUEST_MEMORY,
433+ .max_cow_mem = 0
434+ } };
435+ vm1.setup_linux (
436+ {" kvmtest" , " Hello World!\n " },
437+ {" LC_TYPE=C" , " LC_ALL=C" , " USER=root" });
438+ vm1.run ();
439+
440+ tinykvm::Machine vm2 { binary,
441+ {
442+ .max_mem = GUEST_MEMORY,
443+ .max_cow_mem = 0
444+ } };
445+ vm2.setup_linux (
446+ {" kvmtest" , " Hello World!\n " },
447+ {" LC_TYPE=C" , " LC_ALL=C" , " USER=root" });
448+ vm2.run ();
449+
450+ const uint64_t vmcall_address = vm1.address_of (" bench" );
451+ assert (vmcall_address == vm2.address_of (" bench" ));
452+
453+ /* Reset benchmark */
454+ uint64_t frcall1 = 0 ;
455+ uint64_t frcall2 = 0 ;
456+ long max_call1 = 0 ;
457+ long max_call2 = 0 ;
458+
459+ for (unsigned i = 0 ; i < 100 ; i++) {
460+ vm1.timed_vmcall (vmcall_address, 4 .0f );
461+ vm2.timed_vmcall (vmcall_address, 4 .0f );
462+ }
463+
464+ for (unsigned i = 0 ; i < iterations; i++)
465+ {
466+ auto frt0 = time_now ();
467+ asm (" " : : : " memory" );
468+ vm1.timed_vmcall (vmcall_address, 4 .0f );
469+ asm (" " : : : " memory" );
470+ auto frt1 = time_now ();
471+ asm (" " : : : " memory" );
472+ vm2.timed_vmcall (vmcall_address, 4 .0f );
473+ asm (" " : : : " memory" );
474+ auto frt2 = time_now ();
475+ frcall1 += nanodiff (frt0, frt1);
476+ frcall2 += nanodiff (frt1, frt2);
477+ max_call1 = std::max (max_call1, nanodiff (frt0, frt1));
478+ max_call2 = std::max (max_call2, nanodiff (frt1, frt2));
479+ }
480+ frcall1 /= iterations;
481+ frcall2 /= iterations;
482+
483+ printf (" Alternating vmcall VM1: %ldns, max %ldns (%ld micros)\n " ,
484+ frcall1, max_call1, max_call1 / 1000 );
485+ printf (" Alternating vmcall VM2: %ldns, max %ldns (%ld micros)\n " ,
486+ frcall2, max_call2, max_call2 / 1000 );
487+ }
488+
423489void benchmark_alternate_tenant_resets (tinykvm::Machine& master_vm, const size_t RESETS)
424490{
425491 const uint64_t vmcall_address = master_vm.address_of (" bench" );
@@ -475,8 +541,8 @@ void benchmark_alternate_tenant_resets(tinykvm::Machine& master_vm, const size_t
475541 frtime /= RESETS;
476542 frcall /= RESETS;
477543
478- printf (" Alternating reset: %ldns (%ld micros)\n " , frtime, frtime / 1000 );
479- printf (" Alternating vmcall: %ldns (%ld micros)\n " , frcall, frcall / 1000 );
544+ printf (" Alternating reset: reset: %ldns (%ld micros)\n " , frtime, frtime / 1000 );
545+ printf (" Alternating reset: vmcall: %ldns (%ld micros)\n " , frcall, frcall / 1000 );
480546}
481547
482548void benchmark_multiple_vms (tinykvm::Machine& master_vm, size_t NUM, size_t RESETS)
0 commit comments