Skip to content

Enhance image optimization with compression and CPU affinity support#36

Merged
BigfootACA merged 5 commits into
Droid-VM:masterfrom
lateautumn233:disk
Jul 7, 2026
Merged

Enhance image optimization with compression and CPU affinity support#36
BigfootACA merged 5 commits into
Droid-VM:masterfrom
lateautumn233:disk

Conversation

@lateautumn233

Copy link
Copy Markdown
Contributor

This pull request adds comprehensive support for CPU affinity and disk compression handling in disk operations and the VM backend. It introduces a new utility class for host CPU topology and affinity mask generation, enables user-facing selection of disk compression algorithms during format/convert actions, and ensures that disk optimization preserves compression where appropriate. The changes also lay the groundwork for binding VM processes to specific CPU cores.

CPU Affinity and Host Topology Utilities

  • Added CpuUtils class to enumerate host CPU cores, group them by frequency tiers, and generate CPU masks for affinity binding.
  • Updated CrosvmBackendInstance.java to include a TODO for prepending a taskset wrapper to bind VMs to selected host cores, using affinity masks from CpuUtils.
  • Extended ImageCommandGenerate to support setting a CPU affinity mask for disk operations.

Disk Compression Handling

  • Enhanced disk format dialog (DiskSetFormatDialog) to allow users to select a compression algorithm (deflate/zstd/none) when converting or creating qcow2 disks, with UI visibility logic based on format support [1] [2] [3] [4] [5] [6].
  • Modified disk conversion and optimization logic to preserve compression settings and propagate them through intents and command generation [1] [2] [3].

Disk Compression Detection and Preservation

  • Added ImageUtils.hasCompressedClusters to reliably detect if a disk image actually contains compressed clusters, using qemu-img check.
  • In disk optimization and conversion flows, added logic to automatically preserve the source disk's compression algorithm (zlib/zstd) if the user has opted in and the image is compressed, with a settings toggle to control this behavior [1] [2].

Integration and Refactoring

  • Refactored disk operation activity and related flows to pass compression and affinity parameters through to command generation, ensuring consistent application of user and system preferences [1] [2] [3].

These changes collectively improve performance, user control, and reliability for disk and VM operations by leveraging CPU topology and smarter disk compression management.

- Fix qemu-img convert: positional args must follow all options (was breaking -c placement)
- Add ImageUtils.hasCompressedClusters() using qemu-img check's real compressed-clusters count
- Implement applyKeepCompress() to preserve source compression algorithm during optimization
- New "Keep compression when optimizing" setting (disabled by default) with UI controls
- Add i18n strings (English, Simplified Chinese, Traditional Chinese)
- Add compression selector (visible only for QCOW2 format)
- Allow QCOW2→QCOW2 conversion to support re-compression/decompression
- Handle in-place re-compress via temp file path to avoid read lock collision
- New CpuUtils utility to enumerate cores and identify frequency tiers
- Add CPU affinity settings UI with multi-core selection dialog
- Bind disk operations (qemu-img, clone) to selected cores via taskset
- Support automatic big-core filtering on heterogeneous CPU layouts
- Reserve KEY_CROSVM_CPU_AFFINITY for future VM affinity support
Copilot AI review requested due to automatic review settings July 5, 2026 16:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands disk operation capabilities by adding CPU affinity selection (for qemu-img / disk tasks) and improving qcow2 compression handling during convert/optimize flows, including preserving existing compression when requested.

Changes:

  • Added host CPU topology utilities (CpuUtils) and a new Settings UI flow to pick CPU cores for disk operations (persisted as a preference and applied via taskset).
  • Added user-facing disk compression selection in the disk format/convert dialog and propagated the chosen algorithm into command generation.
  • Improved “optimize” behavior to optionally preserve existing qcow2 compression by detecting compressed clusters and reapplying the source algorithm.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
app/src/main/res/values/strings.xml Adds new Settings strings for compression preservation and CPU affinity.
app/src/main/res/values-zh-rTW/strings.xml Traditional Chinese translations for new Settings strings.
app/src/main/res/values-zh-rCN/strings.xml Simplified Chinese translations for new Settings strings.
app/src/main/res/layout/fragment_main_settings.xml Adds UI rows for “keep compression on optimize” and CPU affinity selection.
app/src/main/res/layout/dialog_disk_set_format.xml Adds compression chooser row to disk format dialog layout.
app/src/main/java/cn/classfun/droidvm/ui/main/settings/MainSettingsFragment.java Persists new preferences; implements CPU affinity selection dialog and summary updates.
app/src/main/java/cn/classfun/droidvm/ui/disk/operation/ImageCommandGenerate.java Applies CPU affinity via taskset and refines qemu-img convert argument ordering/compress flags.
app/src/main/java/cn/classfun/droidvm/ui/disk/operation/DiskOperationActivity.java Propagates affinity + keep-compress behavior into task preparation before command generation.
app/src/main/java/cn/classfun/droidvm/ui/disk/action/DiskSetFormatDialog.java Adds compression selection logic and allows same-format “recompress” conversions for qcow2.
app/src/main/java/cn/classfun/droidvm/ui/disk/action/DiskActionDialog.java Ensures optimize tasks opt into compression preservation.
app/src/main/java/cn/classfun/droidvm/lib/utils/ImageUtils.java Adds compressed-cluster detection via qemu-img check --output=json.
app/src/main/java/cn/classfun/droidvm/lib/utils/CpuUtils.java New CPU core enumeration/tiering + CSV-to-mask and display helpers for affinity.
app/src/main/java/cn/classfun/droidvm/daemon/vm/backend/CrosvmBackendInstance.java Adds TODO notes for future VM process affinity binding using taskset.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +141 to +154
@NonNull
private static List<Integer> parseCsv(@NonNull String csv) {
var out = new ArrayList<Integer>();
if (csv.isEmpty()) return out;
for (var part : csv.split(",")) {
part = part.trim();
if (part.isEmpty()) continue;
try {
out.add(Integer.parseInt(part));
} catch (NumberFormatException ignored) {
}
}
return out;
}
Comment on lines +229 to +232
/**
* qemu-img CPU affinity as a taskset -c core list (e.g. "4,5,6,7").
* Empty string means no binding (qemu-img may use every core).
*/
Comment on lines +40 to +43
/**
* Restrict the worker (qemu-img, or pv for clone) to the given host cores.
* Value is a taskset -c list such as "4,5,6,7"; empty means no binding.
*/
<string name="settings_vm_clear_logs_before_start_title">Clear logs before start</string>
<string name="settings_vm_clear_logs_before_start_summary">Clear all console logs for the VM before it starts</string>
<string name="settings_vm_keep_compress_on_optimize_title">Keep compression when optimizing</string>
<string name="settings_vm_keep_compress_on_optimize_summary">Preserve a disk\'s existing compression when optimizing instead of decompressing it; off rewrites the disk uncompressed</string>
Comment on lines +138 to +142
// TODO(cpu-affinity): to bind the VM to selected host cores, prepend a
// system-taskset wrapper here (mirroring prepareStraceArguments):
// "taskset -a <hexMask>", with the mask from CpuUtils.coresCsvToHexMask
// reading a future MainSettingsFragment.KEY_CROSVM_CPU_AFFINITY setting.
// Note toybox taskset takes a hex mask (no "0x"), not a -c core list.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

该文件没有实际添加代码,请删掉新增注释

Comment on lines +96 to +99
public static String formatFreq(long khz) {
if (khz <= 0) return "";
return String.format(Locale.US, "%.2f GHz", khz / 1_000_000.0);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

复用SizeUtils.findFloatUnit

Comment on lines +210 to +213
@NonNull
private static String fmt(@NonNull String f, Object... args) {
return String.format(Locale.US, f, args);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

删除该函数,使用StringUtils.fmt

try {
raw = FileUtils.shellReadFile(path);
} catch (Exception rootFailed) {
Log.d(TAG, "freq read failed: " + path);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

使用fmt

Comment on lines +42 to +57
public static boolean hasCompressedClusters(String path) {
try {
var result = runListQuiet(
getPrebuiltBinaryPath("qemu-img"),
"check", "--output=json", path);
var out = result.getOutString();
// qemu-img check still writes its JSON report on non-zero exits
// (leaks/corruptions), so parse whatever it produced rather than
// gating on isSuccess(); a missing/garbage body throws -> false.
if (out == null || out.isEmpty())
return false;
return new JSONObject(out).optLong("compressed-clusters", 0) > 0;
} catch (Exception e) {
return false;
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

拆分一个getImageCheck函数,参考getImageInfo

Comment on lines +371 to +372
private static java.util.Set<Integer> parseCsvToSet(@NonNull String csv) {
var set = new java.util.HashSet<Integer>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

应为Set<Integer>HashSet<Integer>,添加import

Comment on lines +69 to +73
public static final String KEY_VM_KEEP_COMPRESS_ON_OPTIMIZE = "vm_keep_compress_on_optimize";
public static final String KEY_QEMU_IMG_CPU_AFFINITY = "qemu_img_cpu_affinity";
// Reserved for a future crosvm/qemu VM affinity setting; not read or
// written yet, and intentionally has no UI in this release.
// public static final String KEY_CROSVM_CPU_AFFINITY = "crosvm_cpu_affinity";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

删掉注释的代码

- CrosvmBackendInstance: drop the no-op cpu-affinity TODO comment
- CpuUtils: reuse SizeUtils.findFloatUnit for frequency formatting and
  StringUtils.fmt instead of a local fmt helper
- ImageUtils: split out getImageCheck() mirroring getImageInfo()
- MainSettingsFragment: import Set/HashSet, drop commented-out reserved key
findFloatUnit uses binary 1024 steps, which underreports clock speeds
(2.60 GHz shown as 2.42 GHz). Frequencies are SI/decimal, so pick the
Hz/kHz/MHz/GHz tier by dividing by 1000.
@BigfootACA BigfootACA merged commit af33ebb into Droid-VM:master Jul 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants