setup qemu virtiofs based caching and enable it for maven builds#2304
setup qemu virtiofs based caching and enable it for maven builds#2304toabctl merged 3 commits intochainguard-dev:mainfrom
Conversation
|
Doing some performance analysis:
At least on small-ish packages, virtiofs performance impact is actually detrimental Can you point out to some bigger packages to test the performance impact? |
|
Tested with this package and indeed it improves by A LOT |
|
i asked claude if he could help me make performance better on this. he said: Virtiofsd Performance Optimization SummaryWhat Was DoneApplied OptimizationsModified the virtiofsd configuration in args := []string{
"--socket-path=" + cfg.VirtiofsdSocketPath,
fmt.Sprintf("--thread-pool-size=%d", runtime.NumCPU()*2), // NEW: Parallel I/O
"-o", "source=" + cfg.CacheDir,
"-o", "cache=always", // CHANGED: from "auto" to "always"
"-o", "sandbox=namespace",
"-o", "xattr",
"-o", "no_posix_lock", // NEW: Reduce locking overhead
"-o", "writeback",
}Three optimizations applied:
Benchmark ResultsTested with
Key Findings
RecommendationKeep these optimizations because:
Other Optimization Options to Explore1. DAX (Direct Access) ModeWhat it does: Memory-maps guest file access directly to host memory, eliminating copy operations. Implementation: // In virtiofsd args
"-o", "announce_submounts",
// In QEMU device configuration (qemu_runner.go around line 814)
"-device", fmt.Sprintf("vhost-user-fs-pci,queue-size=1024,chardev=char_cache,tag=melange_cache,cache-size=%dM", cfg.Memory/2),Expected impact: 5-15% improvement for I/O-heavy workloads 2. Larger Queue SizeWhat it does: Increases concurrent I/O request handling capacity. Implementation: "-device", "vhost-user-fs-pci,queue-size=2048,chardev=char_cache,tag=melange_cache"
// Or even: queue-size=4096Expected impact: 2-5% improvement for highly parallel builds 3. File Descriptor LimitsWhat it does: Increases max open files for virtiofsd process. Implementation: "-o", "rlimit-nofile=1048576",Expected impact: Prevents bottlenecks with many simultaneous file operations 4. Adjust Guest Mount OptionsWhat it does: Reduces unnecessary permission checks on guest side. Implementation (in guest mount command around line 1000): mount -t virtiofs -o default_permissions melange_cache /mount/var/cache/melangeExpected impact: 1-3% improvement from reduced permission overhead 5. Multiple Cache Directories with Separate virtiofsd InstancesWhat it does: Split cache into multiple mount points (e.g., go modules, apk cache) with dedicated virtiofsd instances. Implementation:
Expected impact: 10-20% improvement for builds with diverse cache patterns 6. Tune Writeback Cache BehaviorWhat it does: Adjust how aggressively writes are cached. Implementation: "-o", "writeback",
"-o", "flock", // Add if POSIX file locking is actually needed
"-o", "posix_acl", // Add if ACLs are usedExpected impact: Depends on workload write patterns 7. Different Sandbox ModeWhat it does: Use Implementation: "-o", "sandbox=chroot", // Instead of "namespace"Expected impact: 1-2% improvement from reduced namespace overhead 8. Disable Extended Attributes (xattr)What it does: Skip xattr support if not needed by melange builds. Implementation: // Remove: "-o", "xattr",Expected impact: 1-2% improvement from skipping xattr operations 9. Alternative: io_uring BackendWhat it does: Use newer Linux async I/O interface for better performance. Implementation: Expected impact: 10-20% improvement with modern kernels 10. Cache PrewarmingWhat it does: Pre-populate cache with common dependencies before build starts. Implementation:
Expected impact: Dramatic improvement for repeated builds (30-50%) Benchmarking Methodology for Future TestsWhen testing additional optimizations:
Testing Commands# Clean cache test
mv /local-tmp/melange-cache /local-tmp/melange-cache.backup && \
mkdir -p /local-tmp/melange-cache && \
time ( export PATH="$PWD:$PATH" && export QEMU_USE_VIRTIOFS=1 && \
cd ~/src/stereo/os && make package/yq )
# Warm cache test
time ( export PATH="$PWD:$PATH" && export QEMU_USE_VIRTIOFS=1 && \
cd ~/src/stereo/os && make package/yq )Profiling RecommendationsTo identify the next best optimization target:
Questions to Answer Before Further Optimization
Use these insights to prioritize which of the above optimizations to try next. |
yes. And I think if we add more caching (eg. pytthon's uv, npm, ...) local build iterations will be faster for many cases. |
|
Tested @smoser 's changes, not all of them are possible but these work: diff --git a/pkg/container/qemu_runner.go b/pkg/container/qemu_runner.go
index 2f8d5081..fea58b74 100644
--- a/pkg/container/qemu_runner.go
+++ b/pkg/container/qemu_runner.go
@@ -804,7 +804,8 @@ func createMicroVM(ctx context.Context, cfg *Config) error {
if cfg.VirtiofsEnabled {
log.Info("qemu: using virtiofs for cache directory (read-write)")
// Chardev for socket communication
- baseargs = append(baseargs, "-chardev",
+ baseargs = append(baseargs,
+ "-chardev",
fmt.Sprintf("socket,id=char_cache,path=%s", cfg.VirtiofsdSocketPath))
// vhost-user-fs-pci device
baseargs = append(baseargs, "-device",
@@ -1802,11 +1803,13 @@ func startVirtiofsd(ctx context.Context, cfg *Config) (*exec.Cmd, error) {
args := []string{
"--socket-path=" + cfg.VirtiofsdSocketPath,
+ fmt.Sprintf("--thread-pool-size=%d", runtime.NumCPU()*2), // NEW: Parallel I/O
"-o", "source=" + cfg.CacheDir,
- "-o", "cache=auto", // Balance coherency and performance
+ "-o", "cache=always", // Balance coherency and performance
"-o", "sandbox=namespace", // Use namespace sandbox (works without root)
"-o", "xattr", // Enable xattr support
"-o", "writeback", // Enable writeback caching for better write performance
+ "-o", "no_posix_lock",
}
log.Debugf("starting virtiofsd: %s %v", virtiofsdPath, args)Got a small bump in performance, but still for small packages (in the <1m range) is not worth @toabctl can you suggest some medium size (5-10m build time) packages that we could test? |
not really. my usecase was |
|
related to this one: #2305 |
Add optional virtiofs support for the cache directory mount, enabled via QEMU_USE_VIRTIOFS=1 environment variable. When enabled and virtiofsd is available, this provides a read-write cache mount. This is useful when locally iterating over package builds. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Configure Maven local repository to use /var/cache/melange/m2repository, which is persisted when a cache-dir is provided. This allows Maven builds to reuse downloaded dependencies across builds which is very useful because some java projects have a lot of deps to download (eg. apicurio-registry needs more than 1 GB deps). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a "Cache Persistence by Runner" section explaining that cache write behavior differs by runner: Docker and Bubblewrap use read-write bind mounts (writes persist), while QEMU uses read-only 9p with overlay by default (writes discarded). Update virtiofs section to clarify it enables cache persistence for QEMU. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Caching would speed-up builds when iterating locally over a package significantly. Caching is not yet there for the different ecosystems but maven is enabled with this PR and shows very good results.
If the taken approach here is welcomed, I'm happy to implement more caching for the different ecosystems.
I think (but not sure, I don't have enough experience with our build system yet) this doesn't affect the builds done in CI and for production because we 1) don't set the env variable and 2) use ephemeral machines so there's no cache anyway. Are those assumptions correct?
Functional Changes
Notes:
I've not done this (yet). I'm happy to do this if there's agreement that this PR is a good approach and would be in general welcomed to be merged.
SCA Changes
Notes:
I did build
apicurio-registry(a Java+maven project with 1.1 GB deps) . the build took: