Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-comfy-aimdo for 
openSUSE:Factory checked in at 2026-09-17 15:17:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-comfy-aimdo (Old)
 and      /work/SRC/openSUSE:Factory/.python-comfy-aimdo.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-comfy-aimdo"

Thu Sep 17 15:17:40 2026 rev:7 rq:1378284 version:0.5.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-comfy-aimdo/python-comfy-aimdo.changes    
2026-09-11 18:05:49.896121151 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-comfy-aimdo.new.383539/python-comfy-aimdo.changes
        2026-09-17 15:18:24.551419667 +0200
@@ -1,0 +2,14 @@
+Wed Sep 16 07:24:48 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to 0.5.5:
+  * Windows disk-speed detection for model storage (new
+    comfy_aimdo.storage.fast_disk plus native src-win/disk-id.c).
+    The C helper is Windows-only; this package ships the Python
+    wrapper, which returns None on Linux
+  * Native malloc-graph search no longer skips overlapping alias
+    ranges, so a later allocation can reuse those physical pages;
+    it lives in the native allocator, which this build does not
+    compile
+- Extend %check to import the new comfy_aimdo.storage module
+
+-------------------------------------------------------------------

Old:
----
  comfy-aimdo-0.5.3.tar.gz

New:
----
  comfy-aimdo-0.5.5.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-comfy-aimdo.spec ++++++
--- /var/tmp/diff_new_pack.E2UMf9/_old  2026-09-17 15:18:25.138444307 +0200
+++ /var/tmp/diff_new_pack.E2UMf9/_new  2026-09-17 15:18:25.141444433 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-comfy-aimdo
-Version:        0.5.3
+Version:        0.5.5
 Release:        0
 Summary:        AI Model Dynamic Offloader for ComfyUI (pure-Python fallback)
 License:        GPL-3.0-only
@@ -64,7 +64,7 @@
 # device through the native aimdo.so, which this build does not compile.
 # Confirm instead that the shipped modules import and the loader leaves
 # lib unset (torch.py excluded: it imports torch at module scope).
-%python_expand PYTHONPATH=%{buildroot}%{$python_sitelib} $python -B -c "import 
comfy_aimdo.control, comfy_aimdo.host_buffer, comfy_aimdo.malloc_graph, 
comfy_aimdo.model_mmap, comfy_aimdo.model_vbar, comfy_aimdo.vram_buffer; assert 
comfy_aimdo.control.lib is None"
+%python_expand PYTHONPATH=%{buildroot}%{$python_sitelib} $python -B -c "import 
comfy_aimdo.control, comfy_aimdo.host_buffer, comfy_aimdo.malloc_graph, 
comfy_aimdo.model_mmap, comfy_aimdo.model_vbar, comfy_aimdo.storage, 
comfy_aimdo.vram_buffer; assert comfy_aimdo.control.lib is None"
 # Patch0: without a CUDA or ROCm PyTorch, init() must report failure rather
 # than guess a vendor, and it must not emit a warning while doing so.
 cat > test_no_vendor.py <<'EOF'

++++++ comfy-aimdo-0.5.3.tar.gz -> comfy-aimdo-0.5.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/comfy-aimdo-0.5.3/.github/workflows/build-wheels.yml 
new/comfy-aimdo-0.5.5/.github/workflows/build-wheels.yml
--- old/comfy-aimdo-0.5.3/.github/workflows/build-wheels.yml    2026-09-08 
12:59:20.000000000 +0200
+++ new/comfy-aimdo-0.5.5/.github/workflows/build-wheels.yml    2026-09-15 
10:46:20.000000000 +0200
@@ -72,12 +72,12 @@
             src/*.c src-cuda/dispatch.c src-win/*.c ^
             /Isrc /Isrc-win /IC:/Detours/include /FIcompiler.h 
/Fe:comfy_aimdo/aimdo.dll ^
             /link /LIBPATH:%DETOURS_LIB_DIR% ^
-            dxgi.lib dxguid.lib detours.lib onecore.lib
+            cfgmgr32.lib dxgi.lib dxguid.lib setupapi.lib detours.lib 
onecore.lib
           cl.exe /LD /O2 /D__HIP_PLATFORM_AMD__ ^
             src/*.c src-hip/dispatch.c src-win/*.c ^
             /Isrc /Isrc-win /IC:/Detours/include /FIcompiler.h 
/Fe:comfy_aimdo/aimdo_rocm.dll ^
             /link /LIBPATH:%DETOURS_LIB_DIR% ^
-            dxgi.lib dxguid.lib detours.lib onecore.lib
+            cfgmgr32.lib dxgi.lib dxguid.lib setupapi.lib detours.lib 
onecore.lib
         shell: cmd
 
       - name: Build default Stub Wheel
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/comfy-aimdo-0.5.3/comfy_aimdo/storage.py 
new/comfy-aimdo-0.5.5/comfy_aimdo/storage.py
--- old/comfy-aimdo-0.5.3/comfy_aimdo/storage.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/comfy-aimdo-0.5.5/comfy_aimdo/storage.py        2026-09-15 
10:46:20.000000000 +0200
@@ -0,0 +1,19 @@
+import ctypes
+import os
+
+from . import control
+
+lib = control.lib if os.name == "nt" else None
+
+if lib is not None:
+    lib.aimdo_storage_fast_disk.argtypes = [ctypes.c_wchar_p]
+    lib.aimdo_storage_fast_disk.restype = ctypes.c_int
+
+
+def fast_disk(path):
+    if lib is None:
+        return None
+    result = lib.aimdo_storage_fast_disk(str(path))
+    if result < 0:
+        return None
+    return bool(result)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/comfy-aimdo-0.5.3/src/malloc-graph.c 
new/comfy-aimdo-0.5.5/src/malloc-graph.c
--- old/comfy-aimdo-0.5.3/src/malloc-graph.c    2026-09-08 12:59:20.000000000 
+0200
+++ new/comfy-aimdo-0.5.5/src/malloc-graph.c    2026-09-15 10:46:20.000000000 
+0200
@@ -930,11 +930,13 @@
     size_t pages = ALIGN_UP(size, MG_PAGE) / MG_PAGE;
 
     while (va + pages <= g->va_count) {
-        size_t j;
+        size_t j, advance;
+        int phys;
         for (j = 0; j < pages; j++) {
-            int phys = g->va_phys[va + j];
+            phys = g->va_phys[va + j];
             if (rogue_va(g, va + j) || g->allocations.physical_live[phys] ||
                 rogue_phys(g, phys)) {
+                advance = j + 1;
                 break;
             }
             g->allocations.physical_live[phys] = true;
@@ -943,9 +945,13 @@
             break;
         }
         for (size_t k = 0; k < j; k++) {
+            // Resume after the earlier alias without skipping overlapping 
ranges.
+            if (g->va_phys[va + k] == phys) {
+                advance = k + 1;
+            }
             g->allocations.physical_live[g->va_phys[va + k]] = false;
         }
-        va += j + 1;
+        va += advance;
     }
     RETURN_G_FAILED(g->failed, true);
     if (va + pages > g->va_count) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/comfy-aimdo-0.5.3/src-win/disk-id.c 
new/comfy-aimdo-0.5.5/src-win/disk-id.c
--- old/comfy-aimdo-0.5.3/src-win/disk-id.c     1970-01-01 01:00:00.000000000 
+0100
+++ new/comfy-aimdo-0.5.5/src-win/disk-id.c     2026-09-15 10:46:20.000000000 
+0200
@@ -0,0 +1,243 @@
+#include "plat.h"
+
+#include <windows.h>
+#include <cfgmgr32.h>
+#include <setupapi.h>
+#include <winioctl.h>
+
+#define AIMDO_DISK_UNKNOWN (-1)
+#define AIMDO_DISK_SLOW      0
+#define AIMDO_DISK_FAST      1
+
+#define BUS_TYPE_UNKNOWN             0
+#define BUS_TYPE_USB                 7
+#define BUS_TYPE_RAID                8
+#define BUS_TYPE_ISCSI               9
+#define BUS_TYPE_VIRTUAL            14
+#define BUS_TYPE_FILE_BACKED_VIRTUAL 15
+#define BUS_TYPE_SPACES             16
+#define BUS_TYPE_NVME               17
+#define BUS_TYPE_NVMEOF             20
+#define BUS_TYPE_MAX                21
+
+static const GUID disk_interface_guid = {
+    0x53f56307, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 
0x8b}
+};
+static const GUID pci_property_guid = {
+    0x3ab22e31, 0x8264, 0x4b4e, {0x9a, 0xf5, 0xa8, 0xd2, 0xd8, 0xe3, 0x3e, 
0x62}
+};
+
+static HANDLE open_existing(const wchar_t *path) {
+    return CreateFileW(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | 
FILE_SHARE_DELETE,
+                       NULL, OPEN_EXISTING, 0, NULL);
+}
+
+static bool get_volume_guid(const wchar_t *path, wchar_t volume[64]) {
+    wchar_t buffer[32768];
+    wchar_t *end;
+    DWORD length;
+    HANDLE file = open_existing(path);
+
+    if (file == INVALID_HANDLE_VALUE) {
+        return false;
+    }
+    length = GetFinalPathNameByHandleW(file, buffer, ARRAY_SIZE(buffer),
+                                       FILE_NAME_NORMALIZED | 
VOLUME_NAME_GUID);
+    CloseHandle(file);
+    if (!length || length >= ARRAY_SIZE(buffer) || wcsncmp(buffer, 
L"\\\\?\\Volume{", 11) ||
+        !(end = wcschr(buffer + 11, L'}')) || end - buffer + 2 >= 64) {
+        return false;
+    }
+    end[1] = L'\\';
+    end[2] = L'\0';
+    wcscpy_s(volume, 64, buffer);
+    return true;
+}
+
+static int disk_bus_type(DWORD disk_number) {
+    STORAGE_PROPERTY_QUERY query = {
+        .PropertyId = StorageDeviceProperty,
+        .QueryType = PropertyStandardQuery,
+    };
+    STORAGE_DEVICE_DESCRIPTOR descriptor;
+    wchar_t path[64];
+    DWORD returned;
+    HANDLE disk;
+
+    swprintf_s(path, ARRAY_SIZE(path), L"\\\\.\\PhysicalDrive%lu", 
disk_number);
+    disk = open_existing(path);
+    if (disk == INVALID_HANDLE_VALUE) {
+        return -1;
+    }
+    if (!DeviceIoControl(disk, IOCTL_STORAGE_QUERY_PROPERTY,
+                         &query, sizeof(query), &descriptor, 
sizeof(descriptor),
+                         &returned, NULL) || returned < 
offsetof(STORAGE_DEVICE_DESCRIPTOR, RawDeviceProperties)) {
+        CloseHandle(disk);
+        return -1;
+    }
+    CloseHandle(disk);
+    return descriptor.BusType;
+}
+
+static DEVINST disk_devinst(DWORD disk_number) {
+    SP_DEVICE_INTERFACE_DATA interface_data = {0};
+    SP_DEVINFO_DATA devinfo = {0};
+    HDEVINFO devices;
+    DEVINST found = 0;
+
+    devices = SetupDiGetClassDevsW(&disk_interface_guid, NULL, NULL,
+                                   DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+    if (devices == INVALID_HANDLE_VALUE) {
+        return 0;
+    }
+    interface_data.cbSize = sizeof(interface_data);
+    for (DWORD index = 0; SetupDiEnumDeviceInterfaces(
+             devices, NULL, &disk_interface_guid, index, &interface_data); 
index++) {
+        PSP_DEVICE_INTERFACE_DETAIL_DATA_W detail;
+        STORAGE_DEVICE_NUMBER number;
+        DWORD required = 0;
+        DWORD returned;
+        HANDLE disk;
+
+        SetupDiGetDeviceInterfaceDetailW(devices, &interface_data, NULL, 0, 
&required, NULL);
+        if (required < sizeof(*detail) || !(detail = malloc(required))) {
+            continue;
+        }
+        detail->cbSize = sizeof(*detail);
+        devinfo.cbSize = sizeof(devinfo);
+        if (!SetupDiGetDeviceInterfaceDetailW(devices, &interface_data, 
detail, required,
+                                              NULL, &devinfo) ||
+            (disk = open_existing(detail->DevicePath)) == 
INVALID_HANDLE_VALUE) {
+            free(detail);
+            continue;
+        }
+        if (DeviceIoControl(disk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
+                            &number, sizeof(number), &returned, NULL) &&
+            returned >= sizeof(number) && number.DeviceType == 
FILE_DEVICE_DISK &&
+            number.DeviceNumber == disk_number) {
+            found = devinfo.DevInst;
+        }
+        CloseHandle(disk);
+        free(detail);
+        if (found) {
+            break;
+        }
+    }
+    SetupDiDestroyDeviceInfoList(devices);
+    return found;
+}
+
+static bool pci_property(DEVINST devinst, DWORD pid, DWORD *value) {
+    DEVPROPKEY key = {.fmtid = {0}, .pid = pid};
+    DEVPROPTYPE type;
+    ULONG size = sizeof(*value);
+
+    key.fmtid = pci_property_guid;
+    return CM_Get_DevNode_PropertyW(devinst, &key, &type, (PBYTE)value, &size, 
0) == CR_SUCCESS &&
+           type == DEVPROP_TYPE_UINT32 && size == sizeof(*value);
+}
+
+static DEVINST nvme_controller(DEVINST devinst) {
+    for (int depth = 0; depth < 16; depth++) {
+        DWORD base_class;
+        DWORD sub_class;
+        DWORD prog_if;
+        DEVINST parent;
+
+        if (pci_property(devinst, 3, &base_class) && base_class == 0x01 &&
+            pci_property(devinst, 4, &sub_class) && sub_class == 0x08 &&
+            pci_property(devinst, 5, &prog_if) && prog_if == 0x02) {
+            return devinst;
+        }
+        if (CM_Get_Parent(&parent, devinst, 0) != CR_SUCCESS) {
+            break;
+        }
+        devinst = parent;
+    }
+    return 0;
+}
+
+static int physical_disk_fast(DWORD disk_number) {
+    int bus_type = disk_bus_type(disk_number);
+    DEVINST devinst;
+    DEVINST controller;
+    DWORD generation;
+    DWORD width;
+
+    if (bus_type != BUS_TYPE_NVME) {
+        if (bus_type <= BUS_TYPE_UNKNOWN || bus_type >= BUS_TYPE_MAX ||
+            bus_type == BUS_TYPE_USB || bus_type == BUS_TYPE_RAID ||
+            bus_type == BUS_TYPE_ISCSI || bus_type == BUS_TYPE_VIRTUAL ||
+            bus_type == BUS_TYPE_FILE_BACKED_VIRTUAL || bus_type == 
BUS_TYPE_SPACES ||
+            bus_type == BUS_TYPE_NVMEOF) {
+            return AIMDO_DISK_UNKNOWN;
+        }
+        return AIMDO_DISK_SLOW;
+    }
+    devinst = disk_devinst(disk_number);
+    controller = devinst ? nvme_controller(devinst) : 0;
+    if (!controller || !pci_property(controller, 9, &generation) ||
+        !pci_property(controller, 10, &width)) {
+        return AIMDO_DISK_UNKNOWN;
+    }
+    log(DEBUG, "%s: disk=%lu current_gen=%lu current_width=%lu\n",
+        __func__, disk_number, generation, width);
+
+    return ((generation == 3 && width == 4) ||
+            (generation == 4 && width == 4) ||
+            (generation == 5 && (width == 2 || width == 4))) ?
+           AIMDO_DISK_FAST : AIMDO_DISK_SLOW;
+}
+
+static int volume_fast(const wchar_t *volume) {
+    union {
+        VOLUME_DISK_EXTENTS extents;
+        BYTE bytes[32768];
+    } buffer;
+    VOLUME_DISK_EXTENTS *extents = &buffer.extents;
+    wchar_t volume_path[64];
+    DWORD returned;
+    DWORD count;
+    HANDLE handle;
+    int result = AIMDO_DISK_FAST;
+
+    wcscpy_s(volume_path, ARRAY_SIZE(volume_path), volume);
+    volume_path[wcslen(volume_path) - 1] = L'\0';
+    handle = open_existing(volume_path);
+    if (handle == INVALID_HANDLE_VALUE) {
+        return AIMDO_DISK_UNKNOWN;
+    }
+    bool success = DeviceIoControl(handle, 
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
+                                   NULL, 0, extents, sizeof(buffer), 
&returned, NULL);
+    CloseHandle(handle);
+    if (!success || returned < offsetof(VOLUME_DISK_EXTENTS, Extents) ||
+        returned > sizeof(buffer)) {
+        return AIMDO_DISK_UNKNOWN;
+    }
+    count = extents->NumberOfDiskExtents;
+    if (!count || count > (returned - offsetof(VOLUME_DISK_EXTENTS, Extents)) /
+                          sizeof(extents->Extents[0])) {
+        return AIMDO_DISK_UNKNOWN;
+    }
+    for (DWORD i = 0; i < count; i++) {
+        int disk_result = physical_disk_fast(extents->Extents[i].DiskNumber);
+
+        if (disk_result == AIMDO_DISK_SLOW) {
+            return AIMDO_DISK_SLOW;
+        }
+        if (disk_result == AIMDO_DISK_UNKNOWN) {
+            result = AIMDO_DISK_UNKNOWN;
+        }
+    }
+    return result;
+}
+
+SHARED_EXPORT
+int aimdo_storage_fast_disk(const wchar_t *path) {
+    wchar_t volume[64];
+
+    if (!path || !get_volume_guid(path, volume)) {
+        return AIMDO_DISK_UNKNOWN;
+    }
+    return volume_fast(volume);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/comfy-aimdo-0.5.3/tests/malloc-graph-overlapping-alias-cuda.py 
new/comfy-aimdo-0.5.5/tests/malloc-graph-overlapping-alias-cuda.py
--- old/comfy-aimdo-0.5.3/tests/malloc-graph-overlapping-alias-cuda.py  
1970-01-01 01:00:00.000000000 +0100
+++ new/comfy-aimdo-0.5.5/tests/malloc-graph-overlapping-alias-cuda.py  
2026-09-15 10:46:20.000000000 +0200
@@ -0,0 +1,36 @@
+# Regression: an alias collision must not skip a usable overlapping window.
+# No model needed; at most 80 MiB of graph pages on the failing implementation.
+import os
+os.environ['PYTORCH_ALLOC_CONF'] = 'backend:cudaMallocAsync'
+import json
+import torch
+import comfy_aimdo.control as aimdo
+
+M = 1024 * 1024
+assert aimdo.init('cuda')
+assert aimdo.init_device(torch.cuda.current_device())
+torch.empty(1, device='cuda')
+graph = aimdo.record(torch.cuda.current_stream())
+a = torch.empty(16 * M, dtype=torch.uint8, device='cuda')
+b = torch.empty(8 * M, dtype=torch.uint8, device='cuda')
+del a
+c = torch.empty(24 * M, dtype=torch.uint8, device='cuda')
+del b, c
+before = graph.virtual_bytes
+assert before == 48 * M, f'Expected the six-page alias layout, got {before} 
bytes.'
+d = torch.empty(32 * M, dtype=torch.uint8, device='cuda')
+for page in range(4):
+    d[page * 8 * M:(page + 1) * 8 * M].fill_(37 + page)
+after = graph.virtual_bytes
+physical = graph.physical_bytes
+for page in range(4):
+    assert d[page * 8 * M].item() == 37 + page
+    assert d[(page + 1) * 8 * M - 1].item() == 37 + page
+print(json.dumps({'beforeVirtualBytes': before, 'afterVirtualBytes': after,
+                  'physicalBytes': physical, 'reused': before == after}))
+assert before == after, 'A valid overlapping range was skipped.'
+assert physical == 32 * M, f'Expected 32 MiB of physical pages, got {physical} 
bytes.'
+del d
+graph.pop()
+del graph
+aimdo.deinit()

Reply via email to