Qemu was released in 2003 and kvm in 2006 according to Wikipedia.I'm also nostalgic. I've been enjoying qemu/kvm since the 1990s. I cut my teeth on it. Virtualization has always been my favorite thing to do, first on Linux. Then on FreeBSD. I also owe the Nvidia GPU passthru feature to me, working behind the scenes with Corvin.
Qemu was released in 2003 and kvm in 2006 according to Wikipedia.
I miss the goal.
You made it because you can get HW acceleration only through QEMU? Can't you?
There may be security reasons bhyve is not implementing some things that qemu might.
also: qemu tries to support all kinds of hardware and tons of features. bhyve is slick and does it's job for 95% of standard usecases. bhyves codebase is much smaller, and thus bears far fewer risks and technical debt.There may be security reasons bhyve is not implementing some things that qemu might.
bhyve is slick and does it's job for 95% of standard usecases.

sh bench.sh # run all tests
sh bench.sh cpu # run individual test
#!/bin/sh
#
# bench.sh — QEMU+bhyve VMM performance benchmark suite
#
# Reproduces the benchmark table from the FreeBSD QEMU+bhyve/vmm project.
# Run identically on the host (native) and inside the guest VM, then compare.
#
# Usage:
# sh bench.sh # run all benchmarks
# sh bench.sh cpu # run only CPU integer test
# sh bench.sh syscall # run only syscall overhead test
# sh bench.sh fork # run only process creation test
# sh bench.sh mem_dd # run only dd memory bandwidth test
# sh bench.sh mem_sysbench # run only sysbench memory test (requires sysbench)
# sh bench.sh disk # run only disk I/O test
# sh bench.sh all # run all (default)
#
# Requirements:
# - C compiler (cc or gcc)
# - dd (standard)
# - sysbench (optional, for mem_sysbench; install: pkg install sysbench)
# - /tmp with at least 256 MB free (for disk test)
#
# The script compiles small C test programs on the fly into /tmp, runs them,
# and prints results. No installation needed.
#
# Project: QEMU + bhyve/vmm accelerator on FreeBSD
# Author: marietto (with Claude Code)
# Date: 2026-06-09 (original benchmarks), 2026-06-23 (script)
# License: Public domain / CC0
#
set -e
TMPDIR="${TMPDIR:-/tmp}"
BENCH_DIR="${TMPDIR}/vmm-bench-$$"
mkdir -p "$BENCH_DIR"
# Find a C compiler
CC=""
for c in cc clang gcc; do
if command -v "$c" >/dev/null 2>&1; then CC="$c"; break; fi
done
if [ -z "$CC" ]; then
echo "ERROR: no C compiler found (tried cc, clang, gcc)" >&2
exit 1
fi
cleanup() {
rm -rf "$BENCH_DIR"
}
trap cleanup EXIT
# ── Helpers ──────────────────────────────────────────────────────────────────
print_header() {
echo ""
echo "================================================================================"
echo " VMM Benchmark Suite — $(date '+%Y-%m-%d %H:%M:%S')"
echo " Host: $(uname -srm)"
printf " CPU: "
sysctl -n hw.model 2>/dev/null || { cat /proc/cpuinfo 2>/dev/null | grep 'model name' | head -1 | sed 's/.*: //' || echo "unknown"; }
printf " RAM: "
if sysctl -n hw.physmem >/dev/null 2>&1; then
echo "$(( $(sysctl -n hw.physmem) / 1073741824 )) GB"
elif [ -f /proc/meminfo ]; then
awk '/MemTotal/{printf "%d GB\n", $2/1048576}' /proc/meminfo
else
echo "unknown"
fi
echo "================================================================================"
echo ""
}
# ── 1. CPU integer: 100M integer operations ──────────────────────────────────
bench_cpu() {
echo "── CPU integer (100M int ops) ──"
cat > "$BENCH_DIR/cpu_int.c" << 'CEOF'
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void)
{
struct timespec t0, t1;
volatile int x = 0;
int i;
clock_gettime(CLOCK_MONOTONIC, &t0);
for (i = 0; i < 100000000; i++)
x = x + i * 3 - i / 2 + 1;
clock_gettime(CLOCK_MONOTONIC, &t1);
long ms = (t1.tv_sec - t0.tv_sec) * 1000L
+ (t1.tv_nsec - t0.tv_nsec) / 1000000L;
/* machine-readable on stderr, human-readable on stdout */
fprintf(stderr, "%ld\n", ms);
printf(" 100M int ops: %ld ms\n", ms);
return 0;
}
CEOF
$CC -O2 -o "$BENCH_DIR/cpu_int" "$BENCH_DIR/cpu_int.c"
# warm up
"$BENCH_DIR/cpu_int" > /dev/null 2>/dev/null
# actual run (best of 3)
best=999999
for run in 1 2 3; do
ms=$("$BENCH_DIR/cpu_int" 2>&1 >/dev/null)
if [ "$ms" -lt "$best" ]; then best=$ms; fi
done
echo " 100M int ops: ${best} ms (best of 3 runs)"
echo ""
}
# ── 2. Syscall overhead: getpid() x 1M ──────────────────────────────────────
bench_syscall() {
echo "── Syscall overhead (getpid() x 1M) ──"
cat > "$BENCH_DIR/syscall_bench.c" << 'CEOF'
#include <stdio.h>
#include <time.h>
#include <unistd.h>
int main(void)
{
struct timespec t0, t1;
int i;
volatile pid_t p;
clock_gettime(CLOCK_MONOTONIC, &t0);
for (i = 0; i < 1000000; i++)
p = getpid();
clock_gettime(CLOCK_MONOTONIC, &t1);
long ns = (t1.tv_sec - t0.tv_sec) * 1000000000L
+ (t1.tv_nsec - t0.tv_nsec);
long ns_per = ns / 1000000;
fprintf(stderr, "%ld\n", ns_per);
printf(" getpid() x 1M: %ld ns/call\n", ns_per);
(void)p;
return 0;
}
CEOF
$CC -O2 -o "$BENCH_DIR/syscall_bench" "$BENCH_DIR/syscall_bench.c"
"$BENCH_DIR/syscall_bench" > /dev/null 2>/dev/null
best=999999
for run in 1 2 3; do
val=$("$BENCH_DIR/syscall_bench" 2>&1 >/dev/null)
if [ "$val" -lt "$best" ]; then best=$val; fi
done
echo " getpid() x 1M: ${best} ns/call (best of 3)"
echo ""
}
# ── 3. Process creation: fork()+wait x 200 ──────────────────────────────────
bench_fork() {
echo "── Process creation (fork()+wait x 200) ──"
cat > "$BENCH_DIR/fork_bench.c" << 'CEOF'
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
struct timespec t0, t1;
int i, status;
clock_gettime(CLOCK_MONOTONIC, &t0);
for (i = 0; i < 200; i++) {
pid_t p = fork();
if (p < 0) { perror("fork"); exit(1); }
if (p == 0) _exit(0);
waitpid(p, &status, 0);
}
clock_gettime(CLOCK_MONOTONIC, &t1);
long us = (t1.tv_sec - t0.tv_sec) * 1000000L
+ (t1.tv_nsec - t0.tv_nsec) / 1000L;
fprintf(stderr, "%ld\n", us);
printf(" fork()+wait x 200: %ld us (%ld us/iter)\n", us, us / 200);
return 0;
}
CEOF
$CC -O2 -o "$BENCH_DIR/fork_bench" "$BENCH_DIR/fork_bench.c"
"$BENCH_DIR/fork_bench" > /dev/null 2>/dev/null
best=999999999
for run in 1 2 3; do
val=$("$BENCH_DIR/fork_bench" 2>&1 >/dev/null)
if [ "$val" -lt "$best" ]; then best=$val; fi
done
echo " fork()+wait x 200: ${best} us (best of 3)"
echo ""
}
# ── 4. Memory bandwidth: dd /dev/zero -> /dev/null ───────────────────────────
bench_mem_dd() {
echo "── Memory bandwidth (dd /dev/zero -> /dev/null, 256 MB) ──"
# warm up
dd if=/dev/zero of=/dev/null bs=1m count=256 2>/dev/null
for run in 1 2 3; do
# dd reports speed on stderr
bw=$(dd if=/dev/zero of=/dev/null bs=1m count=256 2>&1 | tail -1)
echo " run $run: $bw"
done
echo ""
}
# ── 5. Memory bandwidth: sysbench ────────────────────────────────────────────
bench_mem_sysbench() {
echo "── Memory bandwidth (sysbench, block 1M, 20 GB total) ──"
if ! command -v sysbench >/dev/null 2>&1; then
echo " SKIPPED: sysbench not installed (pkg install sysbench)"
echo ""
return
fi
for op in read write; do
for threads in 1 8; do
result=$(sysbench memory \
--memory-block-size=1M \
--memory-total-size=20G \
--memory-oper=$op \
--threads=$threads \
run 2>/dev/null | grep 'transferred' | grep -oE '[0-9]+\.[0-9]+' | tail -1)
printf " %-6s %d thread(s): %s MiB/s\n" "$op" "$threads" "${result:-N/A}"
done
done
echo ""
}
# ── 6. Disk I/O: sequential write+fsync and cached read ─────────────────────
bench_disk() {
echo "── Disk I/O (128 MB, /tmp) ──"
TF="$TMPDIR/vmm-bench-disktest-$$"
# Sequential write + fsync
cat > "$BENCH_DIR/disk_write.c" << 'CEOF'
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
const char *path = (argc > 1) ? argv[1] : "/tmp/vmm-bench-diskfile";
int fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (fd < 0) { perror("open"); return 1; }
/* 128 MB in 1 MB chunks */
char *buf = malloc(1048576);
memset(buf, 0x55, 1048576);
struct timespec t0, t1;
clock_gettime(CLOCK_MONOTONIC, &t0);
for (int i = 0; i < 128; i++) {
if (write(fd, buf, 1048576) != 1048576) { perror("write"); return 1; }
fsync(fd);
}
clock_gettime(CLOCK_MONOTONIC, &t1);
close(fd);
free(buf);
long us = (t1.tv_sec - t0.tv_sec) * 1000000L
+ (t1.tv_nsec - t0.tv_nsec) / 1000L;
double mb_s = 128.0 * 1000000.0 / (double)us;
printf(" Seq write+fsync (128 MB): %.1f MB/s (%ld ms)\n", mb_s, us / 1000);
return 0;
}
CEOF
$CC -O2 -o "$BENCH_DIR/disk_write" "$BENCH_DIR/disk_write.c"
"$BENCH_DIR/disk_write" "$TF"
# Sequential read (cached) — file is now hot in page cache
cat > "$BENCH_DIR/disk_read.c" << 'CEOF'
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
const char *path = (argc > 1) ? argv[1] : "/tmp/vmm-bench-diskfile";
int fd = open(path, O_RDONLY);
if (fd < 0) { perror("open"); return 1; }
char *buf = malloc(1048576);
struct timespec t0, t1;
ssize_t n;
/* warm: read once to fill page cache */
while ((n = read(fd, buf, 1048576)) > 0) {}
lseek(fd, 0, SEEK_SET);
clock_gettime(CLOCK_MONOTONIC, &t0);
while ((n = read(fd, buf, 1048576)) > 0) {}
clock_gettime(CLOCK_MONOTONIC, &t1);
close(fd);
free(buf);
long us = (t1.tv_sec - t0.tv_sec) * 1000000L
+ (t1.tv_nsec - t0.tv_nsec) / 1000L;
double gb_s = 128.0 / 1024.0 * 1000000.0 / (double)us;
printf(" Seq read cached (128 MB): %.1f GB/s (%ld ms)\n", gb_s, us / 1000);
return 0;
}
CEOF
$CC -O2 -o "$BENCH_DIR/disk_read" "$BENCH_DIR/disk_read.c"
"$BENCH_DIR/disk_read" "$TF"
rm -f "$TF"
echo ""
}
# ── Main ─────────────────────────────────────────────────────────────────────
run_all=0
run_cpu=0
run_syscall=0
run_fork=0
run_mem_dd=0
run_mem_sysbench=0
run_disk=0
if [ $# -eq 0 ] || [ "$1" = "all" ]; then
run_all=1
else
for arg in "$@"; do
case "$arg" in
cpu) run_cpu=1 ;;
syscall) run_syscall=1 ;;
fork) run_fork=1 ;;
mem_dd) run_mem_dd=1 ;;
mem_sysbench) run_mem_sysbench=1 ;;
disk) run_disk=1 ;;
all) run_all=1 ;;
*) echo "Unknown benchmark: $arg"; exit 1 ;;
esac
done
fi
print_header
if [ $run_all -eq 1 ] || [ $run_cpu -eq 1 ]; then bench_cpu; fi
if [ $run_all -eq 1 ] || [ $run_syscall -eq 1 ]; then bench_syscall; fi
if [ $run_all -eq 1 ] || [ $run_fork -eq 1 ]; then bench_fork; fi
if [ $run_all -eq 1 ] || [ $run_mem_dd -eq 1 ]; then bench_mem_dd; fi
if [ $run_all -eq 1 ] || [ $run_mem_sysbench -eq 1 ]; then bench_mem_sysbench; fi
if [ $run_all -eq 1 ] || [ $run_disk -eq 1 ]; then bench_disk; fi
echo "================================================================================"
echo " Done. Run this script on both host and guest, then compare results."
echo "================================================================================"
- ef8c9f048a — FreeBSD 15.0 guest support (boots to login)
- e21f571e56 — fix keyboard during the loader menu and FreeBSD login
- 5d1963528c — fix CR4.FSGSBASE per fork() (sshd, pkg are working)
- ba623258c0 — runtime fixes for FreeBSD 15.0 and Debian
- ee4e6b12b6 — SMP8 support
- d963e50e9a + bc208686df — GPU passthrough NVIDIA
- fbfa8be673 — LAPIC IRR scrubbing for MSI passthrough